Skip to content

Commit 06596e2

Browse files
committed
Updated sample code
1 parent 4d29422 commit 06596e2

File tree

2 files changed

+364
-1
lines changed

2 files changed

+364
-1
lines changed
Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
package com.serenitydojo.playwright;
2+
3+
import com.microsoft.playwright.*;
4+
import com.microsoft.playwright.options.AriaRole;
5+
import org.assertj.core.api.Assertions;
6+
import org.junit.jupiter.api.*;
7+
import org.junit.jupiter.api.parallel.Execution;
8+
import org.junit.jupiter.api.parallel.ExecutionMode;
9+
10+
import java.util.Arrays;
11+
import java.util.List;
12+
13+
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
14+
15+
@Execution(ExecutionMode.SAME_THREAD)
16+
public class PlaywrightPageObjectTest {
17+
18+
protected static Playwright playwright;
19+
protected static Browser browser;
20+
protected static BrowserContext browserContext;
21+
22+
Page page;
23+
24+
@BeforeAll
25+
static void setUpBrowser() {
26+
playwright = Playwright.create();
27+
playwright.selectors().setTestIdAttribute("data-test");
28+
browser = playwright.chromium().launch(
29+
new BrowserType.LaunchOptions().setHeadless(true)
30+
.setArgs(Arrays.asList("--no-sandbox", "--disable-extensions", "--disable-gpu"))
31+
);
32+
}
33+
34+
@BeforeEach
35+
void setUp() {
36+
browserContext = browser.newContext();
37+
page = browserContext.newPage();
38+
}
39+
40+
@AfterEach
41+
void closeContext() {
42+
browserContext.close();
43+
}
44+
45+
@AfterAll
46+
static void tearDown() {
47+
browser.close();
48+
playwright.close();
49+
}
50+
51+
@BeforeEach
52+
void openHomePage() {
53+
page.navigate("https://practicesoftwaretesting.com");
54+
}
55+
56+
@Nested
57+
class WhenSearchingProductsByKeyword {
58+
59+
@DisplayName("Without Page Objects")
60+
@Test
61+
void withoutPageObjects() {
62+
page.getByPlaceholder("Search").fill("tape");
63+
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Search")).click();
64+
page.waitForResponse("**/products/search?q=tape", () -> {
65+
});
66+
67+
assertThat(page.getByTestId("product-name")).hasCount(3);
68+
Assertions.assertThat(page.getByTestId("product-name").allInnerTexts())
69+
.contains("Tape Measure 7.5m", "Measuring Tape", "Tape Measure 5m");
70+
71+
}
72+
73+
static class SearchComponent {
74+
75+
private final Page page;
76+
77+
public SearchComponent(Page page) {
78+
this.page = page;
79+
}
80+
81+
private Locator searchField() {
82+
return page.getByTestId("search-query");
83+
}
84+
85+
private Locator searchButton() {
86+
return page.getByTestId("search-submit");
87+
}
88+
89+
void searchByKeyword(String keyword) {
90+
page.waitForResponse(
91+
"**/products/search*",
92+
() -> {
93+
searchField().fill(keyword);
94+
searchButton().click();
95+
}
96+
);
97+
}
98+
}
99+
100+
record ProductItem(String name, String image, double price) {
101+
}
102+
103+
;
104+
105+
static class ProductList {
106+
private final Page page;
107+
108+
public ProductList(Page page) {
109+
this.page = page;
110+
}
111+
112+
private Locator getProductCards() {
113+
return page.locator(".card");
114+
}
115+
116+
private double extractPrice(Locator priceElement) {
117+
return Double.parseDouble(
118+
priceElement.textContent()
119+
.trim()
120+
.replace("$", "")
121+
);
122+
}
123+
124+
public List<ProductItem> getProducts() {
125+
return getProductCards()
126+
.all()
127+
.stream()
128+
.map(card -> new ProductItem(
129+
card.getByTestId("product-name").innerText(),
130+
card.locator(".card-img-top").getAttribute("src"),
131+
extractPrice(card.getByTestId("product-price"))
132+
))
133+
.toList();
134+
}
135+
136+
public void viewProduct(String productName) {
137+
page.locator(".card").getByText(productName).click();
138+
}
139+
}
140+
141+
@Test
142+
void withPageObjects() {
143+
SearchComponent searchComponent = new SearchComponent(page);
144+
ProductList productList = new ProductList(page);
145+
146+
searchComponent.searchByKeyword("Tape");
147+
List<ProductItem> matchingProducts = productList.getProducts();
148+
149+
Assertions.assertThat(matchingProducts)
150+
.as("Should find correct number of tape measures")
151+
.hasSize(3);
152+
153+
Assertions.assertThat(matchingProducts)
154+
.as("All products should have valid prices")
155+
.allMatch(product -> product.price() > 0.0);
156+
157+
Assertions.assertThat(matchingProducts)
158+
.extracting(ProductItem::name)
159+
.as("Should find all tape measure products")
160+
.containsExactlyInAnyOrder(
161+
"Tape Measure 7.5m",
162+
"Measuring Tape",
163+
"Tape Measure 5m"
164+
);
165+
}
166+
}
167+
168+
@Nested
169+
class WhenAddingItemsToTheCart {
170+
171+
@DisplayName("Without Page Objects")
172+
@Test
173+
void withoutPageObjects() {
174+
page.getByPlaceholder("Search").fill("pliers");
175+
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Search")).click();
176+
page.waitForResponse("**/products/search?q=pliers", () -> {
177+
});
178+
page.locator(".card").getByText("Combination Pliers").click();
179+
page.getByTestId("increase-quantity").click();
180+
page.getByTestId("increase-quantity").click();
181+
page.getByText("Add to cart").click();
182+
page.waitForCondition(() -> page.getByTestId("cart-quantity").textContent().equals("3"));
183+
page.getByTestId("nav-cart").click();
184+
185+
assertThat(page.locator(".product-title").getByText("Combination Pliers")).isVisible();
186+
assertThat(page.getByTestId("cart-quantity").getByText("3")).isVisible();
187+
}
188+
189+
@DisplayName("With Page Objects")
190+
@Test
191+
void withPageObjects() {
192+
WhenSearchingProductsByKeyword.SearchComponent searchComponent = new WhenSearchingProductsByKeyword.SearchComponent(page);
193+
WhenSearchingProductsByKeyword.ProductList productList = new WhenSearchingProductsByKeyword.ProductList(page);
194+
ProductDetails productDetails = new ProductDetails(page);
195+
NavBar navBar = new NavBar(page);
196+
CheckoutCart checkoutCart = new CheckoutCart(page);
197+
198+
searchComponent.searchByKeyword("pliers");
199+
productList.viewProduct("Combination Pliers");
200+
201+
productDetails.addToCart();
202+
navBar.openCart();
203+
204+
List<CheckoutItem> checkoutItems = checkoutCart.getCheckoutItems();
205+
206+
Assertions.assertThat(checkoutItems).hasSize(1);
207+
Assertions.assertThat(checkoutItems.get(0).title()).contains("Combination Pliers");
208+
Assertions.assertThat(checkoutItems.get(0).quantity()).isEqualTo(1);
209+
210+
}
211+
212+
@DisplayName("With Page Objects and multiple items")
213+
@Test
214+
void withPageObjectsAndMultipleItems() {
215+
WhenSearchingProductsByKeyword.SearchComponent searchComponent = new WhenSearchingProductsByKeyword.SearchComponent(page);
216+
WhenSearchingProductsByKeyword.ProductList productList = new WhenSearchingProductsByKeyword.ProductList(page);
217+
ProductDetails productDetails = new ProductDetails(page);
218+
NavBar navBar = new NavBar(page);
219+
CheckoutCart checkoutCart = new CheckoutCart(page);
220+
221+
searchComponent.searchByKeyword("pliers");
222+
productList.viewProduct("Combination Pliers");
223+
224+
productDetails.increaseQuantityBy(2);
225+
productDetails.addToCart();
226+
navBar.openCart();
227+
228+
List<CheckoutItem> checkoutItems = checkoutCart.getCheckoutItems();
229+
230+
Assertions.assertThat(checkoutItems).hasSize(1);
231+
Assertions.assertThat(checkoutItems.get(0).title()).contains("Combination Pliers");
232+
Assertions.assertThat(checkoutItems.get(0).quantity()).isEqualTo(3);
233+
}
234+
235+
236+
@DisplayName("With Page Objects and multiple products")
237+
@Test
238+
void withPageObjectsAndMultipleProducts() {
239+
WhenSearchingProductsByKeyword.SearchComponent searchComponent = new WhenSearchingProductsByKeyword.SearchComponent(page);
240+
WhenSearchingProductsByKeyword.ProductList productList = new WhenSearchingProductsByKeyword.ProductList(page);
241+
ProductDetails productDetails = new ProductDetails(page);
242+
NavBar navBar = new NavBar(page);
243+
CheckoutCart checkoutCart = new CheckoutCart(page);
244+
245+
searchComponent.searchByKeyword("pliers");
246+
productList.viewProduct("Combination Pliers");
247+
productDetails.addToCart();
248+
249+
navBar.openHomePage();
250+
;
251+
productList.viewProduct("Bolt Cutters");
252+
productDetails.addToCart();
253+
254+
navBar.openCart();
255+
256+
List<CheckoutItem> checkoutItems = checkoutCart.getCheckoutItems();
257+
258+
Assertions.assertThat(checkoutItems).hasSize(2);
259+
}
260+
261+
262+
static class ProductDetails {
263+
private final Page page;
264+
265+
ProductDetails(Page page) {
266+
this.page = page;
267+
}
268+
269+
public void increaseQuantityBy(int increment) {
270+
for (int i = 0; i < increment; i++) {
271+
increaseQuantityButton().click();
272+
}
273+
}
274+
275+
public void addToCart() {
276+
page.waitForResponse(
277+
response -> response.url().contains("/carts/") && response.request().method().equals("POST"),
278+
() -> {
279+
addToCartButton().click();
280+
}
281+
);
282+
}
283+
284+
private Locator increaseQuantityButton() {
285+
return page.getByTestId("increase-quantity");
286+
}
287+
288+
private Locator addToCartButton() {
289+
return page.getByText("Add to cart");
290+
}
291+
}
292+
293+
static class NavBar {
294+
private final Page page;
295+
296+
NavBar(Page page) {
297+
this.page = page;
298+
}
299+
300+
public void openCart() {
301+
page.navigate("https://practicesoftwaretesting.com/checkout");
302+
page.waitForSelector(".product-title");
303+
}
304+
305+
public void openHomePage() {
306+
page.navigate("https://practicesoftwaretesting.com");
307+
}
308+
}
309+
310+
static class CheckoutCart {
311+
private final Page page;
312+
313+
CheckoutCart(Page page) {
314+
this.page = page;
315+
}
316+
317+
List<CheckoutItem> getCheckoutItems() {
318+
return page.locator("app-cart tbody tr")
319+
.all()
320+
.stream()
321+
.map(
322+
checkoutItemRow -> {
323+
List<Locator> itemRowCells = checkoutItemRow.locator("td").all();
324+
String title = stripBlankCharactersFrom(checkoutItemRow.locator(".product-title").innerText());
325+
String itemQuantity = checkoutItemRow.locator("td").all().get(1).locator("input").inputValue();
326+
int quantity = Integer.parseInt(itemQuantity);
327+
double itemPrice = extractPrice(itemRowCells.get(2).textContent());
328+
double totalPrice = extractPrice(itemRowCells.get(3).textContent());
329+
return new CheckoutItem(title, quantity, itemPrice, totalPrice);
330+
}
331+
).toList();
332+
}
333+
334+
private String stripBlankCharactersFrom(String value) {
335+
return value.strip().replaceAll("\u00A0+$", "");
336+
}
337+
338+
private double extractPrice(String priceValue) {
339+
return Double.parseDouble(priceValue.replace("$", ""));
340+
}
341+
342+
}
343+
344+
record CheckoutItem(String title, int quantity, double price, double total) {
345+
}
346+
}
347+
}

