From 4d45c19c9fa2d9533fe328720d4e1b17da07db63 Mon Sep 17 00:00:00 2001 From: erhanbatu Date: Mon, 13 Jul 2026 17:16:03 +0200 Subject: [PATCH 1/8] first initial --- pom.xml | 7 ++++ .../playwright/ASimplePlaywrightTest.java | 41 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/pom.xml b/pom.xml index 497ee081..093ab125 100644 --- a/pom.xml +++ b/pom.xml @@ -27,6 +27,13 @@ 5.11.3 test + + org.assertj + assertj-core + 3.27.7 + test + + \ No newline at end of file diff --git a/src/test/java/com/serenitydojo/playwright/ASimplePlaywrightTest.java b/src/test/java/com/serenitydojo/playwright/ASimplePlaywrightTest.java index c50e7ea0..4f0807bd 100644 --- a/src/test/java/com/serenitydojo/playwright/ASimplePlaywrightTest.java +++ b/src/test/java/com/serenitydojo/playwright/ASimplePlaywrightTest.java @@ -3,13 +3,54 @@ import com.microsoft.playwright.Browser; import com.microsoft.playwright.Page; import com.microsoft.playwright.Playwright; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ASimplePlaywrightTest { + Playwright playwright; + Browser browser; + Page page; + + @BeforeEach + void setup(){ + //Create an environment + playwright = Playwright.create(); + browser = playwright.chromium().launch(); + page = browser.newPage(); + } + + @AfterEach + void tearDown(){ + browser.close(); + playwright.close(); + } + @Test void shouldShowThePageTitle() { // TODO: Write your first playwright test here + + page.navigate("https://practicesoftwaretesting.com/"); + String title = page.title(); + Assertions.assertTrue(title.contains("Practice Software Testing")); + + } + + @Test + void shouldSearchByKeyword(){ + + page.navigate("https://practicesoftwaretesting.com/"); + page.locator("[placeholder=Search]").fill("Pliers"); + //this is basically sort of xpath + page.locator("button:has-text('Search')").click(); + + //this will look for the element with card + int matchingSearchResults = page.locator(".card").count(); + System.out.println(matchingSearchResults); + + Assertions.assertTrue(matchingSearchResults>0); + } } From 01182e0724802bf427138720f148e242d3123ec6 Mon Sep 17 00:00:00 2001 From: erhanbatu Date: Mon, 13 Jul 2026 17:59:01 +0200 Subject: [PATCH 2/8] Section3 lesson --- .../playwright/ASimplePlaywrightTest.java | 41 ++++++++----- .../playwright/AnAnnotatedPlaywrightTest.java | 57 +++++++++++++++++++ 2 files changed, 83 insertions(+), 15 deletions(-) create mode 100644 src/test/java/com/serenitydojo/playwright/AnAnnotatedPlaywrightTest.java diff --git a/src/test/java/com/serenitydojo/playwright/ASimplePlaywrightTest.java b/src/test/java/com/serenitydojo/playwright/ASimplePlaywrightTest.java index 4f0807bd..5fc6f5f9 100644 --- a/src/test/java/com/serenitydojo/playwright/ASimplePlaywrightTest.java +++ b/src/test/java/com/serenitydojo/playwright/ASimplePlaywrightTest.java @@ -1,29 +1,40 @@ package com.serenitydojo.playwright; -import com.microsoft.playwright.Browser; -import com.microsoft.playwright.Page; -import com.microsoft.playwright.Playwright; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import com.microsoft.playwright.*; +import com.microsoft.playwright.junit.UsePlaywright; +import org.junit.jupiter.api.*; +import java.util.Arrays; + +//This annotation allows you to do test without creating playwright environment and browser +//@UsePlaywright public class ASimplePlaywrightTest { - Playwright playwright; - Browser browser; + private static Playwright playwright; + private static Browser browser; + private static BrowserContext browserContext; Page page; - @BeforeEach - void setup(){ - //Create an environment + @BeforeAll + public static void setUpBrowser(){ playwright = Playwright.create(); - browser = playwright.chromium().launch(); + browser = playwright.chromium().launch( + new BrowserType.LaunchOptions() + .setHeadless(false) + .setArgs(Arrays.asList("--no-sandbox", "--disable-extensions","--disable-gpu")) + ); + //this allows paralel execution + browserContext = browser.newContext(); + + } + + @BeforeEach + public void setUp(){ page = browser.newPage(); } - @AfterEach - void tearDown(){ + @AfterAll + public static void tearDown(){ browser.close(); playwright.close(); } diff --git a/src/test/java/com/serenitydojo/playwright/AnAnnotatedPlaywrightTest.java b/src/test/java/com/serenitydojo/playwright/AnAnnotatedPlaywrightTest.java new file mode 100644 index 00000000..a7622d38 --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/AnAnnotatedPlaywrightTest.java @@ -0,0 +1,57 @@ +package com.serenitydojo.playwright; + +import com.microsoft.playwright.Browser; +import com.microsoft.playwright.BrowserType; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.Playwright; +import com.microsoft.playwright.junit.Options; +import com.microsoft.playwright.junit.OptionsFactory; +import com.microsoft.playwright.junit.UsePlaywright; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +@UsePlaywright(AnAnnotatedPlaywrightTest.MyOptions.class) +public class AnAnnotatedPlaywrightTest { + + public static class MyOptions implements OptionsFactory{ + + @Override + public Options getOptions() { + return new Options() + .setHeadless(false) + .setLaunchOptions( + new BrowserType.LaunchOptions() + .setArgs(Arrays.asList("--no-sandbox", "--disable-extensions","--disable-gpu")) + ); + } + } + @Test + void shouldShowThePageTitle(Page page) { + // TODO: Write your first playwright test here + + page.navigate("https://practicesoftwaretesting.com/"); + String title = page.title(); + Assertions.assertTrue(title.contains("Practice Software Testing")); + + } + + @Test + void shouldSearchByKeyword(Page page){ + + page.navigate("https://practicesoftwaretesting.com/"); + page.locator("[placeholder=Search]").fill("Pliers"); + //this is basically sort of xpath + page.locator("button:has-text('Search')").click(); + + //this will look for the element with card + int matchingSearchResults = page.locator(".card").count(); + System.out.println(matchingSearchResults); + + Assertions.assertTrue(matchingSearchResults>0); + + } +} From f9de2689a2371c519a30dafcfddbae4d33cf4f46 Mon Sep 17 00:00:00 2001 From: erhanbatu Date: Mon, 13 Jul 2026 21:40:28 +0200 Subject: [PATCH 3/8] Section4 lesson part1 --- .../playwright/AddingItemsToTheCartTest.java | 79 ++++++++ .../playwright/PlaywrightLocators.java | 175 ++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 src/test/java/com/serenitydojo/playwright/AddingItemsToTheCartTest.java create mode 100644 src/test/java/com/serenitydojo/playwright/PlaywrightLocators.java diff --git a/src/test/java/com/serenitydojo/playwright/AddingItemsToTheCartTest.java b/src/test/java/com/serenitydojo/playwright/AddingItemsToTheCartTest.java new file mode 100644 index 00000000..a6626c63 --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/AddingItemsToTheCartTest.java @@ -0,0 +1,79 @@ +package com.serenitydojo.playwright; + +import com.microsoft.playwright.*; +import com.microsoft.playwright.junit.UsePlaywright; +import com.microsoft.playwright.options.AriaRole; +import org.junit.jupiter.api.*; + +import java.util.Arrays; +import java.util.List; + + +public class AddingItemsToTheCartTest { + + protected static Playwright playwright; + protected static Browser browser; + protected static BrowserContext browserContext; + + Page page; + + @BeforeAll + static void setUpBrowser() { + playwright = Playwright.create(); + browser = playwright.chromium().launch( + new BrowserType.LaunchOptions().setHeadless(true) + .setArgs(Arrays.asList("--no-sandbox", "--disable-extensions", "--disable-gpu")) + ); + } + + @BeforeEach + void setUp() { + browserContext = browser.newContext(); + page = browserContext.newPage(); + } + + @AfterEach + void closeContext() { + browserContext.close(); + } + + @AfterAll + static void tearDown() { + browser.close(); + playwright.close(); + } + + @DisplayName("Search for pliers") + @Test + void searchForPliers(){ + page.navigate("https://practicesoftwaretesting.com/"); +// page.locator("[placeholder='Search']").fill("pliers"); + page.getByPlaceholder("Search").fill("pliers"); + page.getByRole(AriaRole.BUTTON, + new Page.GetByRoleOptions().setName("Search")).click(); + + Locator products = page.locator(".card").filter( + new Locator.FilterOptions() + .setHas(page.getByAltText("Pliers")) + .setHasNot(page.getByText("Out of stock")) + ); + + List productNames = products.locator("h5").allTextContents(); + System.out.println(productNames); + + } + + + + + + + + + + + + + + +} diff --git a/src/test/java/com/serenitydojo/playwright/PlaywrightLocators.java b/src/test/java/com/serenitydojo/playwright/PlaywrightLocators.java new file mode 100644 index 00000000..c09d4eb5 --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/PlaywrightLocators.java @@ -0,0 +1,175 @@ +package com.serenitydojo.playwright; + +import com.microsoft.playwright.*; +import com.microsoft.playwright.assertions.PlaywrightAssertions; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junit.jupiter.api.Nested; + +import java.util.Arrays; +import java.util.List; + +import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; + +@Execution(ExecutionMode.SAME_THREAD) +public class PlaywrightLocators { + + + protected static Playwright playwright; + protected static Browser browser; + protected static BrowserContext browserContext; + + Page page; + + @BeforeAll + static void setUpBrowser() { + playwright = Playwright.create(); + browser = playwright.chromium().launch( + new BrowserType.LaunchOptions().setHeadless(true) + .setArgs(Arrays.asList("--no-sandbox", "--disable-extensions", "--disable-gpu")) + ); + } + + @BeforeEach + void setUp() { + browserContext = browser.newContext(); + page = browserContext.newPage(); + } + + @AfterEach + void closeContext() { + browserContext.close(); + } + + @AfterAll + static void tearDown() { + browser.close(); + playwright.close(); + } + + @DisplayName("Locating elements using CSS") + @Nested + class LocatingElementsUsingCSS { + + @BeforeEach + void openContactPage() { + page.navigate("https://practicesoftwaretesting.com/contact"); + } + + @DisplayName("By id") + @Test + void locateTheFirstNameFieldByID() { + page.locator("#first_name").fill("Sarah-Jane"); + assertThat(page.locator("#first_name")).hasValue("Sarah-Jane"); + } + + @DisplayName("By CSS class") + @Test + void locateTheSendButtonByCssClass() { + page.locator("#first_name").fill("Sarah-Jane"); + page.locator(".btnSubmit").click(); + List alertMessages = page.locator(".alert").allTextContents(); + Assertions.assertTrue(!alertMessages.isEmpty()); + } + + @DisplayName("By attribute") + @Test + void locateTheSendButtonByAttribute() { + page.locator("[placeholder='Your last name *']").fill("Smith"); + PlaywrightAssertions.assertThat(page.locator("#last_name")).hasValue("Smith"); + } + + + + + + + + + + + + } + + + + + + + + + + + + + + + + + + + @DisplayName("Locating elements by text") + @Nested + class LocatingElementsByText { + + @BeforeEach + void openTheCatalogPage() { + openPage(); + } + + @DisplayName("Locating an element by text contents") + @Test + void byText() { + page.getByText("Bolt Cutters").click(); + + assertThat(page.getByText("MightyCraft Hardware")).isVisible(); + } + + @DisplayName("Using alt text") + @Test + void byAltText() { + page.getByAltText("Combination Pliers").click(); + + assertThat(page.getByText("ForgeFlex Tools")).isVisible(); + } + + @DisplayName("Using title") + @Test + void byTitle() { + page.getByAltText("Combination Pliers").click(); + + page.getByTitle("Practice Software Testing - Toolshop").click(); + } + } + + @DisplayName("Locating elements by test Id") + @Nested + class LocatingElementsByTestID { + + @BeforeAll + static void setTestId() { + playwright.selectors().setTestIdAttribute("data-test"); + } + + @DisplayName("Using a custom data-test field") + @Test + void byTestId() { + openPage(); + + playwright.selectors().setTestIdAttribute("data-test"); + + page.getByTestId("search-query").fill("Pliers"); + page.getByTestId("search-submit").click(); + } + + } + + + + private void openPage() { + page.navigate("https://practicesoftwaretesting.com"); + } + + +} From a49d1d378998949173b05522d54fc988ac3d569e Mon Sep 17 00:00:00 2001 From: erhanbatu Date: Mon, 13 Jul 2026 21:48:15 +0200 Subject: [PATCH 4/8] Section4 lesson part2 --- .../playwright/AddingItemsToTheCartTest.java | 36 ++----------------- .../playwright/HeadlessChromeOptions.java | 23 ++++++++++++ 2 files changed, 25 insertions(+), 34 deletions(-) create mode 100644 src/test/java/com/serenitydojo/playwright/HeadlessChromeOptions.java diff --git a/src/test/java/com/serenitydojo/playwright/AddingItemsToTheCartTest.java b/src/test/java/com/serenitydojo/playwright/AddingItemsToTheCartTest.java index a6626c63..4293f096 100644 --- a/src/test/java/com/serenitydojo/playwright/AddingItemsToTheCartTest.java +++ b/src/test/java/com/serenitydojo/playwright/AddingItemsToTheCartTest.java @@ -8,44 +8,12 @@ import java.util.Arrays; import java.util.List; - +@UsePlaywright(HeadlessChromeOptions.class) public class AddingItemsToTheCartTest { - protected static Playwright playwright; - protected static Browser browser; - protected static BrowserContext browserContext; - - Page page; - - @BeforeAll - static void setUpBrowser() { - playwright = Playwright.create(); - browser = playwright.chromium().launch( - new BrowserType.LaunchOptions().setHeadless(true) - .setArgs(Arrays.asList("--no-sandbox", "--disable-extensions", "--disable-gpu")) - ); - } - - @BeforeEach - void setUp() { - browserContext = browser.newContext(); - page = browserContext.newPage(); - } - - @AfterEach - void closeContext() { - browserContext.close(); - } - - @AfterAll - static void tearDown() { - browser.close(); - playwright.close(); - } - @DisplayName("Search for pliers") @Test - void searchForPliers(){ + void searchForPliers(Page page){ page.navigate("https://practicesoftwaretesting.com/"); // page.locator("[placeholder='Search']").fill("pliers"); page.getByPlaceholder("Search").fill("pliers"); diff --git a/src/test/java/com/serenitydojo/playwright/HeadlessChromeOptions.java b/src/test/java/com/serenitydojo/playwright/HeadlessChromeOptions.java new file mode 100644 index 00000000..7cc71a18 --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/HeadlessChromeOptions.java @@ -0,0 +1,23 @@ +package com.serenitydojo.playwright; + +import com.microsoft.playwright.BrowserType; +import com.microsoft.playwright.junit.Options; +import com.microsoft.playwright.junit.OptionsFactory; + +import java.util.Arrays; + +public class HeadlessChromeOptions implements OptionsFactory { + + + @Override + public Options getOptions() { + return new Options().setLaunchOptions( + new BrowserType.LaunchOptions().setHeadless(true) + .setArgs(Arrays.asList("--no-sandbox", "--disable-extensions", "--disable-gpu")) + ) + .setTestIdAttribute("data-test"); + } + + + +} From d96b4aa2c4e027b67f5abaad78292b6f9085f693 Mon Sep 17 00:00:00 2001 From: erhanbatu Date: Tue, 14 Jul 2026 20:31:22 +0200 Subject: [PATCH 5/8] Section5 part1 --- .../playwright/HeadlessChromeOptions.java | 2 +- .../playwright/PlaywrightFormsTest.java | 99 +++++++++++++++++++ src/test/resources/data/sample-data.txt | 1 + 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/serenitydojo/playwright/PlaywrightFormsTest.java create mode 100644 src/test/resources/data/sample-data.txt diff --git a/src/test/java/com/serenitydojo/playwright/HeadlessChromeOptions.java b/src/test/java/com/serenitydojo/playwright/HeadlessChromeOptions.java index 7cc71a18..44ac2a39 100644 --- a/src/test/java/com/serenitydojo/playwright/HeadlessChromeOptions.java +++ b/src/test/java/com/serenitydojo/playwright/HeadlessChromeOptions.java @@ -12,7 +12,7 @@ public class HeadlessChromeOptions implements OptionsFactory { @Override public Options getOptions() { return new Options().setLaunchOptions( - new BrowserType.LaunchOptions().setHeadless(true) + new BrowserType.LaunchOptions().setHeadless(false) .setArgs(Arrays.asList("--no-sandbox", "--disable-extensions", "--disable-gpu")) ) .setTestIdAttribute("data-test"); diff --git a/src/test/java/com/serenitydojo/playwright/PlaywrightFormsTest.java b/src/test/java/com/serenitydojo/playwright/PlaywrightFormsTest.java new file mode 100644 index 00000000..2ad5574f --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/PlaywrightFormsTest.java @@ -0,0 +1,99 @@ +package com.serenitydojo.playwright; + +import com.microsoft.playwright.Page; +import com.microsoft.playwright.junit.UsePlaywright; +import com.microsoft.playwright.options.AriaRole; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.net.URISyntaxException; +import java.nio.file.Path; + +import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; + +@UsePlaywright(HeadlessChromeOptions.class) +public class PlaywrightFormsTest { + + @DisplayName("Interacting with text fields") + @Nested + class WhenInteractingWithTextFields { + + @BeforeEach + void openContactPage(Page page) { + page.navigate("https://practicesoftwaretesting.com/contact"); + } + + @DisplayName("Complete the form") + @Test + void completeForm(Page page) throws URISyntaxException { + var firstNameField = page.getByLabel("First name"); + var lastNameField = page.getByLabel("Last name"); + var emailNameField = page.getByLabel("Email"); + var messageField = page.getByLabel("Message"); + var subjectField = page.getByLabel("Subject"); + var uploadField = page.getByLabel("Attachment"); + + firstNameField.fill("Erhan"); + lastNameField.fill("Batu"); + emailNameField.fill("erbatu@gmail.com"); + messageField.fill("Hello World!"); + subjectField.selectOption("Warranty"); + + Path fileToUpload = Path.of( + ClassLoader.getSystemResource("data/sample-data.txt").toURI() + ); + + page.setInputFiles("#attachment", fileToUpload); + + + assertThat(firstNameField).hasValue("Erhan"); + assertThat(lastNameField).hasValue("Batu"); + assertThat(subjectField).hasValue("warranty"); + + String uploadFile = uploadField.inputValue(); + org.assertj.core.api.Assertions.assertThat(uploadFile).endsWith("sample-data.txt"); + + + + } + + @DisplayName("Mandatory Fields") + @ParameterizedTest + @ValueSource(strings = {"First name", "Last name"}) + void mandatoryFields(String fieldName, Page page){ + var firstNameField = page.getByLabel("First name"); + var lastNameField = page.getByLabel("Last name"); + var emailNameField = page.getByLabel("Email"); + var messageField = page.getByLabel("Message"); + var subjectField = page.getByLabel("Subject"); + var sendButton = page.getByText("Send"); + + firstNameField.fill("Erhan"); + lastNameField.fill("Batu"); + emailNameField.fill("erbatu@gmail.com"); + messageField.fill("Hello World!"); + subjectField.selectOption("Warranty"); + + page.getByLabel(fieldName).clear(); + + sendButton.click(); + + var errorMessage = page.getByRole(AriaRole.ALERT).getByText(fieldName + " is required"); + + assertThat(errorMessage).isVisible(); + + + } + } + + + + + + + +} diff --git a/src/test/resources/data/sample-data.txt b/src/test/resources/data/sample-data.txt new file mode 100644 index 00000000..5e4f2565 --- /dev/null +++ b/src/test/resources/data/sample-data.txt @@ -0,0 +1 @@ +test1234 \ No newline at end of file From fc5cd6c798c8b36740a654589803d83805be88e3 Mon Sep 17 00:00:00 2001 From: erhanbatu Date: Wed, 15 Jul 2026 08:23:44 +0200 Subject: [PATCH 6/8] Section5 part2 --- .../playwright/PlaywrightFormsTest.java | 70 +++++++++++++++++-- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/serenitydojo/playwright/PlaywrightFormsTest.java b/src/test/java/com/serenitydojo/playwright/PlaywrightFormsTest.java index 2ad5574f..73f0dbf4 100644 --- a/src/test/java/com/serenitydojo/playwright/PlaywrightFormsTest.java +++ b/src/test/java/com/serenitydojo/playwright/PlaywrightFormsTest.java @@ -1,17 +1,16 @@ package com.serenitydojo.playwright; +import com.microsoft.playwright.Locator; import com.microsoft.playwright.Page; import com.microsoft.playwright.junit.UsePlaywright; import com.microsoft.playwright.options.AriaRole; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import java.net.URISyntaxException; import java.nio.file.Path; +import java.util.regex.Pattern; import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; @@ -88,6 +87,69 @@ void mandatoryFields(String fieldName, Page page){ } + + @DisplayName("Categories Tests") + @Test + void categoriesTests(Page page){ + + page.getByRole(AriaRole.BUTTON).getByText("Categories").click(); + page.getByLabel("nav-categories").getByText("Power Tools").click(); + + assertThat(page).hasTitle(Pattern.compile(".*Power Tools.*")); + + String circularSawPrice = page.locator(".card").filter( + new Locator.FilterOptions() + .setHasText("circular saw")) + .locator("[data-test='product-price']").innerText(); + System.out.println(circularSawPrice); + + Assertions.assertEquals(circularSawPrice, "$80.19"); + + + } + + @DisplayName("CheckBox Test") + @Test + void checkBoxTest(Page page){ + + page.getByRole(AriaRole.BUTTON).getByText("Categories").click(); + page.getByLabel("nav-categories").getByText("Power Tools").click(); + + page.getByRole(AriaRole.LIST).locator(".checkbox") + .filter( + new Locator.FilterOptions().setHasText("Grinder") + ).locator("input[type='checkbox']").check(); + + + assertThat(page.getByText("There are no products found")).isVisible(); + + page.getByRole(AriaRole.LIST).locator(".checkbox").filter( + new Locator.FilterOptions().setHasText("Sander") + ).locator("[name='category_id']").check(); + + Locator productCards = page.locator(".card-img-top"); + + assertThat(productCards).hasCount(3); + + System.out.println(productCards.count()); + + } + + @DisplayName("Dropdowns Test") + @Test + void dropDownTesting(Page page){ + + page.getByRole(AriaRole.BUTTON).getByText("Categories").click(); + page.getByLabel("nav-categories").getByText("Power Tools").click(); + + page.getByRole(AriaRole.COMBOBOX).selectOption("Price (High - Low)"); + + } + + + + + } From 61497b3cba527a1d149aa6a528b73111ad192e5f Mon Sep 17 00:00:00 2001 From: erhanbatu Date: Wed, 15 Jul 2026 09:00:13 +0200 Subject: [PATCH 7/8] Section6 --- .../playwright/PlaywrightAssertionsTest.java | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 src/test/java/com/serenitydojo/playwright/PlaywrightAssertionsTest.java diff --git a/src/test/java/com/serenitydojo/playwright/PlaywrightAssertionsTest.java b/src/test/java/com/serenitydojo/playwright/PlaywrightAssertionsTest.java new file mode 100644 index 00000000..56e44a12 --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/PlaywrightAssertionsTest.java @@ -0,0 +1,112 @@ +package com.serenitydojo.playwright; + +import com.microsoft.playwright.Page; +import com.microsoft.playwright.junit.UsePlaywright; +import com.microsoft.playwright.options.LoadState; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.util.Comparator; +import java.util.List; + +import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; + +@UsePlaywright(HeadlessChromeOptions.class) +public class PlaywrightAssertionsTest { + + @DisplayName("Making assertions about the contents of a field") + @Nested + class LocationgElementsUsingCSS { + + @BeforeEach + void openContactPage(Page page) { + page.navigate("https://practicesoftwaretesting.com/contact"); + } + + @DisplayName("Checking the value of a field") + @Test + void fieldValues(Page page){ + + var firstNameField = page.getByLabel("First Name"); + + firstNameField.fill("Erhan"); + + assertThat(firstNameField).hasValue("Erhan"); + + assertThat(firstNameField).not().isDisabled(); + assertThat(firstNameField).isVisible(); + assertThat(firstNameField).isEditable(); + + } + + + + } + + + @DisplayName("Making assertions about data values") + @Nested + class MakingAssertionsAboutDataValues { + + @BeforeEach + void openHomePage(Page page) { + page.navigate("https://practicesoftwaretesting.com"); + page.waitForCondition(() -> page.getByTestId("product-name").count() > 0); + } + + @Test + void allProductPricesShouldBeCorrectValues(Page page){ + + List prices = page.getByTestId("product-price") + .allTextContents() + .stream() + .map(price -> Double.parseDouble(price.replace("$",""))) + .toList(); + + Assertions.assertThat(prices) + .isNotEmpty() + .allMatch(price -> price > 0) + .doesNotContain(0.0) + .allMatch(price -> price < 1000) + .allSatisfy(price -> + Assertions.assertThat(price).isGreaterThan(0.0) + .isLessThan(1000.0)); + + } + + @Test + void shouldSortInAlphabeticalOrder(Page page){ + + page.getByLabel("Sort").selectOption("Name (A - Z)"); + page.waitForLoadState(LoadState.NETWORKIDLE); + + List productNames = page.getByTestId("product-name").allTextContents(); + +// Assertions.assertThat(productNames).isSortedAccordingTo(Comparator.naturalOrder()); + Assertions.assertThat(productNames).isSortedAccordingTo(String.CASE_INSENSITIVE_ORDER); + + } + + @Test + void shouldSortInReverseAlphabeticalOrder(Page page){ + + page.getByLabel("Sort").selectOption("Name (Z - A)"); + page.waitForLoadState(LoadState.NETWORKIDLE); + + List productNames = page.getByTestId("product-name").allTextContents(); + + Assertions.assertThat(productNames).isSortedAccordingTo(Comparator.reverseOrder()); + + } + + } + + + + + + +} From 049baf2d9b43351b63a4fa3a812706dbf5d5be98 Mon Sep 17 00:00:00 2001 From: erhanbatu Date: Mon, 27 Jul 2026 20:13:16 +0200 Subject: [PATCH 8/8] Section7 --- pom.xml | 8 + .../playwright/PlaywrightWaitsTest.java | 172 ++++++++++++++++++ .../toolshop/catalog/AddToCartTest.java | 93 ++++++++++ .../catalog/SearchForProductsTest.java | 71 ++++++++ .../catalog/pageobjects/CartLineItem.java | 3 + .../catalog/pageobjects/CheckoutCart.java | 39 ++++ .../toolshop/catalog/pageobjects/NavBar.java | 27 +++ .../catalog/pageobjects/ProductDetails.java | 31 ++++ .../catalog/pageobjects/ProductList.java | 39 ++++ .../catalog/pageobjects/SearchComponent.java | 37 ++++ .../toolshop/contact/ContactForm.java | 63 +++++++ .../toolshop/contact/ContactFormTest.java | 113 ++++++++++++ .../toolshop/fixtures/PlaywrightTestCase.java | 52 ++++++ .../toolshop/fixtures/ProductSummary.java | 3 + 14 files changed, 751 insertions(+) create mode 100644 src/test/java/com/serenitydojo/playwright/PlaywrightWaitsTest.java create mode 100644 src/test/java/com/serenitydojo/playwright/toolshop/catalog/AddToCartTest.java create mode 100644 src/test/java/com/serenitydojo/playwright/toolshop/catalog/SearchForProductsTest.java create mode 100644 src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/CartLineItem.java create mode 100644 src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/CheckoutCart.java create mode 100644 src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/NavBar.java create mode 100644 src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/ProductDetails.java create mode 100644 src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/ProductList.java create mode 100644 src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/SearchComponent.java create mode 100644 src/test/java/com/serenitydojo/playwright/toolshop/contact/ContactForm.java create mode 100644 src/test/java/com/serenitydojo/playwright/toolshop/contact/ContactFormTest.java create mode 100644 src/test/java/com/serenitydojo/playwright/toolshop/fixtures/PlaywrightTestCase.java create mode 100644 src/test/java/com/serenitydojo/playwright/toolshop/fixtures/ProductSummary.java diff --git a/pom.xml b/pom.xml index 093ab125..b9e5dffe 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,8 @@ 21 21 UTF-8 + + 2.35.2 @@ -33,6 +35,12 @@ 3.27.7 test + + io.qameta.allure + allure-jupiter + ${allure.version} + test + diff --git a/src/test/java/com/serenitydojo/playwright/PlaywrightWaitsTest.java b/src/test/java/com/serenitydojo/playwright/PlaywrightWaitsTest.java new file mode 100644 index 00000000..90397442 --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/PlaywrightWaitsTest.java @@ -0,0 +1,172 @@ +package com.serenitydojo.playwright; + + +import com.microsoft.playwright.Page; +import com.microsoft.playwright.junit.UsePlaywright; +import com.microsoft.playwright.options.AriaRole; +import com.microsoft.playwright.options.WaitForSelectorState; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.util.Comparator; +import java.util.List; + +import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; + +@UsePlaywright(HeadlessChromeOptions.class) +public class PlaywrightWaitsTest { + + + @DisplayName("Explicit Wait") + @Nested + class ExplicitWait { + @BeforeEach + void openContactPage(Page page) { + page.navigate("https://practicesoftwaretesting.com"); + page.waitForSelector("[data-test=product-name]"); + page.waitForSelector(".card-img-top"); + } + + @Test + void shouldShowAllProductName(Page page){ + List productNames = page.getByTestId("product-name").allInnerTexts(); + Assertions.assertThat(productNames).contains("Pliers", "Bolt Cutters"); + + } + + @Test + void shouldShowAllProductImages(Page page){ + List productImageTitles = page.locator(".card-img-top").all() + .stream() + .map(img -> img.getAttribute("alt")) + .toList(); + + Assertions.assertThat(productImageTitles).contains("Bolt Cutters"); + } + + } + + @DisplayName("Implicit Wait") + @Nested + class ImplicitWait{ + + @BeforeEach + void openContactPage(Page page) { + page.navigate("https://practicesoftwaretesting.com"); + + } + + @Test + void shouldWaitForTheFilterCheckboxes(Page page){ + + var screwDriverFilter = page.getByLabel("Screwdriver"); + + screwDriverFilter.click(); + + assertThat(screwDriverFilter).isChecked(); + } + + @Test + void shouldFilterProductsByCategory(Page page){ + + page.getByRole(AriaRole.MENUBAR).getByText("Categories").click(); + page.getByRole(AriaRole.MENUBAR).getByText("Power Tools").click(); + + page.waitForSelector(".card", + new Page.WaitForSelectorOptions().setState(WaitForSelectorState.VISIBLE).setTimeout(2000) + ); + var filteredProducts = page.getByTestId("product-name").allInnerTexts(); + + Assertions.assertThat(filteredProducts).contains("Sheet Sander"); + + } + + + + } + + + @Nested + class WaitingForElementsToAppearAndDisappear{ + + @BeforeEach + void openContactPage(Page page) { + page.navigate("https://practicesoftwaretesting.com"); + + } + + @Test + void shouldDisplayToasterMessage(Page page){ + + page.getByText("Bolt Cutters").click(); + page.getByText("Add to cart").click(); + + assertThat(page.getByRole(AriaRole.ALERT)).isVisible(); + assertThat(page.getByRole(AriaRole.ALERT)).hasText("Product added to shopping cart."); + + page.waitForCondition(() -> page.getByRole(AriaRole.ALERT).isHidden()); + + } + + @Test + void shoudlUpdateTheCartItemCount(Page page){ + + page.getByText("Bolt Cutters").click(); + page.getByText("Add to cart").click(); + + page.waitForCondition(() -> page.getByTestId("cart-quantity").textContent().equals("1")); + page.waitForSelector("[data-test=cart-quantity]:has-text('1')"); + + } + + + + + } + + + @Nested + class WaitingForAPICalls { + + @Test + void sortByDescendingPrice(Page page) { + page.navigate("https://practicesoftwaretesting.com"); + + // Sort by descending price + // (Note: The API endpoint has evolved and is slightly different to the one in the video; + // it now sends the sort/page criteria in the request body rather than as URL query params) + page.waitForResponse( + response -> response.url().contains("/products") + && response.request().postData() != null + && response.request().postData().contains("\"sort\":\"price,desc\""), + () -> { + page.getByTestId("sort").selectOption("Price (High - Low)"); + }); + + // Find all the prices on the page + var productPrices = page.getByTestId("product-price") + .allInnerTexts() + .stream() + .map(WaitingForAPICalls::extractPrice) + .toList(); + + // Are the prices in the correct order + Assertions.assertThat(productPrices) + .isNotEmpty() + .isSortedAccordingTo(Comparator.reverseOrder()); + } + + private static double extractPrice(String price) { + return Double.parseDouble(price.replace("$", "")); + } + } + + + + + + +} diff --git a/src/test/java/com/serenitydojo/playwright/toolshop/catalog/AddToCartTest.java b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/AddToCartTest.java new file mode 100644 index 00000000..f9f5f36e --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/AddToCartTest.java @@ -0,0 +1,93 @@ +package com.serenitydojo.playwright.toolshop.catalog; + +import com.serenitydojo.playwright.toolshop.catalog.pageobjects.*; +import com.serenitydojo.playwright.toolshop.fixtures.PlaywrightTestCase; +import io.qameta.allure.Feature; +import io.qameta.allure.Story; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +@DisplayName("Shopping Cart") +@Feature("Shopping Cart") +public class AddToCartTest extends PlaywrightTestCase { + + SearchComponent searchComponent; + ProductList productList; + ProductDetails productDetails; + NavBar navBar; + CheckoutCart checkoutCart; + + @BeforeEach + void openHomePage() { + page.navigate("https://practicesoftwaretesting.com"); + } + + @BeforeEach + void setUp() { + searchComponent = new SearchComponent(page); + productList = new ProductList(page); + productDetails = new ProductDetails(page); + navBar = new NavBar(page); + checkoutCart = new CheckoutCart(page); + } + + @Test + @Story("Check out") + @DisplayName("Checking out a single item") + void whenCheckingOutASingleItem() { + searchComponent.searchBy("pliers"); + productList.viewProductDetails("Combination Pliers"); + + productDetails.increaseQuanityBy(2); + productDetails.addToCart(); + + navBar.openCart(); + + List lineItems = checkoutCart.getLineItems(); + + Assertions.assertThat(lineItems) + .hasSize(1) + .first() + .satisfies(item -> { + Assertions.assertThat(item.title()).contains("Combination Pliers"); + Assertions.assertThat(item.quantity()).isEqualTo(3); + Assertions.assertThat(item.total()).isEqualTo(item.quantity() * item.price()); + }); + } + + @Test + @Story("Check out") + @DisplayName("Checking out multiple items") + void whenCheckingOutMultipleItems() { + navBar.openHomePage(); + + productList.viewProductDetails("Bolt Cutters"); + productDetails.increaseQuanityBy(2); + productDetails.addToCart(); + + navBar.openHomePage(); + productList.viewProductDetails("Slip Joint Pliers"); + productDetails.addToCart(); + + navBar.openCart(); + + List lineItems = checkoutCart.getLineItems(); + + Assertions.assertThat(lineItems).hasSize(2); + List productNames = lineItems.stream().map(CartLineItem::title).toList(); + Assertions.assertThat(productNames).contains("Bolt Cutters", "Slip Joint Pliers"); + + Assertions.assertThat(lineItems) + .allSatisfy(item -> { + Assertions.assertThat(item.quantity()).isGreaterThanOrEqualTo(1); + Assertions.assertThat(item.price()).isGreaterThan(0.0); + Assertions.assertThat(item.total()).isGreaterThan(0.0); + Assertions.assertThat(item.total()).isEqualTo(item.quantity() * item.price()); + }); + + } +} \ No newline at end of file diff --git a/src/test/java/com/serenitydojo/playwright/toolshop/catalog/SearchForProductsTest.java b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/SearchForProductsTest.java new file mode 100644 index 00000000..1cc5d63e --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/SearchForProductsTest.java @@ -0,0 +1,71 @@ +package com.serenitydojo.playwright.toolshop.catalog; + +import com.serenitydojo.playwright.toolshop.catalog.pageobjects.ProductList; +import com.serenitydojo.playwright.toolshop.catalog.pageobjects.SearchComponent; +import com.serenitydojo.playwright.toolshop.fixtures.PlaywrightTestCase; +import io.qameta.allure.Feature; +import io.qameta.allure.Story; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@DisplayName("Searching for products") +@Feature("Searching for products") +public class SearchForProductsTest extends PlaywrightTestCase { + + @BeforeEach + void openHomePage() { + page.navigate("https://practicesoftwaretesting.com"); + } + + @Nested + @DisplayName("Searching by keyword") + @Story("Searching by keyword") + class SearchingByKeyword { + + @Test + @DisplayName("When there are matching results") + void whenSearchingByKeyword() { + SearchComponent searchComponent = new SearchComponent(page); + ProductList productList = new ProductList(page); + + searchComponent.searchBy("tape"); + + var matchingProducts = productList.getProductNames(); + + Assertions.assertThat(matchingProducts).contains("Tape Measure 7.5m", "Measuring Tape", "Tape Measure 5m"); + } + + @Test + @DisplayName("When there are no matching results") + void whenThereIsNoMatchingProduct() { + SearchComponent searchComponent = new SearchComponent(page); + ProductList productList = new ProductList(page); + searchComponent.searchBy("unknown"); + + var matchingProducts = productList.getProductNames(); + + Assertions.assertThat(matchingProducts).isEmpty(); + Assertions.assertThat(productList.getSearchCompletedMessage()).contains("There are no products found."); + } + } + + @Test + @Story("Clearing the previous search results") + @DisplayName("When the user clears a previous search results") + void clearingTheSearchResults() { + SearchComponent searchComponent = new SearchComponent(page); + ProductList productList = new ProductList(page); + searchComponent.searchBy("saw"); + + var matchingFilteredProducts = productList.getProductNames(); + Assertions.assertThat(matchingFilteredProducts).hasSize(2); + + searchComponent.clearSearch(); + + var matchingProducts = productList.getProductNames(); + Assertions.assertThat(matchingProducts).hasSize(9); + } +} \ No newline at end of file diff --git a/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/CartLineItem.java b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/CartLineItem.java new file mode 100644 index 00000000..c8810308 --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/CartLineItem.java @@ -0,0 +1,3 @@ +package com.serenitydojo.playwright.toolshop.catalog.pageobjects; + +public record CartLineItem(String title, int quantity, double price, double total) {} \ No newline at end of file diff --git a/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/CheckoutCart.java b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/CheckoutCart.java new file mode 100644 index 00000000..f2df7c77 --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/CheckoutCart.java @@ -0,0 +1,39 @@ +package com.serenitydojo.playwright.toolshop.catalog.pageobjects; + +import com.microsoft.playwright.Page; +import io.qameta.allure.Step; + +import java.util.List; + +public class CheckoutCart { + private final Page page; + + public CheckoutCart(Page page) { + this.page = page; + } + + public List getLineItems() { + page.locator("app-cart tbody tr").first().waitFor(); + return page.locator("app-cart tbody tr") + .all() + .stream() + .map( + row -> { + String title = trimmed(row.getByTestId("product-title").innerText()); + int quantity = Integer.parseInt(row.getByTestId("product-quantity").inputValue()); + double price = Double.parseDouble(price(row.getByTestId("product-price").innerText())); + double linePrice = Double.parseDouble(price(row.getByTestId("line-price").innerText())); + return new CartLineItem(title, quantity, price, linePrice); + } + ).toList(); + } + + private String trimmed(String value) { + return value.strip().replaceAll("\u00A0", ""); + } + + private String price(String value) { + return value.replace("$", ""); + } + +} \ No newline at end of file diff --git a/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/NavBar.java b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/NavBar.java new file mode 100644 index 00000000..6197234e --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/NavBar.java @@ -0,0 +1,27 @@ +package com.serenitydojo.playwright.toolshop.catalog.pageobjects; + +import com.microsoft.playwright.Page; +import io.qameta.allure.Step; + +public class NavBar { + private final Page page; + + public NavBar(Page page) { + this.page = page; + } + + @Step("Open cart") + public void openCart() { + page.getByTestId("nav-cart").click(); + } + + @Step("Open the home page") + public void openHomePage() { + page.navigate("https://practicesoftwaretesting.com"); + } + + @Step("Open the Contact page") + public void toTheContactPage() { + page.navigate("https://practicesoftwaretesting.com/contact"); + } +} \ No newline at end of file diff --git a/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/ProductDetails.java b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/ProductDetails.java new file mode 100644 index 00000000..0c253431 --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/ProductDetails.java @@ -0,0 +1,31 @@ +package com.serenitydojo.playwright.toolshop.catalog.pageobjects; + +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; +import io.qameta.allure.Step; + +public class ProductDetails { + private final Page page; + + public ProductDetails(Page page) { + this.page = page; + } + + @Step("Increase product quantity") + public void increaseQuanityBy(int increment) { + for (int i = 1; i <= increment; i++) { + page.getByTestId("increase-quantity").click(); + } + } + + @Step("Add to cart") + public void addToCart() { + page.waitForResponse( + response -> response.url().contains("/carts") && response.request().method().equals("POST"), + () -> { + page.getByText("Add to cart").click(); + page.getByRole(AriaRole.ALERT).click(); + } + ); + } +} \ No newline at end of file diff --git a/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/ProductList.java b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/ProductList.java new file mode 100644 index 00000000..dcf354ba --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/ProductList.java @@ -0,0 +1,39 @@ +package com.serenitydojo.playwright.toolshop.catalog.pageobjects; + +import com.microsoft.playwright.Page; +import com.serenitydojo.playwright.toolshop.fixtures.ProductSummary; +import io.qameta.allure.Step; + +import java.util.List; + +public class ProductList { + private final Page page; + + public ProductList(Page page) { + this.page = page; + } + + + public List getProductNames() { + return page.getByTestId("product-name").allInnerTexts(); + } + + public List getProductSummaries() { + return page.locator(".card").all() + .stream() + .map(productCard -> { + String productName = productCard.getByTestId("product-name").textContent().strip(); + String productPrice = productCard.getByTestId("product-price").textContent(); + return new ProductSummary(productName, productPrice); + }).toList(); + } + + @Step("View product details") + public void viewProductDetails(String productName) { + page.locator(".card").getByText(productName).click(); + } + + public String getSearchCompletedMessage() { + return page.getByTestId("search_completed").textContent(); + } +} \ No newline at end of file diff --git a/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/SearchComponent.java b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/SearchComponent.java new file mode 100644 index 00000000..86703196 --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/toolshop/catalog/pageobjects/SearchComponent.java @@ -0,0 +1,37 @@ +package com.serenitydojo.playwright.toolshop.catalog.pageobjects; + +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; + +public class SearchComponent { + private final Page page; + + public SearchComponent(Page page) { + this.page = page; + } + + public void searchBy(String keyword) { + page.waitForResponse("**/products/search?**", () -> { + page.getByPlaceholder("Search").fill(keyword); + page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Search")).click(); + }); + } + + public void clearSearch() { + page.waitForResponse("**/products**", () -> { + page.getByTestId("search-reset").click(); + }); + } + + public void filterBy(String filterName) { + page.waitForResponse("**/products?**by_category=**", () -> { + page.getByLabel(filterName).click(); + }); + } + + public void sortBy(String sortFilter) { + page.waitForResponse("**/products?page=0&sort=**", () -> { + page.getByTestId("sort").selectOption(sortFilter); + }); + } +} \ No newline at end of file diff --git a/src/test/java/com/serenitydojo/playwright/toolshop/contact/ContactForm.java b/src/test/java/com/serenitydojo/playwright/toolshop/contact/ContactForm.java new file mode 100644 index 00000000..09dd4d52 --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/toolshop/contact/ContactForm.java @@ -0,0 +1,63 @@ +package com.serenitydojo.playwright.toolshop.contact; + +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; + +import java.nio.file.Path; + +public class ContactForm { + private final Page page; + private Locator firstNameField; + private Locator lastNameField; + private Locator emailNameField; + private Locator messageField; + private Locator subjectField; + private Locator sendButton; + + public ContactForm(Page page) { + this.page = page; + this.firstNameField = page.getByLabel("First name"); + this.lastNameField = page.getByLabel("Last name"); + this.emailNameField = page.getByLabel("Email"); + this.messageField = page.getByLabel("Message"); + this.subjectField = page.getByLabel("Subject"); + this.sendButton = page.getByText("Send"); + } + + public void setFirstName(String firstName) { + firstNameField.fill(firstName); + } + + public void setLastName(String lastName) { + lastNameField.fill(lastName); + } + + public void setEmail(String email) { + emailNameField.fill(email); + } + + public void setMessage(String message) { + messageField.fill(message); + } + + public void selectSubject(String subject) { + subjectField.selectOption(subject); + } + + public void setAttachment(Path fileToUpload) { + page.setInputFiles("#attachment", fileToUpload); + } + + public void submitForm() { + sendButton.click(); + } + + public String getAlertMessage() { + return page.getByRole(AriaRole.ALERT).textContent(); + } + + public void clearField(String fieldName) { + page.getByLabel(fieldName).clear(); + } +} \ No newline at end of file diff --git a/src/test/java/com/serenitydojo/playwright/toolshop/contact/ContactFormTest.java b/src/test/java/com/serenitydojo/playwright/toolshop/contact/ContactFormTest.java new file mode 100644 index 00000000..6eefe35a --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/toolshop/contact/ContactFormTest.java @@ -0,0 +1,113 @@ +package com.serenitydojo.playwright.toolshop.contact; + +import com.microsoft.playwright.assertions.LocatorAssertions; +import com.microsoft.playwright.options.AriaRole; +import com.serenitydojo.playwright.toolshop.catalog.pageobjects.NavBar; +import com.serenitydojo.playwright.toolshop.fixtures.PlaywrightTestCase; +import io.qameta.allure.Allure; +import io.qameta.allure.Feature; +import io.qameta.allure.Story; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat; + +@DisplayName("Contact form") +@Feature("Contact form") +@Execution(ExecutionMode.SAME_THREAD) +public class ContactFormTest extends PlaywrightTestCase { + + ContactForm contactForm; + NavBar navigate; + + @BeforeEach + void openContactPage() { + contactForm = new ContactForm(page); + navigate = new NavBar(page); + navigate.toTheContactPage(); + } + + @Story("Submitting a request") + @DisplayName("Customers can use the contact form to contact us") + @Test + void completeForm() throws URISyntaxException { + contactForm.setFirstName("Sarah-Jane"); + contactForm.setLastName("Smith"); + contactForm.setEmail("sarah@example.com"); + contactForm.setMessage("A very long message to the warranty service about a warranty on a product!"); + contactForm.selectSubject("Warranty"); + + Path fileToUpload = Paths.get(ClassLoader.getSystemResource("data/sample-data.txt").toURI()); + contactForm.setAttachment(fileToUpload); + + contactForm.submitForm(); + + Assertions.assertThat(contactForm.getAlertMessage()) + .contains("Thanks for your message! We will contact you shortly."); + } + + @Story("Submitting a request") + @DisplayName("First name, last name, email and message are mandatory") + @ParameterizedTest(name = "{arguments} is a mandatory field") + @ValueSource(strings = {"First name", "Last name", "Email", "Message"}) + void mandatoryFields(String fieldName) { + // Fill in the field values + contactForm.setFirstName("Sarah-Jane"); + contactForm.setLastName("Smith"); + contactForm.setEmail("sarah@example.com"); + contactForm.setMessage("A very long message to the warranty service about a warranty on a product!"); + contactForm.selectSubject("Warranty"); + + // Clear one of the fields + contactForm.clearField(fieldName); + page.waitForTimeout(250); + contactForm.submitForm(); + + // Check the error message for that field + var errorMessage = page.getByRole(AriaRole.ALERT).getByText(fieldName + " is required"); + + assertThat(errorMessage).isVisible(); + } + + @Story("Submitting a request") + @DisplayName("The message must be at least 50 characters long") + @Test + void messageTooShort() { + + contactForm.setFirstName("Sarah-Jane"); + contactForm.setLastName("Smith"); + contactForm.setEmail("sarah@example.com"); + contactForm.setMessage("A short long message."); + contactForm.selectSubject("Warranty"); + + contactForm.submitForm(); + + assertThat(page.getByRole(AriaRole.ALERT)).hasText("Message must be minimal 50 characters"); + } + + @Story("Submitting a request") + @DisplayName("The email address must be correctly formatted") + @ParameterizedTest(name = "'{arguments}' should be rejected") + @ValueSource(strings = {"not-an-email", "not-an.email.com", "notanemail"}) + void invalidEmailField(String invalidEmail) { + contactForm.setFirstName("Sarah-Jane"); + contactForm.setLastName("Smith"); + contactForm.setEmail(invalidEmail); + contactForm.setMessage("A very long message to the warranty service about a warranty on a product!"); + contactForm.selectSubject("Warranty"); + + contactForm.submitForm(); + + assertThat(page.getByRole(AriaRole.ALERT)).hasText("Email format is invalid"); + } +} \ No newline at end of file diff --git a/src/test/java/com/serenitydojo/playwright/toolshop/fixtures/PlaywrightTestCase.java b/src/test/java/com/serenitydojo/playwright/toolshop/fixtures/PlaywrightTestCase.java new file mode 100644 index 00000000..39955046 --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/toolshop/fixtures/PlaywrightTestCase.java @@ -0,0 +1,52 @@ +package com.serenitydojo.playwright.toolshop.fixtures; + +import com.microsoft.playwright.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +import java.util.Arrays; + +public abstract class PlaywrightTestCase { + + protected static ThreadLocal playwright + = ThreadLocal.withInitial(() -> { + Playwright playwright = Playwright.create(); + playwright.selectors().setTestIdAttribute("data-test"); + return playwright; + } + ); + + protected static ThreadLocal browser = ThreadLocal.withInitial(() -> + playwright.get().chromium().launch( + new BrowserType.LaunchOptions() + .setHeadless(false) + .setArgs(Arrays.asList("--no-sandbox", "--disable-extensions", "--disable-gpu")) + ) + ); + + protected BrowserContext browserContext; + + protected Page page; + + @BeforeEach + void setUpBrowserContext() { + browserContext = browser.get().newContext(); + page = browserContext.newPage(); + } + + @AfterEach + void closeContext() { + browserContext.close(); + } + + @AfterAll + static void tearDown() { + browser.get().close(); + browser.remove(); + + playwright.get().close(); + playwright.remove(); + } + +} \ No newline at end of file diff --git a/src/test/java/com/serenitydojo/playwright/toolshop/fixtures/ProductSummary.java b/src/test/java/com/serenitydojo/playwright/toolshop/fixtures/ProductSummary.java new file mode 100644 index 00000000..41148bb3 --- /dev/null +++ b/src/test/java/com/serenitydojo/playwright/toolshop/fixtures/ProductSummary.java @@ -0,0 +1,3 @@ +package com.serenitydojo.playwright.toolshop.fixtures; + +public record ProductSummary(String name, String price) {} \ No newline at end of file