diff --git a/docs/modules/plugins/pages/plugin-mobile-app.adoc b/docs/modules/plugins/pages/plugin-mobile-app.adoc index 9f259747be..7b3b7d03f8 100644 --- a/docs/modules/plugins/pages/plugin-mobile-app.adoc +++ b/docs/modules/plugins/pages/plugin-mobile-app.adoc @@ -531,6 +531,7 @@ When I download file `/sdcard/file.json` from device and save its content to sce include::partial$ui-context-management-steps.adoc[] +include::plugins:partial$ui-selenium-text-content-steps.adoc[] === Navigate back diff --git a/docs/modules/plugins/pages/plugin-web-app-playwright.adoc b/docs/modules/plugins/pages/plugin-web-app-playwright.adoc index 7743627bcb..176a8d5a9b 100644 --- a/docs/modules/plugins/pages/plugin-web-app-playwright.adoc +++ b/docs/modules/plugins/pages/plugin-web-app-playwright.adoc @@ -106,3 +106,26 @@ When I clear field located by `$locator` ---- When I clear field located by `id(email)` ---- + +include::plugins:partial$ui-text-content-steps.adoc[] + +==== Save the text of the context + +Saves the text of the context into a variable. + +[source,gherkin] +---- +When I save text of context to $scopes variable `$variableName` +---- + +* `$scopes` - The comma-separated set of the xref:commons:variables.adoc#_scopes[variables scopes]. +* `$variableName` - The name of the variable to save the text content. + +.Save the text of the context +[source,gherkin] +---- +When I change context to element located by `id(username)` +When I save text of context element to scneario variable `username` +---- + +include::plugins:partial$common-web-app-text-steps.adoc[] diff --git a/docs/modules/plugins/partials/common-web-app-text-steps.adoc b/docs/modules/plugins/partials/common-web-app-text-steps.adoc new file mode 100644 index 0000000000..02371cf37b --- /dev/null +++ b/docs/modules/plugins/partials/common-web-app-text-steps.adoc @@ -0,0 +1,57 @@ +=== Text content validation + +The context can be set by the <<_change_context,corresponding steps>>. If no context is set, the text will be searched across the whole page. + + +==== Validate the text exists + +Validates the text is present in the current context. The expected text is *case-sensitive*. + +[source,gherkin] +---- +Then text `$text` exists +---- +* `$text` - The expected text to be found in the context text. + +.Check the text 'Contract Us' is present on the page +[source,gherkin] +---- +Given I am on page with URL `https://docs.vividus.dev/` +Then text `Contact Us` exists +---- + + +==== Validate the text does not exists + +Validates the text is not present in the current context. + +[source,gherkin] +---- +Then text `$text` does not exist +---- +* `$text` - The text that should not be present in the context. + +.Check the text 'Deprecated' is not present in the element +[source,gherkin] +---- +When I change context to element located by `id(code)` +Then text `Deprecated` does not exist +---- + + +==== Validate the text matches regular expression + +Validates the text from current context matches the specified regular expression. + +[source,gherkin] +---- +Then text matches `$regex` +---- +* `$regex` - The https://www.regular-expressions.info[regular expression] used to validate the context text. + +.Check the text with pattern 'User ".*" successfully logged in' is present in the current context +[source,gherkin] +---- +When I change context to element located by `id(message)` +Then text matches `User ".*" successfully logged in` +---- diff --git a/docs/modules/plugins/partials/generic-ui-steps.adoc b/docs/modules/plugins/partials/generic-ui-steps.adoc index 39d062f58d..5a1217c0fd 100644 --- a/docs/modules/plugins/partials/generic-ui-steps.adoc +++ b/docs/modules/plugins/partials/generic-ui-steps.adoc @@ -197,49 +197,6 @@ Then number of $state elements found by `$locator` is $comparisonRule `$quantity Then number of VISIBLE elements found by `tagName(img)` is = `1` ---- -=== Save text of an element - -Saves text of an element into a variable. - -[source,gherkin] ----- -When I save text of element located by `$locator` to $scopes variable `$variableName` ----- - -* `$locator` - <<_locator>>. -* `$scopes` - xref:commons:variables.adoc#_scopes[The comma-separated set of the variables scopes]. -* `$variableName` - The name of the variable to save the text. - -.Save the text of H1 element -[source,gherkin] ----- -When I save text of element located by `tagName(h1)` to scenario variable `headingText` ----- - - -=== Save the text of the context - -Saves the text of the context element into a variable. - -[WARNING] -Step will throw an error if the context element is not set. - -[source,gherkin] ----- -When I save text of context element to $scopes variable `$variableName` ----- - -* `$scopes` - xref:commons:variables.adoc#_scopes[The comma-separated set of the variables scopes]. -* `$variableName` - The name of the variable to save the text. - -.Save the text of the context element -[source,gherkin] ----- -When I change context to element located by `id(username)` -When I save text of context element to SCENARIO variable `username` ----- - - === Saves the attribute value of the context Saves the attribute value of the context element into a variable. diff --git a/docs/modules/plugins/partials/plugin-web-app-steps.adoc b/docs/modules/plugins/partials/plugin-web-app-steps.adoc index 65de095433..0560ec2000 100644 --- a/docs/modules/plugins/partials/plugin-web-app-steps.adoc +++ b/docs/modules/plugins/partials/plugin-web-app-steps.adoc @@ -170,72 +170,9 @@ Then page is scrolled to element located by `$locator` Then page is scrolled to element located by `xpath(//a[text()="Contact"])` ---- -=== Text validation steps +include::plugins:partial$ui-selenium-text-content-steps.adoc[] +include::plugins:partial$common-web-app-text-steps.adoc[] -:context-description: The context can be set by the <<_change_context,corresponding steps>>. If no context is set, the text will be searched on the whole page. - -==== Validate the text exists - -Validates that the text is presented in the current context. Expected text is *case sensitive*. - -{context-description} - -[source,gherkin] ----- -Then text `$text` exists ----- -* `$text` - Expected text. - -.Check the text 'Contract Us' is presented on the page -[source,gherkin] ----- -Given I am on page with URL `https://docs.vividus.dev/` -Then text `Contract Us` exists ----- - -==== Validate the text does not exists - -Validates that the text is not presented in the current context. - -{context-description} - -[source,gherkin] ----- -Then text `$text` does not exist ----- -* `$text` - Text that should not exist. - -.Check the text 'Deprecated' is not presented in the element -[source,gherkin] ----- -When I change context to element located by `id(code)` -Then text `Deprecated` does not exist ----- - -==== Validate the text matches regex - -Validates that the text from current context matches the specified regular expression. - -{context-description} - -[source,gherkin] ----- -Then text matches `$regex` ----- - -_Deprecated syntax (will be removed in VIVIDUS 0.7.0)_: -[source,gherkin] ----- -Then the text matches '$regex' ----- -* `$regex` - The https://www.regular-expressions.info[regular expression] used to validate the text of the context. - -.Check the text with pattern 'User ".*" successfully logged in' is presented in the current context -[source,gherkin] ----- -When I change context to element located by `id(code)` -Then text matches `User ".*" successfully logged in` ----- === Tab steps ==== Open a new tab diff --git a/docs/modules/plugins/partials/ui-selenium-text-content-steps.adoc b/docs/modules/plugins/partials/ui-selenium-text-content-steps.adoc new file mode 100644 index 0000000000..f64f4ee272 --- /dev/null +++ b/docs/modules/plugins/partials/ui-selenium-text-content-steps.adoc @@ -0,0 +1,23 @@ +include::plugins:partial$ui-text-content-steps.adoc[] + +==== Save the text of the context + +Saves the text of the context element into a variable. + +[NOTE] +An error is thrown if the context is not set to the element. + +[source,gherkin] +---- +When I save text of context element to $scopes variable `$variableName` +---- + +* `$scopes` - The comma-separated set of the xref:commons:variables.adoc#_scopes[variables scopes]. +* `$variableName` - The name of the variable to save the text content. + +.Save the text of the context element +[source,gherkin] +---- +When I change context to element located by `id(username)` +When I save text of context element to scneario variable `username` +---- diff --git a/docs/modules/plugins/partials/ui-text-content-steps.adoc b/docs/modules/plugins/partials/ui-text-content-steps.adoc new file mode 100644 index 0000000000..9c677968cd --- /dev/null +++ b/docs/modules/plugins/partials/ui-text-content-steps.adoc @@ -0,0 +1,20 @@ +=== Text content manupulations + +==== Save the text of an element + +Finds the element by the given locator and saves its text into a variable. + +[source,gherkin] +---- +When I save text of element located by `$locator` to $scopes variable `$variableName` +---- + +* `$locator` - The <<_locator,locator>> used to find the element whose text content will be saved. +* `$scopes` - The comma-separated set of the xref:commons:variables.adoc#_scopes[variables scopes]. +* `$variableName` - The name of the variable to save the text content. + +.Save the text of the header element +[source,gherkin] +---- +When I save text of element located by `id(header)` to scenario variable `heading-text` +---- diff --git a/vividus-engine/src/main/resources/org/vividus/engine/spring.xml b/vividus-engine/src/main/resources/org/vividus/engine/spring.xml index 2c0fc965d5..67dda1ae0b 100644 --- a/vividus-engine/src/main/resources/org/vividus/engine/spring.xml +++ b/vividus-engine/src/main/resources/org/vividus/engine/spring.xml @@ -60,7 +60,7 @@ - + diff --git a/vividus-extension-selenium/src/main/java/org/vividus/steps/ui/GenericSetVariableSteps.java b/vividus-extension-selenium/src/main/java/org/vividus/steps/ui/GenericSetVariableSteps.java index 1a0f51e700..6e75d03c9e 100644 --- a/vividus-extension-selenium/src/main/java/org/vividus/steps/ui/GenericSetVariableSteps.java +++ b/vividus-extension-selenium/src/main/java/org/vividus/steps/ui/GenericSetVariableSteps.java @@ -56,21 +56,18 @@ public GenericSetVariableSteps(ISoftAssert softAssert, IBaseValidations baseVali } /** - * Extracts the text of element found in the context and saves it to the variable with the specified - * variableName - * Actions performed at this step: - *
    - *
  • Saves the text of element into the variable name - *