src/test/java/com/serenitydojo/playwright/PlaywrightWaitsTest.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ static void setUpBrowser() {
2727
playwright = Playwright.create();
2828
playwright.selectors().setTestIdAttribute("data-test");
2929
browser = playwright.chromium().launch(
30-
new BrowserType.LaunchOptions().setHeadless(false)
30+
new BrowserType.LaunchOptions().setHeadless(true)
3131
.setArgs(Arrays.asList("--no-sandbox", "--disable-extensions", "--disable-gpu"))
3232
);
3333
}
@@ -108,6 +108,22 @@ void shouldFilterProductsByCategory() {
108108
Assertions.assertThat(filteredProducts).contains("Sheet Sander", "Belt Sander","Random Orbit Sander");
109109

110110
}
111+
112+
113+
@Test
114+
@DisplayName("Should filter products by category and wait for the API response to return")
115+
void shouldFilterProductsByCategoryAfterTheAPIResponseReturns() {
116+
page.getByPlaceholder("Search").fill("saw");
117+
page.getByPlaceholder("Search").press("Enter");
118+
119+
page.waitForResponse("**/products/search?q=saw", () -> {});
120+
121+
var filteredProducts = page.getByTestId("product-name").allInnerTexts();
122+
123+
Assertions.assertThat(filteredProducts).contains("Wood Saw", "Circular Saw");
124+
125+
}
126+
111127
}
112128

113129
@Nested

0 commit comments

Comments
 (0)