diff --git a/pom.xml b/pom.xml
index 497ee081..b9e5dffe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,6 +12,8 @@
21
21
UTF-8
+
+ 2.35.2
@@ -27,6 +29,19 @@
5.11.3
test
+
+ org.assertj
+ assertj-core
+ 3.27.7
+ test
+
+
+ io.qameta.allure
+ allure-jupiter
+ ${allure.version}
+ 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..5fc6f5f9 100644
--- a/src/test/java/com/serenitydojo/playwright/ASimplePlaywrightTest.java
+++ b/src/test/java/com/serenitydojo/playwright/ASimplePlaywrightTest.java
@@ -1,15 +1,67 @@
package com.serenitydojo.playwright;
-import com.microsoft.playwright.Browser;
-import com.microsoft.playwright.Page;
-import com.microsoft.playwright.Playwright;
-import org.junit.jupiter.api.Assertions;
-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 {
+ private static Playwright playwright;
+ private static Browser browser;
+ private static BrowserContext browserContext;
+ Page page;
+
+ @BeforeAll
+ public static void setUpBrowser(){
+ playwright = Playwright.create();
+ 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();
+ }
+
+ @AfterAll
+ public static 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);
+
}
}
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..4293f096
--- /dev/null
+++ b/src/test/java/com/serenitydojo/playwright/AddingItemsToTheCartTest.java
@@ -0,0 +1,47 @@
+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;
+
+@UsePlaywright(HeadlessChromeOptions.class)
+public class AddingItemsToTheCartTest {
+
+ @DisplayName("Search for pliers")
+ @Test
+ void searchForPliers(Page page){
+ 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/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);
+
+ }
+}
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..44ac2a39
--- /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(false)
+ .setArgs(Arrays.asList("--no-sandbox", "--disable-extensions", "--disable-gpu"))
+ )
+ .setTestIdAttribute("data-test");
+ }
+
+
+
+}
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());
+
+ }
+
+ }
+
+
+
+
+
+
+}
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..73f0dbf4
--- /dev/null
+++ b/src/test/java/com/serenitydojo/playwright/PlaywrightFormsTest.java
@@ -0,0 +1,161 @@
+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.*;
+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;
+
+@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();
+
+
+ }
+
+ @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)");
+
+ }
+
+
+
+
+
+ }
+
+
+
+
+
+
+
+}
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");
+ }
+
+
+}
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
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