- * @param scopes The set (comma separated list of scopes e.g.: STORY, NEXT_BATCHES) of variable's scope
- * Available scopes: - *
    - *
  • STEP - the variable will be available only within the step - *
  • SCENARIO - the variable will be available only within the scenario - *
  • STORY - the variable will be available within the whole story - *
  • NEXT_BATCHES - the variable will be available starting from next batch - *
- * @param variableName A name under which the value should be saved + * Saves the text of the context element into a variable. + * + * @param scopes The set (comma separated list of scopes e.g.: STORY, NEXT_BATCHES) of the variable scopes. + *
+ * Available scopes: + *
    + *
  • STEP - the variable will be available only within the step, + *
  • SCENARIO - the variable will be available only within the scenario, + *
  • STORY - the variable will be available within the whole story, + *
  • NEXT_BATCHES - the variable will be available starting from next batch + *
+ * @param variableName The name of the variable to save the text content. */ @When("I save text of context element to $scopes variable `$variableName`") public void saveContextElementTextToVariable(Set scopes, String variableName) @@ -79,18 +76,19 @@ public void saveContextElementTextToVariable(Set scopes, String v } /** - * Saves text of an element into a variable. + * Finds the element by the given locator and saves its text into a variable. * - * @param locator The locator to find an element - * @param scopes The set (comma separated list of scopes e.g.: STORY, NEXT_BATCHES) of variable's scope
- * Available scopes: - *
    - *
  • STEP - the variable will be available only within the step, - *
  • SCENARIO - the variable will be available only within the scenario, - *
  • STORY - the variable will be available within the whole story, - *
  • NEXT_BATCHES - the variable will be available starting from next batch - *
- * @param variableName the variable name to store the text. + * @param locator The locator used to find the element whose text content will be saved. + * @param scopes The set (comma separated list of scopes e.g.: STORY, NEXT_BATCHES) of the variable scopes. + *
+ * Available scopes: + *
    + *
  • STEP - the variable will be available only within the step, + *
  • SCENARIO - the variable will be available only within the scenario, + *
  • STORY - the variable will be available within the whole story, + *
  • NEXT_BATCHES - the variable will be available starting from next batch + *
+ * @param variableName The name of the variable to save the text content. */ @When("I save text of element located by `$locator` to $scopes variable `$variableName`") public void saveTextOfElement(Locator locator, Set scopes, String variableName) diff --git a/vividus-plugin-web-app-playwright/src/main/java/org/vividus/ui/web/playwright/UiContext.java b/vividus-plugin-web-app-playwright/src/main/java/org/vividus/ui/web/playwright/UiContext.java index 4cf7c1fe9a..c4254f0c2f 100644 --- a/vividus-plugin-web-app-playwright/src/main/java/org/vividus/ui/web/playwright/UiContext.java +++ b/vividus-plugin-web-app-playwright/src/main/java/org/vividus/ui/web/playwright/UiContext.java @@ -17,6 +17,7 @@ package org.vividus.ui.web.playwright; import java.util.Optional; +import java.util.function.Function; import com.microsoft.playwright.Locator; import com.microsoft.playwright.Page; @@ -55,11 +56,21 @@ public void reset() public Locator locateElement(PlaywrightLocator playwrightLocator) { - PlaywrightContext playwrightContext = getPlaywrightContext(); String locator = playwrightLocator.getLocator(); + return getInCurrentContext(context -> context.locator(locator), page -> page.locator(locator)); + } + + public Locator getCurrentContexOrPageRoot() + { + return getInCurrentContext(context -> context, page -> page.locator("//html/body")); + } + + private R getInCurrentContext(Function elementContextAction, Function pageContextAction) + { + PlaywrightContext playwrightContext = getPlaywrightContext(); return Optional.ofNullable(playwrightContext.context) - .map(context -> context.locator(locator)) - .orElseGet(() -> playwrightContext.page.locator(locator)); + .map(elementContextAction) + .orElseGet(() -> pageContextAction.apply(playwrightContext.page)); } private PlaywrightContext getPlaywrightContext() diff --git a/vividus-plugin-web-app-playwright/src/main/java/org/vividus/ui/web/playwright/assertions/PlaywrightSoftAssert.java b/vividus-plugin-web-app-playwright/src/main/java/org/vividus/ui/web/playwright/assertions/PlaywrightSoftAssert.java new file mode 100644 index 0000000000..0874762f6a --- /dev/null +++ b/vividus-plugin-web-app-playwright/src/main/java/org/vividus/ui/web/playwright/assertions/PlaywrightSoftAssert.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019-2023 the original author or authors. + * + * 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 + * + * https://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.vividus.ui.web.playwright.assertions; + +import org.opentest4j.AssertionFailedError; +import org.vividus.softassert.ISoftAssert; + +public class PlaywrightSoftAssert +{ + private final ISoftAssert softAssert; + + public PlaywrightSoftAssert(ISoftAssert softAssert) + { + this.softAssert = softAssert; + } + + public void runAssertion(String messageOnFailure, Runnable assertion) + { + try + { + assertion.run(); + } + catch (AssertionFailedError e) + { + softAssert.recordFailedAssertion(messageOnFailure + ". " + e.getMessage(), e); + } + } +} diff --git a/vividus-plugin-web-app-playwright/src/main/java/org/vividus/ui/web/playwright/steps/SetContextSteps.java b/vividus-plugin-web-app-playwright/src/main/java/org/vividus/ui/web/playwright/steps/SetContextSteps.java index 7502105b5b..7ee45d2f6c 100644 --- a/vividus-plugin-web-app-playwright/src/main/java/org/vividus/ui/web/playwright/steps/SetContextSteps.java +++ b/vividus-plugin-web-app-playwright/src/main/java/org/vividus/ui/web/playwright/steps/SetContextSteps.java @@ -20,11 +20,10 @@ import com.microsoft.playwright.assertions.PlaywrightAssertions; import org.jbehave.core.annotations.When; -import org.opentest4j.AssertionFailedError; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.vividus.softassert.ISoftAssert; import org.vividus.ui.web.playwright.UiContext; +import org.vividus.ui.web.playwright.assertions.PlaywrightSoftAssert; import org.vividus.ui.web.playwright.locator.PlaywrightLocator; public class SetContextSteps @@ -32,12 +31,12 @@ public class SetContextSteps private static final Logger LOGGER = LoggerFactory.getLogger(SetContextSteps.class); private final UiContext uiContext; - private final ISoftAssert softAssert; + private final PlaywrightSoftAssert playwrightSoftAssert; - public SetContextSteps(UiContext uiContext, ISoftAssert softAssert) + public SetContextSteps(UiContext uiContext, PlaywrightSoftAssert playwrightSoftAssert) { this.uiContext = uiContext; - this.softAssert = softAssert; + this.playwrightSoftAssert = playwrightSoftAssert; } /** @@ -70,15 +69,10 @@ public void changeContext(PlaywrightLocator locator) public void changeContextInScopeOfCurrentContext(PlaywrightLocator locator) { Locator context = uiContext.locateElement(locator); - try - { + playwrightSoftAssert.runAssertion("The element to set context is not found", () -> { PlaywrightAssertions.assertThat(context).hasCount(1); uiContext.setContext(context); LOGGER.info("The context is successfully changed"); - } - catch (AssertionFailedError e) - { - softAssert.recordFailedAssertion("The element to set context is not found. " + e.getMessage(), e); - } + }); } } diff --git a/vividus-plugin-web-app-playwright/src/main/java/org/vividus/ui/web/playwright/steps/TextContentSteps.java b/vividus-plugin-web-app-playwright/src/main/java/org/vividus/ui/web/playwright/steps/TextContentSteps.java new file mode 100644 index 0000000000..8ea5b89a17 --- /dev/null +++ b/vividus-plugin-web-app-playwright/src/main/java/org/vividus/ui/web/playwright/steps/TextContentSteps.java @@ -0,0 +1,131 @@ +/* + * Copyright 2019-2023 the original author or authors. + * + * 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 + * + * https://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.vividus.ui.web.playwright.steps; + +import java.util.Set; +import java.util.regex.Pattern; + +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.assertions.PlaywrightAssertions; + +import org.jbehave.core.annotations.Then; +import org.jbehave.core.annotations.When; +import org.vividus.context.VariableContext; +import org.vividus.ui.web.playwright.UiContext; +import org.vividus.ui.web.playwright.assertions.PlaywrightSoftAssert; +import org.vividus.ui.web.playwright.locator.PlaywrightLocator; +import org.vividus.variable.VariableScope; + +public class TextContentSteps +{ + private final UiContext uiContext; + private final VariableContext variableContext; + private final PlaywrightSoftAssert playwrightSoftAssert; + + public TextContentSteps(UiContext uiContext, VariableContext variableContext, + PlaywrightSoftAssert playwrightSoftAssert) + { + this.uiContext = uiContext; + this.variableContext = variableContext; + this.playwrightSoftAssert = playwrightSoftAssert; + } + + /** + * Saves the text of the context into a variable. + * + * @param scopes The set (comma separated list of scopes e.g.: STORY, NEXT_BATCHES) of the variable scopes. + *
+ * Available scopes: + *
    + *
  • STEP - the variable will be available only within the step, + *
  • SCENARIO - the variable will be available only within the scenario, + *
  • STORY - the variable will be available within the whole story, + *
  • NEXT_BATCHES - the variable will be available starting from next batch + *
