From 6269f66aa983cd01228cbc6ab48236ce439a590e Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Mon, 29 Apr 2024 08:31:29 -0600 Subject: [PATCH 1/9] Exclude internal PMVC test classes from simulator --- build.savant | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/build.savant b/build.savant index abfc54f9..42da13e4 100644 --- a/build.savant +++ b/build.savant @@ -130,14 +130,21 @@ target(name: "compile", description: "Compiles the project") { target(name: "jar", description: "JARs the project", dependsOn: ["compile"]) { java.jar() + def simulatorPatterns = [ + ~/org\/primeframework\/mvc\/test.+/, + ~/org\/primeframework\/mvc\/message\/TestMessageObserver/, + ~/org\/primeframework\/mvc\/TestPrimeMain/, + ~/org\/primeframework\/mvc\/util\/Throwing.*/, + ~/org\/primeframework\/mock/ + ] // Create a separate test jar that only includes org/primeframework file.copy(to: "build/classes/simulator") { - fileSet(dir: "build/classes/test", includePatterns: [~/org\/primeframework.+/]) + fileSet(dir: "build/classes/test", includePatterns: simulatorPatterns) } // Create a separate test jar that only includes org/primeframework file.copy(to: "build/src/simulator") { - fileSet(dir: "src/test/java", includePatterns: [~/org\/primeframework.+/]) + fileSet(dir: "src/test/java", includePatterns: simulatorPatterns) } file.jar(file: "build/jars/${project.name}-simulator-${project.version}.jar") { From 96f99d25511891b1df9868d75def34e5e628a275 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Wed, 1 May 2024 15:57:11 -0600 Subject: [PATCH 2/9] explain better --- build.savant | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build.savant b/build.savant index 42da13e4..6128ce35 100644 --- a/build.savant +++ b/build.savant @@ -137,12 +137,14 @@ target(name: "jar", description: "JARs the project", dependsOn: ["compile"]) { ~/org\/primeframework\/mvc\/util\/Throwing.*/, ~/org\/primeframework\/mock/ ] - // Create a separate test jar that only includes org/primeframework + // Create a separate test jar that only includes org.primeframework.mvc.test.RequestSimulator + // and its dependencies file.copy(to: "build/classes/simulator") { fileSet(dir: "build/classes/test", includePatterns: simulatorPatterns) } - // Create a separate test jar that only includes org/primeframework + // Create a separate test jar that only includes org.primeframework.mvc.test.RequestSimulator + // and its dependencies file.copy(to: "build/src/simulator") { fileSet(dir: "src/test/java", includePatterns: simulatorPatterns) } From bdb87073d0e3f45f007c2ef7fac57ab553de44c8 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Sat, 22 Jun 2024 11:00:42 -0600 Subject: [PATCH 3/9] Exclude tests for RequestResult, etc. too --- build.savant | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/build.savant b/build.savant index 6128ce35..35c09dc6 100644 --- a/build.savant +++ b/build.savant @@ -137,16 +137,23 @@ target(name: "jar", description: "JARs the project", dependsOn: ["compile"]) { ~/org\/primeframework\/mvc\/util\/Throwing.*/, ~/org\/primeframework\/mock/ ] + def simulatorExcludePatterns = [ + ~/.*Test/ + ] // Create a separate test jar that only includes org.primeframework.mvc.test.RequestSimulator // and its dependencies file.copy(to: "build/classes/simulator") { - fileSet(dir: "build/classes/test", includePatterns: simulatorPatterns) + fileSet(dir: "build/classes/test", + includePatterns: simulatorPatterns, + excludePatterns: simulatorExcludePatterns) } - // Create a separate test jar that only includes org.primeframework.mvc.test.RequestSimulator + // Create a separate test src jar that only includes org.primeframework.mvc.test.RequestSimulator // and its dependencies file.copy(to: "build/src/simulator") { - fileSet(dir: "src/test/java", includePatterns: simulatorPatterns) + fileSet(dir: "src/test/java", + includePatterns: simulatorPatterns, + excludePatterns: simulatorExcludePatterns) } file.jar(file: "build/jars/${project.name}-simulator-${project.version}.jar") { From 111976c5a3aa9f4809e6918e937a956a9215366e Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Sat, 22 Jun 2024 11:11:16 -0600 Subject: [PATCH 4/9] Demonstrate content type is not checked --- .../mvc/test/RequestResultTest.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/primeframework/mvc/test/RequestResultTest.java b/src/test/java/org/primeframework/mvc/test/RequestResultTest.java index 9d006ba6..5ce3e984 100644 --- a/src/test/java/org/primeframework/mvc/test/RequestResultTest.java +++ b/src/test/java/org/primeframework/mvc/test/RequestResultTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2022, Inversoft Inc., All Rights Reserved + * Copyright (c) 2018-2024, Inversoft Inc., All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,12 +20,13 @@ import java.nio.file.Path; import com.fasterxml.jackson.databind.ObjectMapper; +import org.primeframework.mvc.PrimeBaseTest; import org.testng.annotations.Test; /** * @author Daniel DeGroff */ -public class RequestResultTest { +public class RequestResultTest extends PrimeBaseTest { @Test public void arrays() throws IOException { Path jsonFile1 = Path.of("src/test/resources/json/SortedJSONArrays1.json"); @@ -33,6 +34,23 @@ public void arrays() throws IOException { RequestResult.assertJSONEquals(new ObjectMapper(), Files.readString(jsonFile1), Files.readString(jsonFile2)); } + @Test + public void assertContentTypeIsJSON_correct() throws IOException { + simulator.test("/api-final") + .post() + .assertStatusCode(200) + .assertJSONValuesAt("/bar", false); + } + + @Test(expectedExceptions = AssertionError.class, + expectedExceptionsMessageRegExp = "Content-Type \\[null] does not start with the expected value.*") + public void assertContentTypeIsJSON_incorrect() throws IOException { + simulator.test("/api/status") + .get() + .assertStatusCode(200) + .assertJSON("foo"); + } + @Test(enabled = false) public void keys() throws IOException { Path jsonFile1 = Path.of("src/test/resources/json/SortedJSONKeys1.json"); From df900290eb98ce559d578f9d21503fc5ec469724 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Sat, 22 Jun 2024 11:26:32 -0600 Subject: [PATCH 5/9] This is a better test because it's valid JSON without the right content type --- .../action/api/NoContentTypeJsonAction.java | 49 +++++++++++++++++++ .../mvc/test/RequestResultTest.java | 4 +- 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 src/test/java/org/example/action/api/NoContentTypeJsonAction.java diff --git a/src/test/java/org/example/action/api/NoContentTypeJsonAction.java b/src/test/java/org/example/action/api/NoContentTypeJsonAction.java new file mode 100644 index 00000000..f9b5c62a --- /dev/null +++ b/src/test/java/org/example/action/api/NoContentTypeJsonAction.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, Inversoft Inc., All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ +package org.example.action.api; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; + +import org.primeframework.mvc.action.annotation.Action; +import org.primeframework.mvc.action.result.annotation.Stream; + +@Action +@Stream +public class NoContentTypeJsonAction { + public int length; + + public String name; + + public InputStream stream; + + public String type; + + public String get() { + try (StringWriter stringWriter = new StringWriter()) { + stringWriter.write("{\"hello\": 123}"); + stream = new ByteArrayInputStream(stringWriter.toString().getBytes()); + length = stringWriter.getBuffer().length(); + name = ""; + type = "text/plain"; + } catch (IOException e) { + throw new RuntimeException(e); + } + return "success"; + } +} diff --git a/src/test/java/org/primeframework/mvc/test/RequestResultTest.java b/src/test/java/org/primeframework/mvc/test/RequestResultTest.java index 5ce3e984..3d507030 100644 --- a/src/test/java/org/primeframework/mvc/test/RequestResultTest.java +++ b/src/test/java/org/primeframework/mvc/test/RequestResultTest.java @@ -45,10 +45,10 @@ public void assertContentTypeIsJSON_correct() throws IOException { @Test(expectedExceptions = AssertionError.class, expectedExceptionsMessageRegExp = "Content-Type \\[null] does not start with the expected value.*") public void assertContentTypeIsJSON_incorrect() throws IOException { - simulator.test("/api/status") + simulator.test("/api/no-content-type-json") .get() .assertStatusCode(200) - .assertJSON("foo"); + .assertJSONValuesAt("/hello", 123); } @Test(enabled = false) From 3fd43f179149775a3d48c4271c64e11c8d920d81 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Sat, 22 Jun 2024 11:27:15 -0600 Subject: [PATCH 6/9] Pass the test --- .../org/primeframework/mvc/test/RequestResult.java | 14 ++++++++++++++ .../primeframework/mvc/test/RequestResultTest.java | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/primeframework/mvc/test/RequestResult.java b/src/test/java/org/primeframework/mvc/test/RequestResult.java index 78f4ea87..eda413f0 100644 --- a/src/test/java/org/primeframework/mvc/test/RequestResult.java +++ b/src/test/java/org/primeframework/mvc/test/RequestResult.java @@ -934,6 +934,7 @@ public RequestResult assertJSON(Object object) throws IOException { */ public RequestResult assertJSON(Class type, Consumer consumer) throws IOException { ObjectMapper objectMapper = injector.getInstance(ObjectMapper.class); + assertContentTypeIsJSON(); T response = objectMapper.readValue(getBodyAsString(), type); consumer.accept(response); return this; @@ -948,6 +949,7 @@ public RequestResult assertJSON(Class type, Consumer consumer) throws */ public RequestResult assertJSON(String json) throws IOException { ObjectMapper objectMapper = injector.getInstance(ObjectMapper.class); + assertContentTypeIsJSON(); assertJSONEquals(objectMapper, getBodyAsString(), json); return this; } @@ -963,6 +965,7 @@ public RequestResult assertJSON(String json) throws IOException { public RequestResult assertJSONFile(Path jsonFile, Object... values) throws IOException { ObjectMapper objectMapper = injector.getInstance(ObjectMapper.class); String expected = BodyTools.processTemplateForAssertion(jsonFile, appendArray(values, "_to_milli", new ZonedDateTimeToMilliSeconds())); + assertContentTypeIsJSON(); _assertJSONEquals(objectMapper, getBodyAsString(), expected, true, jsonFile); return this; } @@ -979,6 +982,7 @@ public RequestResult assertJSONFile(Path jsonFile, Object... values) throws IOEx */ public RequestResult assertJSONFileWithActual(Class type, Path jsonFile, Object... values) throws IOException { ObjectMapper objectMapper = injector.getInstance(ObjectMapper.class); + assertContentTypeIsJSON(); T actual = objectMapper.readValue(getBodyAsString(), type); return assertJSONFile(jsonFile, appendArray(values, "actual", actual, "_to_milli", new ZonedDateTimeToMilliSeconds())); @@ -998,6 +1002,7 @@ public RequestResult assertJSONValuesAt(Object... pairs) throws IOException { } ObjectMapper objectMapper = injector.getInstance(ObjectMapper.class); + assertContentTypeIsJSON(); JsonNode actual = objectMapper.readTree(getBodyAsString()); for (int i = 0; i < pairs.length; i = i + 2) { @@ -1793,6 +1798,15 @@ private Object[] appendArray(Object[] values, Object... objects) { return list.toArray(); } + private void assertContentTypeIsJSON() { + // did not use assertContentType because did not want to bake in a particular encoding here + String actual = response.getHeader(Headers.ContentType); + String expectedContentType = "application/json"; + if (actual == null || !actual.startsWith(expectedContentType)) { + throw new AssertionError("Content-Type [" + actual + "] does not start with the expected value [" + expectedContentType + "]"); + } + } + private void assertRedirectEquality(String expectedUri) { String redirect = response.getHeader(Headers.Location); SortedMap> actual = uriToMap(redirect); diff --git a/src/test/java/org/primeframework/mvc/test/RequestResultTest.java b/src/test/java/org/primeframework/mvc/test/RequestResultTest.java index 3d507030..6e24b7bd 100644 --- a/src/test/java/org/primeframework/mvc/test/RequestResultTest.java +++ b/src/test/java/org/primeframework/mvc/test/RequestResultTest.java @@ -43,7 +43,7 @@ public void assertContentTypeIsJSON_correct() throws IOException { } @Test(expectedExceptions = AssertionError.class, - expectedExceptionsMessageRegExp = "Content-Type \\[null] does not start with the expected value.*") + expectedExceptionsMessageRegExp = "Content-Type \\[text/plain] does not start with the expected value \\[application/json]") public void assertContentTypeIsJSON_incorrect() throws IOException { simulator.test("/api/no-content-type-json") .get() From 09b7e8f0dfa72d61c807e47fd7a78490349367d1 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Sat, 22 Jun 2024 11:28:07 -0600 Subject: [PATCH 7/9] Version bump --- build.savant | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.savant b/build.savant index 35c09dc6..b52b1701 100644 --- a/build.savant +++ b/build.savant @@ -29,7 +29,7 @@ logbackVersion = "1.4.8" slf4jVersion = "2.0.7" testngVersion = "7.8.0" -project(group: "org.primeframework", name: "prime-mvc", version: "4.22.10", licenses: ["ApacheV2_0"]) { +project(group: "org.primeframework", name: "prime-mvc", version: "4.22.11", licenses: ["ApacheV2_0"]) { workflow { fetch { // Dependency resolution order: diff --git a/pom.xml b/pom.xml index 72f350f5..1d283048 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.primeframework prime-mvc - 4.22.10 + 4.22.11 jar FusionAuth App From a0e95d93c411ce82863e37474496bcf97a759db2 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Mon, 24 Jun 2024 08:11:44 -0600 Subject: [PATCH 8/9] TestURIBuilder was getting excluded by Savant, this might be cleaner anyways Pull the nested classes out, such that: 1) We don't have to craft a regex to deal with including RequestResult$TestURIBuilder.class, but still exclude the other tests 2) RequestResult is not so big --- .../primeframework/mvc/test/DOMHelper.java | 133 +++++ .../primeframework/mvc/test/HTMLAsserter.java | 288 +++++++++++ .../primeframework/mvc/test/JSONAsserter.java | 47 ++ .../mvc/test/RequestResult.java | 489 +----------------- .../primeframework/mvc/test/URIBuilder.java | 104 ++++ 5 files changed, 574 insertions(+), 487 deletions(-) create mode 100644 src/test/java/org/primeframework/mvc/test/DOMHelper.java create mode 100644 src/test/java/org/primeframework/mvc/test/HTMLAsserter.java create mode 100644 src/test/java/org/primeframework/mvc/test/JSONAsserter.java create mode 100644 src/test/java/org/primeframework/mvc/test/URIBuilder.java diff --git a/src/test/java/org/primeframework/mvc/test/DOMHelper.java b/src/test/java/org/primeframework/mvc/test/DOMHelper.java new file mode 100644 index 00000000..2d0860b9 --- /dev/null +++ b/src/test/java/org/primeframework/mvc/test/DOMHelper.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2024, Inversoft Inc., All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ +package org.primeframework.mvc.test; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.primeframework.mvc.test.RequestResult.ThrowingConsumer; +import org.primeframework.mvc.util.ThrowingRunnable; + +public class DOMHelper { + public String body; + + public Document document; + + public DOMHelper(String body, Document document) { + this.body = body; + this.document = document; + } + + /** + * Perform any custom modifications to the HTML document + * + * @return this. + */ + public DOMHelper custom(ThrowingRunnable runnable) throws Exception { + runnable.run(); + return this; + } + + /** + * Perform any custom modifications to the HTML document + * + * @param consumer the HTML document consumer + * @return this. + */ + public DOMHelper custom(ThrowingConsumer consumer) throws Exception { + consumer.accept(document); + return this; + } + + /** + * Remove an attribute from a DOM element. + * + * @param selector the DOM selector + * @param name the name of the attribute to remove + * @return this. + */ + public DOMHelper removeAttribute(String selector, String name) { + Element element = document.selectFirst(selector); + if (element == null) { + throw new AssertionError("Expected at least one element to match the selector " + selector + ". Found [0] elements instead. Unable to set element value.\n\nActual body:\n" + body); + } + + element.removeAttr(name); + + return this; + } + + /** + * Set an attribute w/ value on a DOM element. + * + * @param selector the DOM selector + * @param name the name of the attribute + * @param value the value of the attribute + * @return this. + */ + public DOMHelper setAttribute(String selector, String name, String value) { + Element element = document.selectFirst(selector); + if (element == null) { + throw new AssertionError("Expected at least one element to match the selector " + selector + ". Found [0] elements instead. Unable to set element value.\n\nActual body:\n" + body); + } + + element.attr(name, value); + + return this; + } + + public DOMHelper setChecked(String selector, boolean value) { + Element element = document.selectFirst(selector); + if (element == null) { + throw new AssertionError("Expected at least one element to match the selector " + selector + ". Found [0] elements instead. Unable to set element value.\n\nActual body:\n" + body); + } + + if (element.is("input[type=radio]") && value) { + Elements elements = document.select(element.tagName().toLowerCase() + "[type=radio][name=" + element.attr("name") + "]"); + for (Element e : elements) { + e.attr("checked", false); + } + } + + element.attr("checked", value); + return this; + } + + public DOMHelper setValue(String selector, Object value) { + if (value != null) { + Element element = document.selectFirst(selector); + if (element == null) { + throw new AssertionError("Expected at least one element to match the selector " + selector + ". Found [0] elements instead. Unable to set element value.\n\nActual body:\n" + body); + } + + // Handle a select element + if (element.is("select")) { + // Remove the selected attribute for each option, add it to the one that matches the requested value. + for (Element option : element.getElementsByTag("option")) { + if (option.attr("value").equals(value.toString())) { + option.attr("selected", "selected"); + } else { + option.removeAttr("selected"); + } + } + } else { + element.val(value.toString()); + } + } + + return this; + } +} diff --git a/src/test/java/org/primeframework/mvc/test/HTMLAsserter.java b/src/test/java/org/primeframework/mvc/test/HTMLAsserter.java new file mode 100644 index 00000000..8af7b590 --- /dev/null +++ b/src/test/java/org/primeframework/mvc/test/HTMLAsserter.java @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2024, Inversoft Inc., All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ +package org.primeframework.mvc.test; + +import java.util.Locale; +import java.util.function.Consumer; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.primeframework.mvc.test.RequestResult.ThrowingConsumer; + +public class HTMLAsserter { + public Document document; + + public RequestResult requestResult; + + public HTMLAsserter(RequestResult requestResult) { + this.requestResult = requestResult; + document = Jsoup.parse(requestResult.getBodyAsString()); + } + + /** + * Ensure a single element matches the provided selector. + * + * @param selector the DOM selector + * @param expected the number of expected matches for this selector in the DOM. + * @return this. + */ + public HTMLAsserter assertElementCount(String selector, int expected) { + Elements elements = document.select(selector); + if (elements.size() != expected) { + throw new AssertionError("Expected [" + expected + "] elements to match the selector " + selector + " but found [" + elements.size() + "] elements instead." + ((elements.size() == 0) ? "" : "\n\n" + elements) + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + return this; + } + + /** + * Ensure no elements match the provided selector. + * + * @param selector the DOM selector + * @return this. + */ + public HTMLAsserter assertElementDoesNotExist(String selector) { + Elements elements = document.select(selector); + if (elements.size() > 0) { + throw new AssertionError("Expected 0 elements to match the selector " + selector + ". Found [" + (elements.size() + "] elements.\n" + elements) + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + return this; + } + + /** + * Assert an element does not have an attribute. The value is not checked. + * + * @param selector the DOM selector + * @param attribute the attribute you expect + * @return this. + */ + public HTMLAsserter assertElementDoesNotHaveAttribute(String selector, String attribute) { + Element element = selectExpectOne(selector); + + if (element.hasAttr(attribute)) { + throw new AssertionError("Expected the element not to have attribute [" + attribute + "]." + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + return this; + } + + /** + * Ensure a single element matches the provided selector. + * + * @param selector the DOM selector + * @return this. + */ + public HTMLAsserter assertElementExists(String selector) { + selectExpectOne(selector); + return this; + } + + /** + * Assert an element has an attribute. The value is not checked. + * + * @param selector the DOM selector + * @param attribute the attribute you expect + * @return this. + */ + public HTMLAsserter assertElementHasAttribute(String selector, String attribute) { + Element element = selectExpectOne(selector); + + if (!element.hasAttr(attribute)) { + throw new AssertionError("Expected the element to have attribute [" + attribute + "]." + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + return this; + } + + /** + * Assert the element has an attribute with a specific value. + * + * @param selector the DOM selector + * @param attribute the name of the attribute you expect + * @param value the value of the attribute you expect + * @return this. + */ + public HTMLAsserter assertElementHasAttributeValue(String selector, String attribute, String value) { + Element element = selectExpectOne(selector); + + if (!element.hasAttr(attribute)) { + throw new AssertionError("Expected the element to have attribute [" + attribute + "]." + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + String actual = element.attr(attribute); + if (!value.equals(actual)) { + throw new AssertionError("Attribute [" + attribute + "] value not equal to expected.\nExpected [" + value + "] but found [" + actual + "]" + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + return this; + } + + /** + * Ensure a single element matches the provided selector and has an expected inner HTML. + * + * @param selector the DOM selector + * @param expectedInnerHTML the expected inner HTML + * @return this. + */ + public HTMLAsserter assertElementInnerHTML(String selector, String expectedInnerHTML) { + Elements elements = document.select(selector); + if (elements.size() != 1) { + throw new AssertionError("Expected a single element to match the selector " + selector + ". Found [" + elements.size() + "] elements instead." + ((elements.size() == 0) ? "" : "\n\n" + elements) + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + Element element = elements.get(0); + if (!expectedInnerHTML.equals(element.html())) { + throw new AssertionError("Expected a value of [" + expectedInnerHTML + "] to match the selector " + selector + ". Found [" + element.html() + "] instead." + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + return this; + } + + /** + * Ensure a single element matches the provided selector and is "checked" + * + * @param selector the DOM selector + * @return this. + */ + public HTMLAsserter assertElementIsChecked(String selector) { + Elements elements = document.select(selector); + if (elements.size() != 1) { + throw new AssertionError("Expected a single element to match the selector " + selector + ". Found [" + elements.size() + "] elements instead." + ((elements.size() == 0) ? "" : "\n\n" + elements) + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + if (!elements.get(0).hasAttr("checked")) { + throw new AssertionError("Expected the element to be checked." + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + return this; + } + + /** + * Ensure a single element matches the provided selector and is NOT "checked" + * + * @param selector the DOM selector + * @return this. + */ + public HTMLAsserter assertElementIsNotChecked(String selector) { + Elements elements = document.select(selector); + if (elements.size() != 1) { + throw new AssertionError("Expected a single element to match the selector " + selector + ". Found [" + elements.size() + "] elements instead." + ((elements.size() == 0) ? "" : "\n\n" + elements) + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + if (elements.get(0).hasAttr("checked")) { + throw new AssertionError("Expected the element NOT to be checked." + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + return this; + } + + /** + * Ensure a single element matches the provided selector and equals the provided value. + * + * @param selector the DOM selector + * @param value the expected value + * @return this. + */ + public HTMLAsserter assertElementValue(String selector, Object value) { + Elements elements = document.select(selector); + if (elements.size() != 1) { + throw new AssertionError("Expected a single element to match the selector " + selector + ". Found [" + elements.size() + "] instead." + ((elements.size() == 0) ? "" : "\n\n" + elements) + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + Element element = elements.get(0); + if (!element.val().equals(value.toString())) { + throw new AssertionError("Using the selector [" + selector + "] expected [" + value + "] but found [" + element.val() + "]. Actual matched element: \n\n" + element + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + return this; + } + + /** + * Allow for custom assertions on an element. + * + * @param selector the DOM selector + * @param consumer a consumer that will take the element found by the selector + * @return this. + */ + public HTMLAsserter assertOnElement(String selector, Consumer consumer) { + Element element = selectExpectOne(selector); + consumer.accept(element); + return this; + } + + /** + * Assert that a Select option is not selected. + * + * @param selector the DOM selector + * @return this. + */ + public HTMLAsserter assertOptionIsNotSelected(String selector) { + Element element = selectExpectOne(selector); + + if (!element.is("option")) { + throw new AssertionError("Expected the element not be an [option] but found [" + element.tagName().toLowerCase(Locale.ROOT) + "].\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + if (element.hasAttr("selected")) { + throw new AssertionError("Expected the element not to be selected." + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + return this; + } + + /** + * Assert that an option is selected. + * + * @param selector the DOM selector + * @return this. + */ + public HTMLAsserter assertOptionIsSelected(String selector) { + Element element = selectExpectOne(selector); + + if (!element.is("option")) { + throw new AssertionError("Expected the element not be an [option] but found [" + element.tagName().toLowerCase(Locale.ROOT) + "].\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + if (!element.hasAttr("selected")) { + throw new AssertionError("Expected the element to be selected." + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + return this; + } + + /** + * Perform any custom assertions on the parsed HTML document. + * + * @param consumer the HTML document consumer + * @return this. + */ + public HTMLAsserter custom(ThrowingConsumer consumer) throws Exception { + consumer.accept(document); + return this; + } + + private Element selectExpectOne(String selector) { + Elements elements = document.select(selector); + if (elements.size() != 1) { + throw new AssertionError("Expected a single element to match the selector " + selector + ". Found [" + elements.size() + "] elements instead." + ((elements.size() == 0) ? "" : "\n\n" + elements) + "\n\nActual body:\n" + requestResult.getBodyAsString()); + } + + return elements.get(0); + } +} diff --git a/src/test/java/org/primeframework/mvc/test/JSONAsserter.java b/src/test/java/org/primeframework/mvc/test/JSONAsserter.java new file mode 100644 index 00000000..432b5cab --- /dev/null +++ b/src/test/java/org/primeframework/mvc/test/JSONAsserter.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024, Inversoft Inc., All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ +package org.primeframework.mvc.test; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeType; + +public class JSONAsserter { + public String errorMessage; + + public JsonNode node; + + public Integer size; + + public JSONAsserter(JsonNode node) { + this.node = node; + } + + public JSONAsserter assertLength(int expected) { + if (node.size() != expected) { + errorMessage = "Expected an array of size [" + expected + "] but found [" + node.size() + "]"; + } + + return this; + } + + public JSONAsserter assertType(JsonNodeType expected) { + if (node.getNodeType() != expected) { + errorMessage = "Expected node to be of type [" + expected + "] but found [" + node.getNodeType() + "]"; + } + + return this; + } +} diff --git a/src/test/java/org/primeframework/mvc/test/RequestResult.java b/src/test/java/org/primeframework/mvc/test/RequestResult.java index eda413f0..623032be 100644 --- a/src/test/java/org/primeframework/mvc/test/RequestResult.java +++ b/src/test/java/org/primeframework/mvc/test/RequestResult.java @@ -28,7 +28,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -44,7 +43,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.node.JsonNodeType; import com.google.inject.Injector; import io.fusionauth.http.Cookie; import io.fusionauth.http.Cookie.SameSite; @@ -75,7 +73,6 @@ import org.primeframework.mvc.message.scope.CookieFlashScope; import org.primeframework.mvc.security.Encryptor; import org.primeframework.mvc.util.CookieTools; -import org.primeframework.mvc.util.QueryStringBuilder; import org.primeframework.mvc.util.QueryStringTools; import org.primeframework.mvc.util.ThrowingFunction; import org.primeframework.mvc.util.ThrowingRunnable; @@ -1114,13 +1111,13 @@ public RequestResult assertOnJSONValueAt(String pointer, Consumer * @param consumer The consumer to accept a URI builder to add parameters * @return This. */ - public RequestResult assertRedirect(String uri, Consumer consumer) { + public RequestResult assertRedirect(String uri, Consumer consumer) { String redirect = response.getHeader(Headers.Location); if (redirect == null) { throw new AssertionError("\nActual redirect was null. Why do you want to assert on it? Status code was [" + response.getStatus() + "]"); } - TestURIBuilder builder = TestURIBuilder.builder(uri); + URIBuilder builder = URIBuilder.builder(uri); consumer.accept(builder); String expectedUri = builder.build(); @@ -2059,486 +2056,4 @@ public interface ThrowingConsumer { void accept(T t) throws Exception; } - public static class DOMHelper { - public String body; - - public Document document; - - public DOMHelper(String body, Document document) { - this.body = body; - this.document = document; - } - - /** - * Perform any custom modifications to the HTML document - * - * @return this. - */ - public DOMHelper custom(ThrowingRunnable runnable) throws Exception { - runnable.run(); - return this; - } - - /** - * Perform any custom modifications to the HTML document - * - * @param consumer the HTML document consumer - * @return this. - */ - public DOMHelper custom(ThrowingConsumer consumer) throws Exception { - consumer.accept(document); - return this; - } - - /** - * Remove an attribute from a DOM element. - * - * @param selector the DOM selector - * @param name the name of the attribute to remove - * @return this. - */ - public DOMHelper removeAttribute(String selector, String name) { - Element element = document.selectFirst(selector); - if (element == null) { - throw new AssertionError("Expected at least one element to match the selector " + selector + ". Found [0] elements instead. Unable to set element value.\n\nActual body:\n" + body); - } - - element.removeAttr(name); - - return this; - } - - /** - * Set an attribute w/ value on a DOM element. - * - * @param selector the DOM selector - * @param name the name of the attribute - * @param value the value of the attribute - * @return this. - */ - public DOMHelper setAttribute(String selector, String name, String value) { - Element element = document.selectFirst(selector); - if (element == null) { - throw new AssertionError("Expected at least one element to match the selector " + selector + ". Found [0] elements instead. Unable to set element value.\n\nActual body:\n" + body); - } - - element.attr(name, value); - - return this; - } - - public DOMHelper setChecked(String selector, boolean value) { - Element element = document.selectFirst(selector); - if (element == null) { - throw new AssertionError("Expected at least one element to match the selector " + selector + ". Found [0] elements instead. Unable to set element value.\n\nActual body:\n" + body); - } - - if (element.is("input[type=radio]") && value) { - Elements elements = document.select(element.tagName().toLowerCase() + "[type=radio][name=" + element.attr("name") + "]"); - for (Element e : elements) { - e.attr("checked", false); - } - } - - element.attr("checked", value); - return this; - } - - public DOMHelper setValue(String selector, Object value) { - if (value != null) { - Element element = document.selectFirst(selector); - if (element == null) { - throw new AssertionError("Expected at least one element to match the selector " + selector + ". Found [0] elements instead. Unable to set element value.\n\nActual body:\n" + body); - } - - // Handle a select element - if (element.is("select")) { - // Remove the selected attribute for each option, add it to the one that matches the requested value. - for (Element option : element.getElementsByTag("option")) { - if (option.attr("value").equals(value.toString())) { - option.attr("selected", "selected"); - } else { - option.removeAttr("selected"); - } - } - } else { - element.val(value.toString()); - } - } - - return this; - } - } - - public static class HTMLAsserter { - public Document document; - - public RequestResult requestResult; - - public HTMLAsserter(RequestResult requestResult) { - this.requestResult = requestResult; - document = Jsoup.parse(requestResult.getBodyAsString()); - } - - /** - * Ensure a single element matches the provided selector. - * - * @param selector the DOM selector - * @param expected the number of expected matches for this selector in the DOM. - * @return this. - */ - public HTMLAsserter assertElementCount(String selector, int expected) { - Elements elements = document.select(selector); - if (elements.size() != expected) { - throw new AssertionError("Expected [" + expected + "] elements to match the selector " + selector + " but found [" + elements.size() + "] elements instead." + ((elements.size() == 0) ? "" : "\n\n" + elements) + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - return this; - } - - /** - * Ensure no elements match the provided selector. - * - * @param selector the DOM selector - * @return this. - */ - public HTMLAsserter assertElementDoesNotExist(String selector) { - Elements elements = document.select(selector); - if (elements.size() > 0) { - throw new AssertionError("Expected 0 elements to match the selector " + selector + ". Found [" + (elements.size() + "] elements.\n" + elements) + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - return this; - } - - /** - * Assert an element does not have an attribute. The value is not checked. - * - * @param selector the DOM selector - * @param attribute the attribute you expect - * @return this. - */ - public HTMLAsserter assertElementDoesNotHaveAttribute(String selector, String attribute) { - Element element = selectExpectOne(selector); - - if (element.hasAttr(attribute)) { - throw new AssertionError("Expected the element not to have attribute [" + attribute + "]." + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - return this; - } - - /** - * Ensure a single element matches the provided selector. - * - * @param selector the DOM selector - * @return this. - */ - public HTMLAsserter assertElementExists(String selector) { - selectExpectOne(selector); - return this; - } - - /** - * Assert an element has an attribute. The value is not checked. - * - * @param selector the DOM selector - * @param attribute the attribute you expect - * @return this. - */ - public HTMLAsserter assertElementHasAttribute(String selector, String attribute) { - Element element = selectExpectOne(selector); - - if (!element.hasAttr(attribute)) { - throw new AssertionError("Expected the element to have attribute [" + attribute + "]." + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - return this; - } - - /** - * Assert the element has an attribute with a specific value. - * - * @param selector the DOM selector - * @param attribute the name of the attribute you expect - * @param value the value of the attribute you expect - * @return this. - */ - public HTMLAsserter assertElementHasAttributeValue(String selector, String attribute, String value) { - Element element = selectExpectOne(selector); - - if (!element.hasAttr(attribute)) { - throw new AssertionError("Expected the element to have attribute [" + attribute + "]." + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - String actual = element.attr(attribute); - if (!value.equals(actual)) { - throw new AssertionError("Attribute [" + attribute + "] value not equal to expected.\nExpected [" + value + "] but found [" + actual + "]" + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - return this; - } - - /** - * Ensure a single element matches the provided selector and has an expected inner HTML. - * - * @param selector the DOM selector - * @param expectedInnerHTML the expected inner HTML - * @return this. - */ - public HTMLAsserter assertElementInnerHTML(String selector, String expectedInnerHTML) { - Elements elements = document.select(selector); - if (elements.size() != 1) { - throw new AssertionError("Expected a single element to match the selector " + selector + ". Found [" + elements.size() + "] elements instead." + ((elements.size() == 0) ? "" : "\n\n" + elements) + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - Element element = elements.get(0); - if (!expectedInnerHTML.equals(element.html())) { - throw new AssertionError("Expected a value of [" + expectedInnerHTML + "] to match the selector " + selector + ". Found [" + element.html() + "] instead." + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - return this; - } - - /** - * Ensure a single element matches the provided selector and is "checked" - * - * @param selector the DOM selector - * @return this. - */ - public HTMLAsserter assertElementIsChecked(String selector) { - Elements elements = document.select(selector); - if (elements.size() != 1) { - throw new AssertionError("Expected a single element to match the selector " + selector + ". Found [" + elements.size() + "] elements instead." + ((elements.size() == 0) ? "" : "\n\n" + elements) + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - if (!elements.get(0).hasAttr("checked")) { - throw new AssertionError("Expected the element to be checked." + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - return this; - } - - /** - * Ensure a single element matches the provided selector and is NOT "checked" - * - * @param selector the DOM selector - * @return this. - */ - public HTMLAsserter assertElementIsNotChecked(String selector) { - Elements elements = document.select(selector); - if (elements.size() != 1) { - throw new AssertionError("Expected a single element to match the selector " + selector + ". Found [" + elements.size() + "] elements instead." + ((elements.size() == 0) ? "" : "\n\n" + elements) + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - if (elements.get(0).hasAttr("checked")) { - throw new AssertionError("Expected the element NOT to be checked." + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - return this; - } - - /** - * Ensure a single element matches the provided selector and equals the provided value. - * - * @param selector the DOM selector - * @param value the expected value - * @return this. - */ - public HTMLAsserter assertElementValue(String selector, Object value) { - Elements elements = document.select(selector); - if (elements.size() != 1) { - throw new AssertionError("Expected a single element to match the selector " + selector + ". Found [" + elements.size() + "] instead." + ((elements.size() == 0) ? "" : "\n\n" + elements) + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - Element element = elements.get(0); - if (!element.val().equals(value.toString())) { - throw new AssertionError("Using the selector [" + selector + "] expected [" + value + "] but found [" + element.val() + "]. Actual matched element: \n\n" + element + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - return this; - } - - /** - * Allow for custom assertions on an element. - * - * @param selector the DOM selector - * @param consumer a consumer that will take the element found by the selector - * @return this. - */ - public HTMLAsserter assertOnElement(String selector, Consumer consumer) { - Element element = selectExpectOne(selector); - consumer.accept(element); - return this; - } - - /** - * Assert that a Select option is not selected. - * - * @param selector the DOM selector - * @return this. - */ - public HTMLAsserter assertOptionIsNotSelected(String selector) { - Element element = selectExpectOne(selector); - - if (!element.is("option")) { - throw new AssertionError("Expected the element not be an [option] but found [" + element.tagName().toLowerCase(Locale.ROOT) + "].\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - if (element.hasAttr("selected")) { - throw new AssertionError("Expected the element not to be selected." + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - return this; - } - - /** - * Assert that an option is selected. - * - * @param selector the DOM selector - * @return this. - */ - public HTMLAsserter assertOptionIsSelected(String selector) { - Element element = selectExpectOne(selector); - - if (!element.is("option")) { - throw new AssertionError("Expected the element not be an [option] but found [" + element.tagName().toLowerCase(Locale.ROOT) + "].\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - if (!element.hasAttr("selected")) { - throw new AssertionError("Expected the element to be selected." + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - return this; - } - - /** - * Perform any custom assertions on the parsed HTML document. - * - * @param consumer the HTML document consumer - * @return this. - */ - public HTMLAsserter custom(ThrowingConsumer consumer) throws Exception { - consumer.accept(document); - return this; - } - - private Element selectExpectOne(String selector) { - Elements elements = document.select(selector); - if (elements.size() != 1) { - throw new AssertionError("Expected a single element to match the selector " + selector + ". Found [" + elements.size() + "] elements instead." + ((elements.size() == 0) ? "" : "\n\n" + elements) + "\n\nActual body:\n" + requestResult.getBodyAsString()); - } - - return elements.get(0); - } - } - - public static class JSONAsserter { - public String errorMessage; - - public JsonNode node; - - public Integer size; - - public JSONAsserter(JsonNode node) { - this.node = node; - } - - public JSONAsserter assertLength(int expected) { - if (node.size() != expected) { - errorMessage = "Expected an array of size [" + expected + "] but found [" + node.size() + "]"; - } - - return this; - } - - public JSONAsserter assertType(JsonNodeType expected) { - if (node.getNodeType() != expected) { - errorMessage = "Expected node to be of type [" + expected + "] but found [" + node.getNodeType() + "]"; - } - - return this; - } - } - - public static class TestURIBuilder extends QueryStringBuilder { - private TestURIBuilder() { - } - - private TestURIBuilder(String uri) { - super(uri); - } - - public static TestURIBuilder builder() { - return new TestURIBuilder(); - } - - public static TestURIBuilder builder(String uri) { - return new TestURIBuilder(uri); - } - - @Override - public TestURIBuilder uri(String uri) { - return (TestURIBuilder) super.uri(uri); - } - - @Override - public TestURIBuilder with(String name, Object value) { - return (TestURIBuilder) super.with(name, value); - } - - @Override - public TestURIBuilder with(String name, Consumer consumer) { - return (TestURIBuilder) super.with(name, consumer); - } - - /** - * Add a parameter to the request that you will expect to match the expected. This may be useful if a timestamp oro other random data is returned - * that is not important to assert on. - *

- * This is only intended for use during testing. - * - * @param name the parameter name - * @return this - */ - @Override - public TestURIBuilder withActual(String name) { - with(name, "___actual___"); - return this; - } - - public TestURIBuilder withConsumer(Consumer consumer) { - if (consumer != null) { - consumer.accept(this); - } - - return this; - } - - /** - * Add a parameter as optional which means that it is not required to be on the query string, but if it is, the actual value will be used during - * the assertion. - *

- * This is only intended for use during testing. - * - * @param name the parameter name - * @return this - */ - public TestURIBuilder withOptional(String name) { - with(name, "___optional___"); - return this; - } - - public TestURIBuilder withQueryString(String queryString) { - Map> parsed = QueryStringTools.parseQueryString(queryString); - parsed.forEach((name, values) -> values - .forEach(value -> super.with(name, value))); - return this; - } - - @Override - public TestURIBuilder withSegment(Object segment) { - return (TestURIBuilder) super.withSegment(segment); - } - } } diff --git a/src/test/java/org/primeframework/mvc/test/URIBuilder.java b/src/test/java/org/primeframework/mvc/test/URIBuilder.java new file mode 100644 index 00000000..5a91d53e --- /dev/null +++ b/src/test/java/org/primeframework/mvc/test/URIBuilder.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2024, Inversoft Inc., All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ +package org.primeframework.mvc.test; + +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import org.primeframework.mvc.util.QueryStringBuilder; +import org.primeframework.mvc.util.QueryStringTools; + +public class URIBuilder extends QueryStringBuilder { + private URIBuilder() { + } + + private URIBuilder(String uri) { + super(uri); + } + + public static URIBuilder builder() { + return new URIBuilder(); + } + + public static URIBuilder builder(String uri) { + return new URIBuilder(uri); + } + + @Override + public URIBuilder uri(String uri) { + return (URIBuilder) super.uri(uri); + } + + @Override + public URIBuilder with(String name, Object value) { + return (URIBuilder) super.with(name, value); + } + + @Override + public URIBuilder with(String name, Consumer consumer) { + return (URIBuilder) super.with(name, consumer); + } + + /** + * Add a parameter to the request that you will expect to match the expected. This may be useful if a timestamp oro other random data is returned + * that is not important to assert on. + *

+ * This is only intended for use during testing. + * + * @param name the parameter name + * @return this + */ + @Override + public URIBuilder withActual(String name) { + with(name, "___actual___"); + return this; + } + + public URIBuilder withConsumer(Consumer consumer) { + if (consumer != null) { + consumer.accept(this); + } + + return this; + } + + /** + * Add a parameter as optional which means that it is not required to be on the query string, but if it is, the actual value will be used during + * the assertion. + *

+ * This is only intended for use during testing. + * + * @param name the parameter name + * @return this + */ + public URIBuilder withOptional(String name) { + with(name, "___optional___"); + return this; + } + + public URIBuilder withQueryString(String queryString) { + Map> parsed = QueryStringTools.parseQueryString(queryString); + parsed.forEach((name, values) -> values + .forEach(value -> super.with(name, value))); + return this; + } + + @Override + public URIBuilder withSegment(Object segment) { + return (URIBuilder) super.withSegment(segment); + } +} From 818fed5d25ac70ab5c99c114a9143cb7cb1bb2f9 Mon Sep 17 00:00:00 2001 From: Brady Wied Date: Mon, 24 Jun 2024 08:48:27 -0600 Subject: [PATCH 9/9] Missing more dependencies --- build.savant | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/build.savant b/build.savant index b52b1701..6e459f48 100644 --- a/build.savant +++ b/build.savant @@ -135,10 +135,14 @@ target(name: "jar", description: "JARs the project", dependsOn: ["compile"]) { ~/org\/primeframework\/mvc\/message\/TestMessageObserver/, ~/org\/primeframework\/mvc\/TestPrimeMain/, ~/org\/primeframework\/mvc\/util\/Throwing.*/, - ~/org\/primeframework\/mock/ + ~/org\/primeframework\/mock/, + // dependencies of RequestBuilder + ~/org\/primeframework\/mvc\/http\/MultipartBodyHandler.*/, + ~/org\/primeframework\/mvc\/http\/MultipartFileUpload.*/ ] def simulatorExcludePatterns = [ - ~/.*Test/ + // don't want to exclude things that start with Test, like TestPrimeMain or TestMessageObserver + ~/.*Test$/ ] // Create a separate test jar that only includes org.primeframework.mvc.test.RequestSimulator // and its dependencies