diff --git a/frontend/tips/7-use-a-page-object/_selenide.mdx b/frontend/tips/7-use-a-page-object/_selenide.mdx new file mode 100644 index 0000000000..b721632211 --- /dev/null +++ b/frontend/tips/7-use-a-page-object/_selenide.mdx @@ -0,0 +1,92 @@ +import TipCode from "@site/src/components/tipCode"; + +## TL;DR - Show Me The Code + + + +## Code Walkthrough + +### Importing Libraries + +First let's import our requisite classes: + * for annotations (e.g., `org.junit.jupiter.api.Test`), + * opening the browser with Selenide (e.g., `com.codeborne.selenide.Selenide.open`), + +Next, we'll start our test. + +### Example 1: pageObject + +Our first example `pageObject` shows how we can open an url and create a page object instance for it. + +Instead of a usual Selenide method `open(url)`, you can use method `open(url, Class)` that returns a newly created page object instance: +> var page = open("https://the-internet.herokuapp.com/dynamic_loading/2", DynamicLoadingPage.class); + +Note that `page` is fully initialized object. You don't need to call any "page factories", initializers etc. - Selenide's already done it for you. + +Now you can use this `page` as any other Java object - call its methods (or even pass it as parameter to other methods): +> page.start(); + +and +> page.waitForCompletion("Hello World!"); + +Take a look how this page object is implemented - it's pretty simple: + +
+ Toggle to see the DynamicLoadingPage.java +
+ +
+
+ + +Web elements can be declared as fields in page object class. + +NB! Please make the fields private. In OOP (Object-oriented programming), +* objects expose their behaviour via methods, but +* objects should NOT expose their state via public fields. + +### Example 2: alternativePageObject + +This example just shows a bit different page object that has fields of type `SelenideElement` instead of `By`. +It's a matter of taste which variant you like more. + +
+ Toggle to see the AlternativeDynamicLoadingPage.java +
+ +
+
+ +### Example 3: anotherPageObject + +This example just shows a bit different page object that has no fields at all. :) + +Yes, it's also possible. And it's often even better because the code is shorter and simpler. + +
+ Toggle to see the AnotherDynamicLoadingPage.java +
+ +
+
+ +### Executing the Test + +Before executing the test, we need to make sure the required dependencies are declared on the `pom.xml` file. + +
+ Toggle to see the pom.xml file. +
+ +
+
+ +Finally, we can run the test by executing `mvn test` from the command-line. \ No newline at end of file diff --git a/frontend/tips/7-use-a-page-object/code/selenide/pom.xml b/frontend/tips/7-use-a-page-object/code/selenide/pom.xml new file mode 100644 index 0000000000..36c5a3a2b5 --- /dev/null +++ b/frontend/tips/7-use-a-page-object/code/selenide/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + com.elemental.selenium + tips-selenide-page-object + 1.0.0 + + + UTF-8 + 17 + 17 + + + + + com.codeborne + selenide + 7.2.3 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.10.2 + test + + + org.assertj + assertj-core + 3.25.3 + test + + + org.slf4j + slf4j-simple + 2.0.12 + test + + + diff --git a/frontend/tips/7-use-a-page-object/code/selenide/src/test/java/com/elemental/selenium/AlternativeDynamicLoadingPage.java b/frontend/tips/7-use-a-page-object/code/selenide/src/test/java/com/elemental/selenium/AlternativeDynamicLoadingPage.java new file mode 100644 index 0000000000..d968d7a3e5 --- /dev/null +++ b/frontend/tips/7-use-a-page-object/code/selenide/src/test/java/com/elemental/selenium/AlternativeDynamicLoadingPage.java @@ -0,0 +1,23 @@ +package com.elemental.selenium; + +import com.codeborne.selenide.SelenideElement; + +import java.time.Duration; + +import static com.codeborne.selenide.Condition.text; +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; + +public class AlternativeDynamicLoadingPage { + private final SelenideElement startButton= $("#start button"); + private final SelenideElement finishButton= $("#finish"); + + public void start() { + startButton.click(); + } + + public void waitForCompletion(String expectedText) { + finishButton.shouldBe(visible, Duration.ofSeconds(10)); + finishButton.shouldHave(text(expectedText)); + } +} diff --git a/frontend/tips/7-use-a-page-object/code/selenide/src/test/java/com/elemental/selenium/AnotherDynamicLoadingPage.java b/frontend/tips/7-use-a-page-object/code/selenide/src/test/java/com/elemental/selenium/AnotherDynamicLoadingPage.java new file mode 100644 index 0000000000..ad88bd067f --- /dev/null +++ b/frontend/tips/7-use-a-page-object/code/selenide/src/test/java/com/elemental/selenium/AnotherDynamicLoadingPage.java @@ -0,0 +1,26 @@ +package com.elemental.selenium; + +import java.time.Duration; + +import static com.codeborne.selenide.Condition.text; +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; + +/** + * Page objects don't necessarily have to contain fields. + * It's totally ok to only have methods. + * + * Remember, object exposes its behavior via METHODS, not fields! + */ +public class AnotherDynamicLoadingPage { + + public void start() { + $("#start button").click(); + } + + public void waitForCompletion(String expectedText) { + $("#finish") + .shouldBe(visible, Duration.ofSeconds(10)) + .shouldHave(text(expectedText)); + } +} diff --git a/frontend/tips/7-use-a-page-object/code/selenide/src/test/java/com/elemental/selenium/DynamicLoadingPage.java b/frontend/tips/7-use-a-page-object/code/selenide/src/test/java/com/elemental/selenium/DynamicLoadingPage.java new file mode 100644 index 0000000000..810508b19f --- /dev/null +++ b/frontend/tips/7-use-a-page-object/code/selenide/src/test/java/com/elemental/selenium/DynamicLoadingPage.java @@ -0,0 +1,23 @@ +package com.elemental.selenium; + +import org.openqa.selenium.By; + +import java.time.Duration; + +import static com.codeborne.selenide.Condition.text; +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; + +public class DynamicLoadingPage { + private final By startButton = By.cssSelector("#start button"); + private final By finishButton = By.cssSelector("#finish"); + + public void start() { + $(startButton).click(); + } + + public void waitForCompletion(String expectedText) { + $(finishButton).shouldBe(visible, Duration.ofSeconds(10)); + $(finishButton).shouldHave(text(expectedText)); + } +} diff --git a/frontend/tips/7-use-a-page-object/code/selenide/src/test/java/com/elemental/selenium/PageObjectTest.java b/frontend/tips/7-use-a-page-object/code/selenide/src/test/java/com/elemental/selenium/PageObjectTest.java new file mode 100644 index 0000000000..bd8f51da2e --- /dev/null +++ b/frontend/tips/7-use-a-page-object/code/selenide/src/test/java/com/elemental/selenium/PageObjectTest.java @@ -0,0 +1,28 @@ +package com.elemental.selenium; + +import org.junit.jupiter.api.Test; + +import static com.codeborne.selenide.Selenide.open; + +public class PageObjectTest { + @Test + void pageObject() { + var page = open("https://the-internet.herokuapp.com/dynamic_loading/2", DynamicLoadingPage.class); + page.start(); + page.waitForCompletion("Hello World!"); + } + + @Test + void alternativePageObject() { + var page = open("https://the-internet.herokuapp.com/dynamic_loading/2", AlternativeDynamicLoadingPage.class); + page.start(); + page.waitForCompletion("Hello World!"); + } + + @Test + void anotherPageObject() { + var page = open("https://the-internet.herokuapp.com/dynamic_loading/2", AnotherDynamicLoadingPage.class); + page.start(); + page.waitForCompletion("Hello World!"); + } +} \ No newline at end of file diff --git a/frontend/tips/7-use-a-page-object/main.mdx b/frontend/tips/7-use-a-page-object/main.mdx index 6e94ed3a49..6979663e47 100644 --- a/frontend/tips/7-use-a-page-object/main.mdx +++ b/frontend/tips/7-use-a-page-object/main.mdx @@ -41,17 +41,24 @@ import DisplayTips from '@site/src/components/displayTips'; -## About The Author +## About The Authors -Dave Haeffner is the original writer of Elemental Selenium -- a free, once weekly Selenium tip newsletter that's read -by thousands of testing professionals. He also created and maintains the-internet (an open-source web app that's -perfect for writing automated tests against). +import DefaultAvatar from "@site/src/components/defaultAvatar"; -Dave has helped numerous companies successfully implement automated acceptance testing; including The Motley Fool, -ManTech International, Sittercity, and Animoto. He is also an active member of the Selenium project and has spoken -at numerous conferences and meetups around the world about automated acceptance testing. + -![Dave Haeffner profile picture](/img/authors/dave-haeffner.jpeg#author-img 'a title') + \ No newline at end of file