+ * @param variableName The name of the variable to save the text content. + */ + @When("I save text of context to $scopes variable `$variableName`") + public void saveTextOfContext(Set scopes, String variableName) + { + saveTextOfElement(uiContext.getCurrentContexOrPageRoot(), scopes, variableName); + } + + /** + * Finds the element by the given locator and saves its text into a variable. + * + * @param locator The locator used to find the element whose text content will be saved. + * @param scopes The set (comma separated list of scopes e.g.: STORY, NEXT_BATCHES) of the variable scopes. + *
+ * Available scopes: + *
    + *
  • STEP - the variable will be available only within the step, + *
  • SCENARIO - the variable will be available only within the scenario, + *
  • STORY - the variable will be available within the whole story, + *
  • NEXT_BATCHES - the variable will be available starting from next batch + *
+ * @param variableName The name of the variable to save the text content. + */ + @When("I save text of element located by `$locator` to $scopes variable `$variableName`") + public void saveTextOfElement(PlaywrightLocator locator, Set scopes, String variableName) + { + saveTextOfElement(uiContext.locateElement(locator), scopes, variableName); + } + + private void saveTextOfElement(Locator element, Set scopes, String variableName) + { + variableContext.putVariable(scopes, variableName, element.textContent()); + } + + /** + * Validates the text from current context matches the specified regular expression. + * + * @param regex The regular expression used to validate the context text. + */ + @Then("text matches `$regex`") + public void assertTextMatchesRegex(Pattern regex) + { + playwrightSoftAssert.runAssertion("The text matching regular expression is not found in the context", + () -> PlaywrightAssertions.assertThat(uiContext.getCurrentContexOrPageRoot()).containsText(regex) + ); + } + + /** + * Validates the text is present in the current context. The expected text is case-sensitive. + * + * @param text The expected text to be found in the context text. + */ + @Then("text `$text` exists") + public void assertTextExists(String text) + { + playwrightSoftAssert.runAssertion("The expected text is not found in the context", + () -> PlaywrightAssertions.assertThat(uiContext.getCurrentContexOrPageRoot()).containsText(text) + ); + } + + /** + * Validates the text is not present in the current context. + * + * @param text The text that should not be present in the context. + */ + @Then("text `$text` does not exist") + public void assertTextDoesNotExist(String text) + { + playwrightSoftAssert.runAssertion("The unexpected text is found in the context", + () -> PlaywrightAssertions.assertThat(uiContext.getCurrentContexOrPageRoot()).not().containsText(text) + ); + } +} diff --git a/vividus-plugin-web-app-playwright/src/main/resources/properties/profile/web_pw/profile.properties b/vividus-plugin-web-app-playwright/src/main/resources/properties/profile/web_pw/profile.properties index e23ae1b7e6..83c3649e50 100644 --- a/vividus-plugin-web-app-playwright/src/main/resources/properties/profile/web_pw/profile.properties +++ b/vividus-plugin-web-app-playwright/src/main/resources/properties/profile/web_pw/profile.properties @@ -8,3 +8,5 @@ web-application.authentication-mode=URL playwright.tracing.output-directory=${output.report-directory}/playwright-traces playwright.tracing.screenshots-enabled=false playwright.tracing.snapshots-enabled=false + +internal.engine.composite-paths=${engine.composite-paths},steps/defaults/*.steps,steps/mappings/*.steps diff --git a/vividus-plugin-web-app-playwright/src/main/resources/steps/mappings/mapping-to-common-ui.steps b/vividus-plugin-web-app-playwright/src/main/resources/steps/mappings/mapping-to-common-ui.steps new file mode 100644 index 0000000000..eeb45cb699 --- /dev/null +++ b/vividus-plugin-web-app-playwright/src/main/resources/steps/mappings/mapping-to-common-ui.steps @@ -0,0 +1,2 @@ +Composite: When I save text of context element to $scopes variable `$variableName` +When I save text of context to variable `` diff --git a/vividus-plugin-web-app-playwright/src/main/resources/vividus-plugin/spring.xml b/vividus-plugin-web-app-playwright/src/main/resources/vividus-plugin/spring.xml index d0825abf88..ebfbb82d8c 100644 --- a/vividus-plugin-web-app-playwright/src/main/resources/vividus-plugin/spring.xml +++ b/vividus-plugin-web-app-playwright/src/main/resources/vividus-plugin/spring.xml @@ -26,11 +26,14 @@ + + + @@ -38,6 +41,7 @@ + diff --git a/vividus-plugin-web-app-playwright/src/test/java/org/vividus/ui/web/playwright/UiContextTests.java b/vividus-plugin-web-app-playwright/src/test/java/org/vividus/ui/web/playwright/UiContextTests.java index ade625a1be..7d6fc712ed 100644 --- a/vividus-plugin-web-app-playwright/src/test/java/org/vividus/ui/web/playwright/UiContextTests.java +++ b/vividus-plugin-web-app-playwright/src/test/java/org/vividus/ui/web/playwright/UiContextTests.java @@ -95,4 +95,27 @@ void shouldLocateElementOnPageAfterContextReset() assertSame(locator, actual); verifyNoInteractions(context); } + + @Test + void shouldReturnCurrentContext() + { + Page page = mock(); + uiContext.setCurrentPage(page); + Locator context = mock(); + uiContext.setContext(context); + Locator actual = uiContext.getCurrentContexOrPageRoot(); + assertSame(context, actual); + verifyNoInteractions(page); + } + + @Test + void shouldReturnPageRootWhenNoContextIsSet() + { + Page page = mock(); + uiContext.setCurrentPage(page); + Locator root = mock(); + when(page.locator("//html/body")).thenReturn(root); + Locator actual = uiContext.getCurrentContexOrPageRoot(); + assertSame(root, actual); + } } diff --git a/vividus-plugin-web-app-playwright/src/test/java/org/vividus/ui/web/playwright/assertions/PlaywrightSoftAssertTests.java b/vividus-plugin-web-app-playwright/src/test/java/org/vividus/ui/web/playwright/assertions/PlaywrightSoftAssertTests.java new file mode 100644 index 0000000000..0540f0c3c6 --- /dev/null +++ b/vividus-plugin-web-app-playwright/src/test/java/org/vividus/ui/web/playwright/assertions/PlaywrightSoftAssertTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2019-2023 the original author or authors. + * + * 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 + * + * https://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.vividus.ui.web.playwright.assertions; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opentest4j.AssertionFailedError; +import org.vividus.softassert.ISoftAssert; + +@ExtendWith(MockitoExtension.class) +class PlaywrightSoftAssertTests +{ + @Mock private ISoftAssert softAssert; + @InjectMocks private PlaywrightSoftAssert playwrightSoftAssert; + + @Test + void shouldRecordSoftAssertionErrorOnFailedAssertion() + { + var messageOnFailure = "No text is found"; + var exceptionMessage = "Locator expected to contain text: cat\nReceived: dog\n"; + var assertionFailedError = new AssertionFailedError(exceptionMessage); + playwrightSoftAssert.runAssertion(messageOnFailure, () -> { + throw assertionFailedError; + }); + verify(softAssert).recordFailedAssertion(messageOnFailure + ". " + exceptionMessage, assertionFailedError); + } + + @Test + void shouldDoNothingOnSuccessfulAssertion() + { + playwrightSoftAssert.runAssertion("any", () -> { }); + verifyNoInteractions(softAssert); + } +} diff --git a/vividus-plugin-web-app-playwright/src/test/java/org/vividus/ui/web/playwright/steps/SetContextStepsTests.java b/vividus-plugin-web-app-playwright/src/test/java/org/vividus/ui/web/playwright/steps/SetContextStepsTests.java index d41b4027f8..e668973d31 100644 --- a/vividus-plugin-web-app-playwright/src/test/java/org/vividus/ui/web/playwright/steps/SetContextStepsTests.java +++ b/vividus-plugin-web-app-playwright/src/test/java/org/vividus/ui/web/playwright/steps/SetContextStepsTests.java @@ -18,9 +18,10 @@ import static com.github.valfirst.slf4jtest.LoggingEvent.info; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.is; -import static org.mockito.Mockito.doThrow; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; @@ -37,90 +38,45 @@ import com.microsoft.playwright.assertions.LocatorAssertions; import com.microsoft.playwright.assertions.PlaywrightAssertions; -import org.apache.commons.lang3.function.TriConsumer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InOrder; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.opentest4j.AssertionFailedError; -import org.vividus.softassert.ISoftAssert; import org.vividus.ui.web.playwright.UiContext; +import org.vividus.ui.web.playwright.assertions.PlaywrightSoftAssert; import org.vividus.ui.web.playwright.locator.PlaywrightLocator; @ExtendWith({ MockitoExtension.class, TestLoggerFactoryExtension.class }) class SetContextStepsTests { @Mock private UiContext uiContext; - @Mock private ISoftAssert softAssert; - @InjectMocks private SetContextSteps setContextSteps; + @Mock private PlaywrightSoftAssert playwrightSoftAssert; + @InjectMocks private SetContextSteps steps; private final TestLogger logger = TestLoggerFactory.getTestLogger(SetContextSteps.class); @Test void shouldResetContext() { - setContextSteps.resetContext(); + steps.resetContext(); verify(uiContext).reset(); } @Test - void shouldChangeContextSuccessfully() + void shouldChangeContext() { - testSuccessfulContextChange(setContextSteps::changeContext, ordered -> ordered.verify(uiContext).reset()); + testSuccessfulContextChange(steps::changeContext, ordered -> ordered.verify(uiContext).reset()); } @Test - void shouldFailToChangeContext() + void shouldChangeContextInScopeOfCurrentContext() { - testFailureToContextChange(setContextSteps::changeContext, ordered -> ordered.verify(uiContext).reset()); - } - - @Test - void shouldChangeContextInScopeOfCurrentContextSuccessfully() - { - testSuccessfulContextChange(setContextSteps::changeContextInScopeOfCurrentContext, ordered -> { }); - } - - @Test - void shouldFailToChangeContextInScopeOfCurrentContext() - { - testFailureToContextChange(setContextSteps::changeContextInScopeOfCurrentContext, ordered -> { }); + testSuccessfulContextChange(steps::changeContextInScopeOfCurrentContext, ordered -> { }); } private void testSuccessfulContextChange(Consumer test, Consumer orderedVerification) - { - testContextChange((locator, context, locatorAssertions) -> - { - test.accept(locator); - var ordered = inOrder(uiContext, locatorAssertions); - orderedVerification.accept(ordered); - ordered.verify(locatorAssertions).hasCount(1); - ordered.verify(uiContext).setContext(context); - ordered.verifyNoMoreInteractions(); - assertThat(logger.getLoggingEvents(), is(List.of(info("The context is successfully changed")))); - }); - } - - private void testFailureToContextChange(Consumer test, Consumer orderedVerification) - { - testContextChange((locator, context, locatorAssertions) -> - { - var error = new AssertionFailedError("Locator expected to have count: 1\nReceived: 8"); - doThrow(error).when(locatorAssertions).hasCount(1); - test.accept(locator); - var ordered = inOrder(uiContext, locatorAssertions, softAssert); - orderedVerification.accept(ordered); - ordered.verify(locatorAssertions).hasCount(1); - ordered.verify(softAssert).recordFailedAssertion( - "The element to set context is not found. " + error.getMessage(), error); - ordered.verifyNoMoreInteractions(); - assertThat(logger.getLoggingEvents(), is(empty())); - }); - } - - private void testContextChange(TriConsumer test) { var locator = new PlaywrightLocator("xpath", "//a"); Locator context = mock(); @@ -130,7 +86,18 @@ private void testContextChange(TriConsumer PlaywrightAssertions.assertThat(context)).thenReturn( locatorAssertions); - test.accept(locator, context, locatorAssertions); + doNothing().when(playwrightSoftAssert).runAssertion(eq("The element to set context is not found"), + argThat(runnable -> { + runnable.run(); + return true; + })); + test.accept(locator); + var ordered = inOrder(uiContext, locatorAssertions); + orderedVerification.accept(ordered); + ordered.verify(locatorAssertions).hasCount(1); + ordered.verify(uiContext).setContext(context); + ordered.verifyNoMoreInteractions(); + assertThat(logger.getLoggingEvents(), is(List.of(info("The context is successfully changed")))); } } } diff --git a/vividus-plugin-web-app-playwright/src/test/java/org/vividus/ui/web/playwright/steps/TextContentStepsTests.java b/vividus-plugin-web-app-playwright/src/test/java/org/vividus/ui/web/playwright/steps/TextContentStepsTests.java new file mode 100644 index 0000000000..c60cf350a4 --- /dev/null +++ b/vividus-plugin-web-app-playwright/src/test/java/org/vividus/ui/web/playwright/steps/TextContentStepsTests.java @@ -0,0 +1,135 @@ +/* + * Copyright 2019-2023 the original author or authors. + * + * 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 + * + * https://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.vividus.ui.web.playwright.steps; + +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.RETURNS_SELF; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.regex.Pattern; + +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.assertions.LocatorAssertions; +import com.microsoft.playwright.assertions.PlaywrightAssertions; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.vividus.context.VariableContext; +import org.vividus.ui.web.playwright.UiContext; +import org.vividus.ui.web.playwright.assertions.PlaywrightSoftAssert; +import org.vividus.ui.web.playwright.locator.PlaywrightLocator; +import org.vividus.variable.VariableScope; + +@ExtendWith(MockitoExtension.class) +class TextContentStepsTests +{ + @Mock private UiContext uiContext; + @Mock private VariableContext variableContext; + @Mock private PlaywrightSoftAssert playwrightSoftAssert; + @InjectMocks private TextContentSteps steps; + + @Test + void shouldSaveTextOfContext() + { + Locator context = mock(); + when(uiContext.getCurrentContexOrPageRoot()).thenReturn(context); + var text = "context"; + when(context.textContent()).thenReturn(text); + var scopes = Set.of(VariableScope.STORY); + var variableName = "textOfContext"; + steps.saveTextOfContext(scopes, variableName); + verify(variableContext).putVariable(scopes, variableName, text); + } + + @Test + void shouldSaveTextOfElement() + { + Locator element = mock(); + PlaywrightLocator playwrightLocator = mock(); + when(uiContext.locateElement(playwrightLocator)).thenReturn(element); + var text = "element"; + when(element.textContent()).thenReturn(text); + var scopes = Set.of(VariableScope.STORY); + var variableName = "textOfElement"; + steps.saveTextOfElement(playwrightLocator, scopes, variableName); + verify(variableContext).putVariable(scopes, variableName, text); + } + + @Test + void shouldAssertTextMatchesRegex() + { + shouldAssertText("The text matching regular expression is not found in the context", + (steps, locatorAssertions) -> { + var regex = Pattern.compile(".*"); + steps.assertTextMatchesRegex(regex); + verify(locatorAssertions).containsText(regex); + }); + } + + @Test + void shouldAssertTextExists() + { + shouldAssertText("The expected text is not found in the context", + (steps, locatorAssertions) -> { + var text = "text"; + steps.assertTextExists(text); + verify(locatorAssertions).containsText(text); + }); + } + + @Test + void shouldAssertTextDoesNotExist() + { + shouldAssertText("The unexpected text is found in the context", + (steps, locatorAssertions) -> { + var unexpectedText = "error"; + steps.assertTextDoesNotExist(unexpectedText); + var ordered = inOrder(locatorAssertions); + ordered.verify(locatorAssertions).not(); + ordered.verify(locatorAssertions).containsText(unexpectedText); + ordered.verifyNoMoreInteractions(); + }); + } + + private void shouldAssertText(String messageOnFailure, BiConsumer test) + { + Locator context = mock(); + when(uiContext.getCurrentContexOrPageRoot()).thenReturn(context); + try (var playwrightAssertionsStaticMock = mockStatic(PlaywrightAssertions.class)) + { + var locatorAssertions = mock(LocatorAssertions.class, RETURNS_SELF); + playwrightAssertionsStaticMock.when(() -> PlaywrightAssertions.assertThat(context)).thenReturn( + locatorAssertions); + doNothing().when(playwrightSoftAssert).runAssertion(eq(messageOnFailure), argThat(runnable -> { + runnable.run(); + return true; + })); + test.accept(steps, locatorAssertions); + } + } +} diff --git a/vividus-plugin-web-app/src/main/java/org/vividus/steps/ui/web/TextValidationSteps.java b/vividus-plugin-web-app/src/main/java/org/vividus/steps/ui/web/TextValidationSteps.java index ec6657a3ae..43221c168d 100644 --- a/vividus-plugin-web-app/src/main/java/org/vividus/steps/ui/web/TextValidationSteps.java +++ b/vividus-plugin-web-app/src/main/java/org/vividus/steps/ui/web/TextValidationSteps.java @@ -59,11 +59,12 @@ public TextValidationSteps(IUiContext uiContext, ISearchActions searchActions, I } /** - * Checks if the text in context matches regex - * @param regex Expected regular expression + * Validates the text from current context matches the specified regular expression. + * + * @param regex The regular expression used to validate the context text. */ @Then("text matches `$regex`") - public void ifTextMatchesRegex(Pattern regex) + public void assertTextMatchesRegex(Pattern regex) { uiContext.getOptionalSearchContext().ifPresent(searchContext -> { @@ -93,11 +94,12 @@ private boolean verifyText(Pattern regex, String actualText, SearchContext searc } /** - * Checks if the text exists in context - * @param text Expected text + * Validates the text is present in the current context. The expected text is case-sensitive. + * + * @param text The expected text to be found in the context text. */ @Then("text `$text` exists") - public void ifTextExists(String text) + public void assertTextExists(String text) { uiContext.getOptionalSearchContext().ifPresent(searchContext -> { @@ -148,8 +150,9 @@ private List findElements(SearchContext searchContext, By locator) } /** - * Checks if the text does not exist in context - * @param text Text value + * Validates the text from the context does not contain the given value. + * + * @param text The text to be not found in the context text. * @return true if text does not exist, otherwise false * @deprecated Use step: "Then text `$text` does not exist" */ @@ -170,8 +173,9 @@ public boolean textDoesNotExist(String text) } /** - * Checks if the text does not exist in context - * @param text Text value + * Validates the text is not present in the current context. + * + * @param text The text that should not be present in the context. */ @Then("text `$text` does not exist") public void assertTextDoesNotExist(String text) diff --git a/vividus-plugin-web-app/src/test/java/org/vividus/steps/ui/web/TextValidationStepsTests.java b/vividus-plugin-web-app/src/test/java/org/vividus/steps/ui/web/TextValidationStepsTests.java index 0b653f71f7..dd01e65547 100644 --- a/vividus-plugin-web-app/src/test/java/org/vividus/steps/ui/web/TextValidationStepsTests.java +++ b/vividus-plugin-web-app/src/test/java/org/vividus/steps/ui/web/TextValidationStepsTests.java @@ -91,7 +91,7 @@ public List answer(InvocationOnMock invocation) return List.of(webElement); } }); - textValidationSteps.ifTextExists(TEXT); + textValidationSteps.assertTextExists(TEXT); verify(softAssert).assertTrue(THERE_IS_AN_ELEMENT_WITH_TEXT_TEXT_IN_THE_CONTEXT, true); } @@ -101,7 +101,7 @@ void testCheckPageContainsText() when(uiContext.getOptionalSearchContext()).thenReturn(Optional.of(webDriver)); when(webDriver.findElements(WebXpathLocatorUtils.getXPathLocatorByInnerText(TEXT))) .thenReturn(List.of(webElement)); - textValidationSteps.ifTextExists(TEXT); + textValidationSteps.assertTextExists(TEXT); verify(softAssert).assertTrue(THERE_IS_AN_ELEMENT_WITH_TEXT_TEXT_IN_THE_CONTEXT, true); } @@ -109,7 +109,7 @@ void testCheckPageContainsText() void testDomElementsContainText() { when(uiContext.getOptionalSearchContext()).thenReturn(Optional.of(webElement)); - textValidationSteps.ifTextExists(TEXT); + textValidationSteps.assertTextExists(TEXT); verify(elementValidations).assertIfElementContainsText(webElement, TEXT, true); } @@ -118,7 +118,7 @@ void testCheckPageContainsTextInPseudoElements() { when(uiContext.getOptionalSearchContext()).thenReturn(Optional.of(webDriver)); when(webElementActions.getAllPseudoElementsContent()).thenReturn(List.of(TEXT)); - textValidationSteps.ifTextExists(TEXT); + textValidationSteps.assertTextExists(TEXT); verify(softAssert).assertTrue(THERE_IS_AN_ELEMENT_WITH_TEXT_TEXT_IN_THE_CONTEXT, true); } @@ -128,7 +128,7 @@ void testIfTextExists() when(uiContext.getOptionalSearchContext()).thenReturn(Optional.of(webDriver)); when(webElementActions.getAllPseudoElementsContent()).thenReturn(List.of()); when(webElementActions.getPageText()).thenReturn("no"); - textValidationSteps.ifTextExists(TEXT); + textValidationSteps.assertTextExists(TEXT); verify(softAssert).assertTrue(THERE_IS_AN_ELEMENT_WITH_TEXT_TEXT_IN_THE_CONTEXT, false); } @@ -138,7 +138,7 @@ void testIfTextExistsOnPage() when(uiContext.getOptionalSearchContext()).thenReturn(Optional.of(webDriver)); when(webElementActions.getAllPseudoElementsContent()).thenReturn(List.of()); when(webElementActions.getPageText()).thenReturn(TEXT); - textValidationSteps.ifTextExists(TEXT); + textValidationSteps.assertTextExists(TEXT); verify(softAssert).assertTrue(THERE_IS_AN_ELEMENT_WITH_TEXT_TEXT_IN_THE_CONTEXT, true); } @@ -187,7 +187,7 @@ void testIfTextMatchesRegexWebElementContext(String elementText, boolean actual) { when(uiContext.getOptionalSearchContext()).thenReturn(Optional.of(webElement)); when(webElementActions.getElementText(webElement)).thenReturn(elementText); - textValidationSteps.ifTextMatchesRegex(REGEX); + textValidationSteps.assertTextMatchesRegex(REGEX); verify(softAssert).assertTrue(TEXT_MATCHES_REGEX_MESSAGE + REGEX, actual); } @@ -200,7 +200,7 @@ void testIfTextMatchesRegexWebDriverContext(String pageText, boolean actual) { when(uiContext.getOptionalSearchContext()).thenReturn(Optional.of(webDriver)); when(webElementActions.getPageText()).thenReturn(pageText); - textValidationSteps.ifTextMatchesRegex(REGEX); + textValidationSteps.assertTextMatchesRegex(REGEX); verify(softAssert).assertTrue(TEXT_MATCHES_REGEX_MESSAGE + REGEX, actual); } @@ -215,7 +215,7 @@ void testIfTextDoesntMatchRegexWebElementContext(String pseudoElementContent, bo when(uiContext.getOptionalSearchContext()).thenReturn(Optional.of(webElement)); when(webElementActions.getElementText(webElement)).thenReturn(ELEMENT_TEXT); when(webElementActions.getPseudoElementContent(webElement)).thenReturn(pseudoElementContent); - textValidationSteps.ifTextMatchesRegex(REGEX); + textValidationSteps.assertTextMatchesRegex(REGEX); verify(softAssert).assertTrue(TEXT_MATCHES_REGEX_MESSAGE + REGEX, actual); } @@ -226,7 +226,7 @@ void testIfTextExistsFirefox() when(uiContext.getOptionalSearchContext()).thenReturn(Optional.of(webDriver)); Mockito.lenient().when(webDriver.findElements(locator)).thenReturn(List.of()); when(searchActions.findElements(any(Locator.class))).thenReturn(List.of(webElement)); - textValidationSteps.ifTextExists(TEXT.toUpperCase()); + textValidationSteps.assertTextExists(TEXT.toUpperCase()); verify(softAssert).assertTrue("There is an element with text=TEXT in the context", true); } } diff --git a/vividus/src/main/resources/properties/defaults/default.properties b/vividus/src/main/resources/properties/defaults/default.properties index 5c9b1b829e..bc5b3b5465 100644 --- a/vividus/src/main/resources/properties/defaults/default.properties +++ b/vividus/src/main/resources/properties/defaults/default.properties @@ -13,6 +13,7 @@ story.fail-fast=false scenario.fail-fast=false engine.composite-paths= +internal.engine.composite-paths=${engine.composite-paths},steps/defaults/*.steps engine.alias-paths= bdd.configuration.formats= bdd.configuration.dry-run=false