From 86f929aaf0c58eb4c6a27f4cdcb5adb6180bb6cc Mon Sep 17 00:00:00 2001 From: uchagani Date: Mon, 28 Aug 2023 15:41:44 -0400 Subject: [PATCH] feat(soft-assertions): Implement soft assertions for playwright-java (#1361) --- playwright/pom.xml | 4 + .../playwright/assertions/SoftAssertions.java | 114 ++++++++ .../impl/APIResponseAssertionsImpl.java | 2 +- .../impl/APIResponseAssertionsImplProxy.java | 45 ++++ .../impl/LocatorAssertionsImpl.java | 2 +- .../impl/LocatorAssertionsImplProxy.java | 195 ++++++++++++++ .../playwright/impl/PageAssertionsImpl.java | 2 +- .../impl/PageAssertionsImplProxy.java | 61 +++++ .../playwright/impl/SoftAssertionsBase.java | 38 +++ .../playwright/impl/SoftAssertionsImpl.java | 74 ++++++ .../TestSoftAPIResponseAssertions.java | 47 ++++ .../playwright/TestSoftAssertions.java | 46 ++++ .../playwright/TestSoftLocatorAssertions.java | 250 ++++++++++++++++++ .../playwright/TestSoftPageAssertions.java | 68 +++++ .../java/com/microsoft/playwright/Utils.java | 18 +- pom.xml | 7 + .../playwright/tools/ApiGenerator.java | 19 +- tools/test-local-installation/pom.xml | 7 + 18 files changed, 986 insertions(+), 13 deletions(-) create mode 100644 playwright/src/main/java/com/microsoft/playwright/assertions/SoftAssertions.java create mode 100644 playwright/src/main/java/com/microsoft/playwright/impl/APIResponseAssertionsImplProxy.java create mode 100644 playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImplProxy.java create mode 100644 playwright/src/main/java/com/microsoft/playwright/impl/PageAssertionsImplProxy.java create mode 100644 playwright/src/main/java/com/microsoft/playwright/impl/SoftAssertionsBase.java create mode 100644 playwright/src/main/java/com/microsoft/playwright/impl/SoftAssertionsImpl.java create mode 100644 playwright/src/test/java/com/microsoft/playwright/TestSoftAPIResponseAssertions.java create mode 100644 playwright/src/test/java/com/microsoft/playwright/TestSoftAssertions.java create mode 100644 playwright/src/test/java/com/microsoft/playwright/TestSoftLocatorAssertions.java create mode 100644 playwright/src/test/java/com/microsoft/playwright/TestSoftPageAssertions.java diff --git a/playwright/pom.xml b/playwright/pom.xml index 30db61b7..b1706c8c 100644 --- a/playwright/pom.xml +++ b/playwright/pom.xml @@ -73,5 +73,9 @@ com.microsoft.playwright driver-bundle + + org.mockito + mockito-junit-jupiter + diff --git a/playwright/src/main/java/com/microsoft/playwright/assertions/SoftAssertions.java b/playwright/src/main/java/com/microsoft/playwright/assertions/SoftAssertions.java new file mode 100644 index 00000000..618cc7a0 --- /dev/null +++ b/playwright/src/main/java/com/microsoft/playwright/assertions/SoftAssertions.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) Microsoft Corporation. + * + * 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 com.microsoft.playwright.assertions; + +import com.microsoft.playwright.impl.SoftAssertionsImpl; +import com.microsoft.playwright.APIResponse; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; + +/** + * The {@code SoftAssertions} class provides assertion methods that can be used to make multiple assertions without failing + * the test immediately. + *
{@code
+ * ...
+ * import com.microsoft.playwright.assertions.SoftAssertions;
+ *
+ * public class TestPage {
+ *   ...
+ *   @Test
+ *   void hasUrlTextPass() {
+ *     SoftAssertions softly = SoftAssertions.create();
+ *     page.getByText("Sign in").click();
+ *     softly.assertThat(page).hasURL(Pattern.compile(".*\/login"));
+ *     softly.assertAll();
+ *   }
+ * }
+ * }
+ */ +public interface SoftAssertions { + /** + * Creates a {@code SoftAssertions} object. + * + *

**Usage** + *

{@code
+   * SoftAssertions softly = SoftAssertions.create();
+   * }
+ * + * @since v1.38 + */ + static SoftAssertions create() { + return new SoftAssertionsImpl(); + } + /** + * Creates a {@code LocatorAssertions} object for the given {@code Locator}. + * + *

**Usage** + *

{@code
+   * SoftAssertions softly = SoftAssertions.create();
+   * ...
+   * softly.assertThat(locator).isVisible();
+   * }
+ * + * @param locator {@code Locator} object to use for assertions. + * @since v1.38 + */ + LocatorAssertions assertThat(Locator locator); + /** + * Creates a {@code PageAssertions} object for the given {@code Page}. + * + *

**Usage** + *

{@code
+   * SoftAssertions softly = SoftAssertions.create();
+   * ...
+   * softly.assertThat(page).hasTitle("News");
+   * }
+ * + * @param page {@code Page} object to use for assertions. + * @since v1.38 + */ + PageAssertions assertThat(Page page); + /** + * Creates a {@code APIResponseAssertions} object for the given {@code APIResponse}. + * + *

**Usage** + *

{@code
+   * SoftAssertions softly = SoftAssertions.create();
+   * ...
+   * softly.assertThat(response).isOK();
+   * }
+ * + * @param response {@code APIResponse} object to use for assertions. + * @since v1.38 + */ + APIResponseAssertions assertThat(APIResponse response); + /** + * Runs all the assertions have been executed for this {@code SoftAssertions} object. If any assertions fail, this method + * throws an AssertionFailedError with the details of all the failed assertions. + * + *

**Usage** + *

{@code
+   * SoftAssertions softly = SoftAssertions.create();
+   * ...
+   * softly.assertAll();
+   * }
+ * + * @since v1.38 + */ + void assertAll(); +} + diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/APIResponseAssertionsImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/APIResponseAssertionsImpl.java index 67d7dfc5..2893c7c1 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/APIResponseAssertionsImpl.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/APIResponseAssertionsImpl.java @@ -37,7 +37,7 @@ public class APIResponseAssertionsImpl implements APIResponseAssertions { } @Override - public APIResponseAssertions not() { + public APIResponseAssertionsImpl not() { return new APIResponseAssertionsImpl(actual, !isNot); } diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/APIResponseAssertionsImplProxy.java b/playwright/src/main/java/com/microsoft/playwright/impl/APIResponseAssertionsImplProxy.java new file mode 100644 index 00000000..7e35212c --- /dev/null +++ b/playwright/src/main/java/com/microsoft/playwright/impl/APIResponseAssertionsImplProxy.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) Microsoft Corporation. + * + * 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 com.microsoft.playwright.impl; + +import com.microsoft.playwright.APIResponse; +import com.microsoft.playwright.assertions.APIResponseAssertions; + +import java.util.List; + +public class APIResponseAssertionsImplProxy extends SoftAssertionsBase implements APIResponseAssertions { + private final APIResponseAssertionsImpl apiResponseAssertionsImpl; + + APIResponseAssertionsImplProxy(APIResponse response, List results) { + this(results, new APIResponseAssertionsImpl(response)); + } + + private APIResponseAssertionsImplProxy(List results, APIResponseAssertionsImpl apiResponseAssertionsImpl) { + super(results); + this.apiResponseAssertionsImpl = apiResponseAssertionsImpl; + } + + @Override + public APIResponseAssertions not() { + return new APIResponseAssertionsImplProxy(super.results, apiResponseAssertionsImpl.not()); + } + + @Override + public void isOK() { + assertAndCaptureResult(apiResponseAssertionsImpl::isOK); + } +} diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImpl.java index 3b821318..144b613a 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImpl.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImpl.java @@ -353,7 +353,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse } @Override - public LocatorAssertions not() { + public LocatorAssertionsImpl not() { return new LocatorAssertionsImpl(actualLocator, !isNot); } diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImplProxy.java b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImplProxy.java new file mode 100644 index 00000000..422510a9 --- /dev/null +++ b/playwright/src/main/java/com/microsoft/playwright/impl/LocatorAssertionsImplProxy.java @@ -0,0 +1,195 @@ +package com.microsoft.playwright.impl; + +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.assertions.LocatorAssertions; + +import java.util.List; +import java.util.regex.Pattern; + +public class LocatorAssertionsImplProxy extends SoftAssertionsBase implements LocatorAssertions { + private final LocatorAssertionsImpl locatorAssertionsImpl; + + LocatorAssertionsImplProxy(Locator locator, List results) { + this(results, new LocatorAssertionsImpl(locator)); + } + + private LocatorAssertionsImplProxy(List results, LocatorAssertionsImpl locatorAssertionsImpl) { + super(results); + this.locatorAssertionsImpl = locatorAssertionsImpl; + } + + @Override + public LocatorAssertions not() { + return new LocatorAssertionsImplProxy(super.results, locatorAssertionsImpl.not()); + } + + @Override + public void isAttached(IsAttachedOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.isAttached(options)); + } + + @Override + public void isChecked(IsCheckedOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.isChecked(options)); + } + + @Override + public void isDisabled(IsDisabledOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.isDisabled(options)); + } + + @Override + public void isEditable(IsEditableOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.isEditable(options)); + } + + @Override + public void isEmpty(IsEmptyOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.isEmpty(options)); + } + + @Override + public void isEnabled(IsEnabledOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.isEnabled(options)); + } + + @Override + public void isFocused(IsFocusedOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.isFocused(options)); + } + + @Override + public void isHidden(IsHiddenOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.isHidden(options)); + } + + @Override + public void isInViewport(IsInViewportOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.isInViewport(options)); + } + + @Override + public void isVisible(IsVisibleOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.isVisible(options)); + } + + @Override + public void containsText(String expected, ContainsTextOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.containsText(expected, options)); + } + + @Override + public void containsText(Pattern expected, ContainsTextOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.containsText(expected, options)); + } + + @Override + public void containsText(String[] expected, ContainsTextOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.containsText(expected, options)); + } + + @Override + public void containsText(Pattern[] expected, ContainsTextOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.containsText(expected, options)); + } + + @Override + public void hasAttribute(String name, String value, HasAttributeOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasAttribute(name, value, options)); + } + + @Override + public void hasAttribute(String name, Pattern value, HasAttributeOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasAttribute(name, value, options)); + } + + @Override + public void hasClass(String expected, HasClassOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasClass(expected, options)); + } + + @Override + public void hasClass(Pattern expected, HasClassOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasClass(expected, options)); + } + + @Override + public void hasClass(String[] expected, HasClassOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasClass(expected, options)); + } + + @Override + public void hasClass(Pattern[] expected, HasClassOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasClass(expected, options)); + } + + @Override + public void hasCount(int count, HasCountOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasCount(count, options)); + } + + @Override + public void hasCSS(String name, String value, HasCSSOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasCSS(name, value, options)); + } + + @Override + public void hasCSS(String name, Pattern value, HasCSSOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasCSS(name, value, options)); + } + + @Override + public void hasId(String id, HasIdOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasId(id, options)); + } + + @Override + public void hasId(Pattern id, HasIdOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasId(id, options)); + } + + @Override + public void hasJSProperty(String name, Object value, HasJSPropertyOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasJSProperty(name, value, options)); + } + + @Override + public void hasText(String expected, HasTextOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasText(expected, options)); + } + + @Override + public void hasText(Pattern expected, HasTextOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasText(expected, options)); + } + + @Override + public void hasText(String[] expected, HasTextOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasText(expected, options)); + } + + @Override + public void hasText(Pattern[] expected, HasTextOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasText(expected, options)); + } + + @Override + public void hasValue(String value, HasValueOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasValue(value, options)); + } + + @Override + public void hasValue(Pattern value, HasValueOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasValue(value, options)); + } + + @Override + public void hasValues(String[] values, HasValuesOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasValues(values, options)); + } + + @Override + public void hasValues(Pattern[] values, HasValuesOptions options) { + assertAndCaptureResult(() -> locatorAssertionsImpl.hasValues(values, options)); + } +} diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/PageAssertionsImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/PageAssertionsImpl.java index ee6e7158..616b282d 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/PageAssertionsImpl.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/PageAssertionsImpl.java @@ -67,7 +67,7 @@ public class PageAssertionsImpl extends AssertionsBase implements PageAssertions } @Override - public PageAssertions not() { + public PageAssertionsImpl not() { return new PageAssertionsImpl(actualPage, !isNot); } } diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/PageAssertionsImplProxy.java b/playwright/src/main/java/com/microsoft/playwright/impl/PageAssertionsImplProxy.java new file mode 100644 index 00000000..51e8a6a6 --- /dev/null +++ b/playwright/src/main/java/com/microsoft/playwright/impl/PageAssertionsImplProxy.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) Microsoft Corporation. + * + * 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 com.microsoft.playwright.impl; + +import com.microsoft.playwright.Page; +import com.microsoft.playwright.assertions.PageAssertions; + +import java.util.List; +import java.util.regex.Pattern; + +public class PageAssertionsImplProxy extends SoftAssertionsBase implements PageAssertions { + private final PageAssertionsImpl pageAssertionsImpl; + + PageAssertionsImplProxy(Page page, List results) { + this(results, new PageAssertionsImpl(page)); + } + + private PageAssertionsImplProxy(List results, PageAssertionsImpl pageAssertionsImpl) { + super(results); + this.pageAssertionsImpl = pageAssertionsImpl; + } + + @Override + public PageAssertions not() { + return new PageAssertionsImplProxy(super.results, pageAssertionsImpl.not()); + } + + @Override + public void hasTitle(String titleOrRegExp, HasTitleOptions options) { + assertAndCaptureResult(() -> pageAssertionsImpl.hasTitle(titleOrRegExp, options)); + } + + @Override + public void hasTitle(Pattern titleOrRegExp, HasTitleOptions options) { + assertAndCaptureResult(() -> pageAssertionsImpl.hasTitle(titleOrRegExp, options)); + } + + @Override + public void hasURL(String urlOrRegExp, HasURLOptions options) { + assertAndCaptureResult(() -> pageAssertionsImpl.hasURL(urlOrRegExp, options)); + } + + @Override + public void hasURL(Pattern urlOrRegExp, HasURLOptions options) { + assertAndCaptureResult(() -> pageAssertionsImpl.hasURL(urlOrRegExp, options)); + } +} diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/SoftAssertionsBase.java b/playwright/src/main/java/com/microsoft/playwright/impl/SoftAssertionsBase.java new file mode 100644 index 00000000..917cd629 --- /dev/null +++ b/playwright/src/main/java/com/microsoft/playwright/impl/SoftAssertionsBase.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) Microsoft Corporation. + * + * 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 com.microsoft.playwright.impl; + +import com.microsoft.playwright.PlaywrightException; +import org.opentest4j.AssertionFailedError; + +import java.util.List; + +class SoftAssertionsBase { + final List results; + + SoftAssertionsBase(List results) { + this.results = results; + } + + void assertAndCaptureResult(Runnable assertion) { + try { + assertion.run(); + } catch (AssertionFailedError | PlaywrightException failure) { + results.add(failure); + } + } +} diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/SoftAssertionsImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/SoftAssertionsImpl.java new file mode 100644 index 00000000..034e08ee --- /dev/null +++ b/playwright/src/main/java/com/microsoft/playwright/impl/SoftAssertionsImpl.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) Microsoft Corporation. + * + * 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 com.microsoft.playwright.impl; + +import com.microsoft.playwright.APIResponse; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.assertions.APIResponseAssertions; +import com.microsoft.playwright.assertions.LocatorAssertions; +import com.microsoft.playwright.assertions.PageAssertions; +import com.microsoft.playwright.assertions.SoftAssertions; +import org.opentest4j.AssertionFailedError; + +import java.util.ArrayList; +import java.util.List; + +public class SoftAssertionsImpl implements SoftAssertions { + final List results; + + public SoftAssertionsImpl() { + this.results = new ArrayList<>(); + } + + @Override + public LocatorAssertions assertThat(Locator locator) { + return new LocatorAssertionsImplProxy(locator, results); + } + + @Override + public PageAssertions assertThat(Page page) { + return new PageAssertionsImplProxy(page, results); + } + + @Override + public APIResponseAssertions assertThat(APIResponse response) { + return new APIResponseAssertionsImplProxy(response, results); + } + + @Override + public void assertAll() { + if (!results.isEmpty()) { + throw new AssertionFailedError(getFormattedErrorMessage()); + } + } + + private String getFormattedErrorMessage() { + StringBuilder message = new StringBuilder(); + message + .append(results.size()) + .append(" assertion(s) failed:"); + + for (Throwable t : results) { + message.append("\n"); + message.append("----------------------------------------\n"); + message.append(t.getMessage()); + } + + return message.toString(); + } +} diff --git a/playwright/src/test/java/com/microsoft/playwright/TestSoftAPIResponseAssertions.java b/playwright/src/test/java/com/microsoft/playwright/TestSoftAPIResponseAssertions.java new file mode 100644 index 00000000..26db96f9 --- /dev/null +++ b/playwright/src/test/java/com/microsoft/playwright/TestSoftAPIResponseAssertions.java @@ -0,0 +1,47 @@ +package com.microsoft.playwright; + +import com.microsoft.playwright.assertions.APIResponseAssertions; +import com.microsoft.playwright.impl.APIResponseAssertionsImpl; +import com.microsoft.playwright.impl.APIResponseAssertionsImplProxy; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static com.microsoft.playwright.Utils.createProxy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.verify; + +// The only thing we want to verify in these tests is that the correct method was called +@Tag("mockito") +@ExtendWith(MockitoExtension.class) +public class TestSoftAPIResponseAssertions { + @Mock + private APIResponseAssertionsImpl apiResponseAssertionsMock; + private APIResponseAssertionsImplProxy proxy; + + @BeforeEach + void beforeEach() { + proxy = createProxy(APIResponseAssertionsImplProxy.class, apiResponseAssertionsMock); + } + + @Test + void proxyImplementsAPIResponseAssertions() { + assertTrue(APIResponseAssertions.class.isAssignableFrom(proxy.getClass())); + } + + @Test + void not() { + proxy.not(); + verify(apiResponseAssertionsMock).not(); + } + + @Test + void isOK() { + assertDoesNotThrow(() -> proxy.isOK()); + verify(apiResponseAssertionsMock).isOK(); + } +} diff --git a/playwright/src/test/java/com/microsoft/playwright/TestSoftAssertions.java b/playwright/src/test/java/com/microsoft/playwright/TestSoftAssertions.java new file mode 100644 index 00000000..d265c1ff --- /dev/null +++ b/playwright/src/test/java/com/microsoft/playwright/TestSoftAssertions.java @@ -0,0 +1,46 @@ +package com.microsoft.playwright; + +import com.microsoft.playwright.assertions.LocatorAssertions; +import com.microsoft.playwright.assertions.SoftAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestSoftAssertions extends TestBase { + private SoftAssertions softly; + + @BeforeEach + void beforeEach() { + softly = SoftAssertions.create(); + } + + @Test + void canMakeMultipleAssertionsWithoutFailingImmediately() { + page.setContent("
Text content
"); + Locator locator = page.locator(".foo"); + assertDoesNotThrow(() -> softly.assertThat(locator).hasText("Text content")); + assertDoesNotThrow(() -> softly.assertThat(locator).hasClass("foo bar")); + assertDoesNotThrow(() -> softly.assertThat(locator).hasId("node")); + softly.assertAll(); + } + + @Test + void failureMessageIncludesMessagesFromAllAssertions() { + page.setContent("
Text content
"); + Locator locator = page.locator(".foo"); + assertDoesNotThrow(() -> softly.assertThat(locator).hasText("some text", new LocatorAssertions.HasTextOptions().setTimeout(1000))); + assertDoesNotThrow(() -> softly.assertThat(locator).hasClass("abc", new LocatorAssertions.HasClassOptions().setTimeout(1000))); + assertDoesNotThrow(() -> softly.assertThat(locator).hasId("foo", new LocatorAssertions.HasIdOptions().setTimeout(1000))); + AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> softly.assertAll()); + + assertTrue(e.getMessage().contains("3 assertion(s) failed")); + assertTrue(e.getMessage().contains("Locator expected to have text: some text")); + assertTrue(e.getMessage().contains("Received: Text content")); + assertTrue(e.getMessage().contains("Locator expected to have class: abc")); + assertTrue(e.getMessage().contains("Received: foo bar")); + assertTrue(e.getMessage().contains("Locator expected to have ID: foo")); + assertTrue(e.getMessage().contains("Received: node")); + } +} diff --git a/playwright/src/test/java/com/microsoft/playwright/TestSoftLocatorAssertions.java b/playwright/src/test/java/com/microsoft/playwright/TestSoftLocatorAssertions.java new file mode 100644 index 00000000..b913c26f --- /dev/null +++ b/playwright/src/test/java/com/microsoft/playwright/TestSoftLocatorAssertions.java @@ -0,0 +1,250 @@ +package com.microsoft.playwright; + +import com.microsoft.playwright.assertions.LocatorAssertions; +import com.microsoft.playwright.impl.LocatorAssertionsImpl; +import com.microsoft.playwright.impl.LocatorAssertionsImplProxy; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.regex.Pattern; + +import static com.microsoft.playwright.Utils.createProxy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.verify; + +// The only thing we want to verify in these tests is that the correct method was called +@Tag("mockito") +@ExtendWith(MockitoExtension.class) +public class TestSoftLocatorAssertions { + @Mock + private LocatorAssertionsImpl locatorAssertionsMock; + private final static Pattern pattern = Pattern.compile(""); + private final static Pattern[] patternArray = new Pattern[]{}; + private final static String[] stringArray = new String[]{}; + private LocatorAssertionsImplProxy proxy; + + @BeforeEach + void beforeEach() { + proxy = createProxy(LocatorAssertionsImplProxy.class, locatorAssertionsMock); + } + + @Test + void proxyImplementsLocatorAssertions() { + assertTrue(LocatorAssertions.class.isAssignableFrom(proxy.getClass())); + } + + @Test + void not() { + proxy.not(); + verify(locatorAssertionsMock).not(); + } + + @Test + void isAttached() { + assertDoesNotThrow(() -> proxy.isAttached(null)); + verify(locatorAssertionsMock).isAttached(null); + } + + @Test + void isChecked() { + assertDoesNotThrow(() -> proxy.isChecked(null)); + verify(locatorAssertionsMock).isChecked(null); + } + + @Test + void isDisabled() { + assertDoesNotThrow(() -> proxy.isDisabled(null)); + verify(locatorAssertionsMock).isDisabled(null); + } + + @Test + void isEditable() { + assertDoesNotThrow(() -> proxy.isEditable(null)); + verify(locatorAssertionsMock).isEditable(null); + } + + @Test + void isEmpty() { + assertDoesNotThrow(() -> proxy.isEmpty(null)); + verify(locatorAssertionsMock).isEmpty(null); + } + + @Test + void isEnabled() { + assertDoesNotThrow(() -> proxy.isEnabled(null)); + verify(locatorAssertionsMock).isEnabled(null); + } + + @Test + void isFocused() { + assertDoesNotThrow(() -> proxy.isFocused(null)); + verify(locatorAssertionsMock).isFocused(null); + } + + @Test + void isHidden() { + assertDoesNotThrow(() -> proxy.isHidden(null)); + verify(locatorAssertionsMock).isHidden(null); + } + + @Test + void isInViewPort() { + assertDoesNotThrow(() -> proxy.isInViewport(null)); + verify(locatorAssertionsMock).isInViewport(null); + } + + @Test + void isVisible() { + assertDoesNotThrow(() -> proxy.isVisible(null)); + verify(locatorAssertionsMock).isVisible(null); + } + + @Test + void containsTextString() { + assertDoesNotThrow(() -> proxy.containsText("", null)); + verify(locatorAssertionsMock).containsText("", null); + } + + @Test + void containsTextPattern() { + assertDoesNotThrow(() -> proxy.containsText(pattern, null)); + verify(locatorAssertionsMock).containsText(pattern, null); + } + + @Test + void containsTextStringArray() { + assertDoesNotThrow(() -> proxy.containsText(stringArray, null)); + verify(locatorAssertionsMock).containsText(stringArray, null); + } + + @Test + void containsTextPatternArray() { + assertDoesNotThrow(() -> proxy.containsText(patternArray, null)); + verify(locatorAssertionsMock).containsText(patternArray, null); + } + + @Test + void hasAttributeStringValue() { + assertDoesNotThrow(() -> proxy.hasAttribute("", "", null)); + verify(locatorAssertionsMock).hasAttribute("", "", null); + } + + @Test + void hasAttributePatternValue() { + assertDoesNotThrow(() -> proxy.hasAttribute("", pattern, null)); + verify(locatorAssertionsMock).hasAttribute("", pattern, null); + } + + @Test + void hasClassString() { + assertDoesNotThrow(() -> proxy.hasClass("", null)); + verify(locatorAssertionsMock).hasClass("", null); + } + + @Test + void hasClassPattern() { + assertDoesNotThrow(() -> proxy.hasClass(pattern, null)); + verify(locatorAssertionsMock).hasClass(pattern, null); + } + + @Test + void hasClassStringArray() { + assertDoesNotThrow(() -> proxy.hasClass(stringArray, null)); + verify(locatorAssertionsMock).hasClass(stringArray, null); + } + + @Test + void hasClassPatternArray() { + assertDoesNotThrow(() -> proxy.hasClass(patternArray, null)); + verify(locatorAssertionsMock).hasClass(patternArray, null); + } + + @Test + void hasCount() { + assertDoesNotThrow(() -> proxy.hasCount(0, null)); + verify(locatorAssertionsMock).hasCount(0, null); + } + + @Test + void hasCSSString() { + assertDoesNotThrow(() -> proxy.hasCSS("", "", null)); + verify(locatorAssertionsMock).hasCSS("", "", null); + } + + @Test + void hasCSSPattern() { + assertDoesNotThrow(() -> proxy.hasCSS("", pattern, null)); + verify(locatorAssertionsMock).hasCSS("", pattern, null); + } + + @Test + void hasIdString() { + assertDoesNotThrow(() -> proxy.hasId("", null)); + verify(locatorAssertionsMock).hasId("", null); + } + + @Test + void hasIdPattern() { + assertDoesNotThrow(() -> proxy.hasId(pattern, null)); + verify(locatorAssertionsMock).hasId(pattern, null); + } + + @Test + void hasJSProperty() { + assertDoesNotThrow(() -> proxy.hasJSProperty("", null, null)); + verify(locatorAssertionsMock).hasJSProperty("", null, null); + } + + @Test + void hasTextString() { + assertDoesNotThrow(() -> proxy.hasText("", null)); + verify(locatorAssertionsMock).hasText("", null); + } + + @Test + void hasTextPattern() { + assertDoesNotThrow(() -> proxy.hasText(pattern, null)); + verify(locatorAssertionsMock).hasText(pattern, null); + } + + @Test + void hasTextStringArray() { + assertDoesNotThrow(() -> proxy.hasText(stringArray, null)); + verify(locatorAssertionsMock).hasText(stringArray, null); + } + + @Test + void hasTextPatternArray() { + assertDoesNotThrow(() -> proxy.hasText(patternArray, null)); + verify(locatorAssertionsMock).hasText(patternArray, null); + } + + @Test + void hasValueString() { + assertDoesNotThrow(() -> proxy.hasValue("", null)); + verify(locatorAssertionsMock).hasValue("", null); + } + + @Test + void hasValuePattern() { + assertDoesNotThrow(() -> proxy.hasValue(pattern, null)); + verify(locatorAssertionsMock).hasValue(pattern, null); + } + + @Test + void hasValuesStringArray() { + assertDoesNotThrow(() -> proxy.hasValues(stringArray, null)); + verify(locatorAssertionsMock).hasValues(stringArray, null); + } + + @Test + void hasValuesPatternArray() { + assertDoesNotThrow(() -> proxy.hasValues(patternArray, null)); + verify(locatorAssertionsMock).hasValues(patternArray, null); + } +} diff --git a/playwright/src/test/java/com/microsoft/playwright/TestSoftPageAssertions.java b/playwright/src/test/java/com/microsoft/playwright/TestSoftPageAssertions.java new file mode 100644 index 00000000..53fc41b1 --- /dev/null +++ b/playwright/src/test/java/com/microsoft/playwright/TestSoftPageAssertions.java @@ -0,0 +1,68 @@ +package com.microsoft.playwright; + +import com.microsoft.playwright.assertions.PageAssertions; +import com.microsoft.playwright.impl.PageAssertionsImpl; +import com.microsoft.playwright.impl.PageAssertionsImplProxy; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.regex.Pattern; + +import static com.microsoft.playwright.Utils.createProxy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.verify; + +// The only thing we want to verify in these tests is that the correct method was called +@Tag("mockito") +@ExtendWith(MockitoExtension.class) +public class TestSoftPageAssertions { + @Mock + private PageAssertionsImpl pageAssertionsMock; + private PageAssertionsImplProxy proxy; + private final static Pattern pattern = Pattern.compile(""); + + @BeforeEach + void beforeEach() { + proxy = createProxy(PageAssertionsImplProxy.class, pageAssertionsMock); + } + + @Test + void proxyImplementsPageAssertions() { + assertTrue(PageAssertions.class.isAssignableFrom(proxy.getClass())); + } + + @Test + void not() { + proxy.not(); + verify(pageAssertionsMock).not(); + } + + @Test + void hasTitleString() { + assertDoesNotThrow(() -> proxy.hasTitle("", null)); + verify(pageAssertionsMock).hasTitle("", null); + } + + @Test + void hasTitlePattern() { + assertDoesNotThrow(() -> proxy.hasTitle(pattern, null)); + verify(pageAssertionsMock).hasTitle(pattern, null); + } + + @Test + void hasURLString() { + assertDoesNotThrow(() -> proxy.hasURL("", null)); + verify(pageAssertionsMock).hasURL("", null); + } + + @Test + void hasURLPattern() { + assertDoesNotThrow(() -> proxy.hasURL(pattern, null)); + verify(pageAssertionsMock).hasURL(pattern, null); + } +} diff --git a/playwright/src/test/java/com/microsoft/playwright/Utils.java b/playwright/src/test/java/com/microsoft/playwright/Utils.java index dff6bab2..e4e9a21d 100644 --- a/playwright/src/test/java/com/microsoft/playwright/Utils.java +++ b/playwright/src/test/java/com/microsoft/playwright/Utils.java @@ -21,18 +21,18 @@ import com.google.gson.JsonElement; import com.google.gson.JsonParser; import java.io.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.net.ServerSocket; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mockingDetails; class Utils { private static final AtomicInteger nextUnusedPort = new AtomicInteger(9000); @@ -214,4 +214,14 @@ class Utils { static String generateDifferentOriginPort(final Server server){ return server.PREFIX.replace(String.valueOf(server.PORT), String.valueOf(server.PORT+1)); } + + static T createProxy(Class proxyToCreate, Object underlyingObject) { + try { + Constructor constructor = proxyToCreate.getDeclaredConstructor(List.class, mockingDetails(underlyingObject).getMockCreationSettings().getTypeToMock()); + constructor.setAccessible(true); + return (T) proxyToCreate.cast(constructor.newInstance(new ArrayList<>(), underlyingObject)); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { + throw new RuntimeException("Unable to create proxy", e); + } + } } diff --git a/pom.xml b/pom.xml index 38e62faa..a16520bb 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,7 @@ UTF-8 1.5.1 1.2.0 + 4.11.0 @@ -85,6 +86,12 @@ ${websocket.version} test + + org.mockito + mockito-junit-jupiter + ${mockito.version} + test + diff --git a/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java b/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java index 070f4d94..2951a82a 100644 --- a/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java +++ b/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java @@ -688,6 +688,13 @@ class Method extends Element { output.add(""); return; } + if ("SoftAssertions.create".equals(jsonPath)) { + writeJavadoc(params, output, offset); + output.add(offset + "static SoftAssertions create() {"); + output.add(offset + " return new SoftAssertionsImpl();"); + output.add(offset + "}"); + return; + } int numOverloads = 1; for (int i = 0; i < params.size(); i++) { if (params.get(i).type.isTypeUnion()) { @@ -989,6 +996,11 @@ class Interface extends TypeDefinition { output.add("import com.microsoft.playwright.impl.LocatorAssertionsImpl;"); output.add("import com.microsoft.playwright.impl.PageAssertionsImpl;"); } + if ("SoftAssertions".equals(jsonName)) { + output.add("import com.microsoft.playwright.APIResponse;"); + output.add("import com.microsoft.playwright.Locator;"); + output.add("import com.microsoft.playwright.Page;"); + } output.add(""); List superInterfaces = new ArrayList<>(); @@ -1156,18 +1168,13 @@ public class ApiGenerator { File assertionsDir = new File(cwd,"playwright/src/main/java/com/microsoft/playwright/assertions"); System.out.println("Writing assertion files to: " + dir.getCanonicalPath()); - generate(api, assertionsDir, "com.microsoft.playwright.assertions", isAssertion().and(isSoftAssertion().negate())); + generate(api, assertionsDir, "com.microsoft.playwright.assertions", isAssertion()); } private static Predicate isAssertion() { return className -> className.toLowerCase().contains("assert"); } - // TODO: Remove this predicate once SoftAssertions are implemented. - private static Predicate isSoftAssertion() { - return className -> className.contains("SoftAssertions"); - } - private void generate(JsonArray api, File dir, String packageName, Predicate classFilter) throws IOException { Map topLevelTypes = new HashMap<>(); for (JsonElement entry: api) { diff --git a/tools/test-local-installation/pom.xml b/tools/test-local-installation/pom.xml index 19502d57..33b1fcc7 100644 --- a/tools/test-local-installation/pom.xml +++ b/tools/test-local-installation/pom.xml @@ -13,6 +13,7 @@ 5.7.0 UTF-8 1.5.1 + 4.11.0 @@ -43,6 +44,12 @@ Java-WebSocket ${websocket.version} + + org.mockito + mockito-junit-jupiter + ${mockito.version} + test +