Skip to content

Commit 1a6bfaa

Browse files
Stabilized Playwright tests and fixed dialog handling issues
1 parent 23f99a0 commit 1a6bfaa

File tree

12 files changed

+380
-46
lines changed

12 files changed

+380
-46
lines changed

ApiTests/ProductApiTests.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using NUnit.Framework;
2+
using Microsoft.Playwright;
3+
using System.Threading.Tasks;
4+
5+
namespace QA_Automation_Framework_Playwright.ApiTests
6+
{
7+
[TestFixture]
8+
public class ProductApiTests
9+
{
10+
[Test]
11+
public async Task GetProducts_ApiReturns200()
12+
{
13+
using var playwright = await Playwright.CreateAsync();
14+
var requestContext = await playwright.APIRequest.NewContextAsync();
15+
16+
var response = await requestContext.GetAsync("https://fakestoreapi.com/products");
17+
Assert.AreEqual(200, response.Status, "Expected 200 OK from product API.");
18+
19+
string body = await response.TextAsync();
20+
Assert.IsNotEmpty(body, "API response body should not be empty.");
21+
}
22+
}
23+
}

Pages/CartPage.cs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,42 @@ namespace QA_Automation_Framework_Playwright.Pages
66
public class CartPage
77
{
88
private readonly IPage _page;
9-
public CartPage(IPage page) => _page = page;
109

11-
public async Task GoToCartAsync() => await _page.ClickAsync("#cartur");
10+
public CartPage(IPage page)
11+
{
12+
_page = page;
13+
}
1214

13-
public async Task ProceedToCheckoutAsync() =>
14-
await _page.ClickAsync("text=Place Order");
15+
public async Task GoToCartAsync()
16+
{
17+
await _page.ClickAsync("#cartur");
18+
await _page.WaitForURLAsync("**/cart.html", new() { Timeout = 15000 });
19+
await _page.WaitForSelectorAsync("#totalp", new() { Timeout = 15000 });
20+
}
21+
22+
23+
public async Task ProceedToCheckoutAsync()
24+
{
25+
var placeOrderButton = _page.Locator("button.btn.btn-success", new() { HasTextString = "Place Order" });
26+
await placeOrderButton.First.ClickAsync();
27+
await _page.WaitForSelectorAsync("#orderModal", new() { State = WaitForSelectorState.Visible, Timeout = 15000 });
28+
}
29+
30+
31+
public async Task FillCheckoutFormAsync(string name, string country, string city, string card, string month, string year)
32+
{
33+
await _page.FillAsync("#name", name);
34+
await _page.FillAsync("#country", country);
35+
await _page.FillAsync("#city", city);
36+
await _page.FillAsync("#card", card);
37+
await _page.FillAsync("#month", month);
38+
await _page.FillAsync("#year", year);
39+
}
40+
41+
public async Task ConfirmPurchaseAsync()
42+
{
43+
await _page.ClickAsync("button.btn.btn-primary:has-text('Purchase')");
44+
await _page.WaitForSelectorAsync(".sweet-alert", new() { Timeout = 10000 });
45+
}
1546
}
1647
}

Pages/HomePage.cs

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,92 @@
11
using Microsoft.Playwright;
2+
using System;
23
using System.Threading.Tasks;
34

45
namespace QA_Automation_Framework_Playwright.Pages
56
{
67
public class HomePage
78
{
89
private readonly IPage _page;
9-
private readonly string _url = "https://demoblaze.com"; // sample e-commerce site
1010

11-
public HomePage(IPage page) => _page = page;
11+
public HomePage(IPage page)
12+
{
13+
_page = page;
14+
}
15+
16+
public async Task NavigateToHomePageAsync()
17+
{
18+
for (int attempt = 1; attempt <= 2; attempt++)
19+
{
20+
try
21+
{
22+
await _page.GotoAsync("https://www.demoblaze.com/", new()
23+
{
24+
WaitUntil = WaitUntilState.DOMContentLoaded,
25+
Timeout = 15000
26+
});
27+
28+
await _page.WaitForSelectorAsync(".hrefch", new()
29+
{
30+
State = WaitForSelectorState.Visible,
31+
Timeout = 10000
32+
});
33+
34+
// Small delay for stability
35+
await _page.WaitForTimeoutAsync(500);
36+
return; // success
37+
}
38+
catch (TimeoutException) when (attempt < 2)
39+
{
40+
Console.WriteLine($"[Retry] Home page load attempt {attempt} failed, retrying...");
41+
}
42+
}
43+
}
44+
45+
public async Task ClickProductAsync(string productName)
46+
{
47+
// Wait for the page to be interactive
48+
await _page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);
49+
50+
// Wait explicitly until product cards are loaded dynamically
51+
await _page.WaitForFunctionAsync(
52+
@"() => Array.from(document.querySelectorAll('.hrefch')).length >= 3",
53+
null,
54+
new() { Timeout = 60000 } // allow more time for GitHub runners
55+
);
1256

13-
public async Task NavigateAsync() => await _page.GotoAsync(_url);
57+
// Wait until the desired product is visible
58+
var productLocator = _page.Locator($".hrefch:has-text('{productName}')").First;
59+
for (int attempt = 1; attempt <= 3; attempt++)
60+
{
61+
if (await productLocator.IsVisibleAsync())
62+
{
63+
await productLocator.ScrollIntoViewIfNeededAsync();
64+
await productLocator.ClickAsync();
65+
await _page.WaitForSelectorAsync("text=Add to cart", new() { Timeout = 20000 });
66+
return;
67+
}
68+
69+
Console.WriteLine($"[Retry {attempt}] Product '{productName}' not visible, refreshing...");
70+
await _page.ReloadAsync();
71+
await _page.WaitForTimeoutAsync(2000);
72+
}
1473

15-
public async Task ClickProductAsync(string productName)
74+
throw new TimeoutException($"❌ Product '{productName}' not found after 3 retries.");
75+
}
76+
77+
78+
public async Task AddToCartAsync()
1679
{
17-
await _page.ClickAsync($"text={productName}");
80+
var addToCartButton = _page.Locator("text=Add to cart");
81+
82+
// Handle alert dialogs
83+
_page.Dialog += async (_, dialog) =>
84+
{
85+
await dialog.AcceptAsync();
86+
};
87+
88+
await addToCartButton.ClickAsync();
89+
await _page.WaitForTimeoutAsync(1000);
1890
}
1991
}
2092
}

Pages/ProductPage.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,21 @@ public class ProductPage
1010

1111
public async Task AddToCartAsync()
1212
{
13-
await _page.ClickAsync("text=Add to cart");
14-
await _page.WaitForTimeoutAsync(1000);
15-
await _page.Dialog.AcceptAsync();
13+
var addToCartButton = _page.Locator("text=Add to cart");
14+
15+
// One-time handler to avoid "dialog already handled" crashes
16+
void DialogHandler(object? sender, IDialog dialog)
17+
{
18+
_ = dialog.AcceptAsync(); // fire-and-forget acceptance
19+
_page.Dialog -= DialogHandler; // detach immediately
20+
}
21+
22+
_page.Dialog += DialogHandler;
23+
24+
await addToCartButton.ClickAsync();
25+
26+
// Allow dialog to appear and disappear
27+
await _page.WaitForTimeoutAsync(1500);
1628
}
1729
}
1830
}

QA_Automation_Framework_Playwright.csproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,26 @@
1616
<PackageReference Include="NUnit" Version="3.14.0" />
1717
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
1818
<PackageReference Include="NUnit3TestAdapter" Version="5.2.0" />
19+
<PackageReference Include="RazorEngine.NetCore" Version="3.1.0" />
20+
<PackageReference Include="System.Reactive" Version="6.0.0" />
1921
</ItemGroup>
2022

2123
<ItemGroup>
2224
<Using Include="NUnit.Framework" />
2325
</ItemGroup>
2426

27+
<ItemGroup>
28+
<None Update="TestData\Products.json">
29+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
30+
</None>
31+
</ItemGroup>
32+
33+
<ItemGroup>
34+
<Reference Include="ExtentReports">
35+
<HintPath>lib\ExtentReports.dll</HintPath>
36+
</Reference>
37+
</ItemGroup>
38+
2539
</Project>
40+
41+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Microsoft Visual Studio Solution File, Format Version 12.00
2+
# Visual Studio Version 17
3+
VisualStudioVersion = 17.5.2.0
4+
MinimumVisualStudioVersion = 10.0.40219.1
5+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QA_Automation_Framework_Playwright", "QA_Automation_Framework_Playwright.csproj", "{D1A76479-B8B8-F872-1D97-F74662195975}"
6+
EndProject
7+
Global
8+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
9+
Debug|Any CPU = Debug|Any CPU
10+
Release|Any CPU = Release|Any CPU
11+
EndGlobalSection
12+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
13+
{D1A76479-B8B8-F872-1D97-F74662195975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
14+
{D1A76479-B8B8-F872-1D97-F74662195975}.Debug|Any CPU.Build.0 = Debug|Any CPU
15+
{D1A76479-B8B8-F872-1D97-F74662195975}.Release|Any CPU.ActiveCfg = Release|Any CPU
16+
{D1A76479-B8B8-F872-1D97-F74662195975}.Release|Any CPU.Build.0 = Release|Any CPU
17+
EndGlobalSection
18+
GlobalSection(SolutionProperties) = preSolution
19+
HideSolutionNode = FALSE
20+
EndGlobalSection
21+
GlobalSection(ExtensibilityGlobals) = postSolution
22+
SolutionGuid = {D9D765D2-A291-45D5-834E-ED63DDD3AD0E}
23+
EndGlobalSection
24+
EndGlobal

TestData/Products.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"Products": [
3+
"Samsung galaxy s6",
4+
"Nokia lumia 1520",
5+
"HTC One M9"
6+
]
7+
}

Tests/CartTests.cs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using NUnit.Framework;
2+
using Microsoft.Playwright;
3+
using QA_Automation_Framework_Playwright.Pages;
4+
using QA_Automation_Framework_Playwright.Utilities;
5+
using System.Threading.Tasks;
6+
7+
namespace QA_Automation_Framework_Playwright.Tests
8+
{
9+
[TestFixture]
10+
public class CartTests
11+
{
12+
private IPage _page;
13+
14+
[SetUp]
15+
public async Task Setup()
16+
{
17+
_page = await BrowserFactory.CreatePageAsync();
18+
}
19+
20+
[Test, TestCaseSource(typeof(TestDataHelper), nameof(TestDataHelper.LoadProducts))]
21+
public async Task AddProductToCart_DataDriven(string productName)
22+
{
23+
var home = new HomePage(_page);
24+
var product = new ProductPage(_page);
25+
var cart = new CartPage(_page);
26+
27+
await home.NavigateToHomePageAsync();
28+
await home.ClickProductAsync(productName);
29+
await product.AddToCartAsync();
30+
await cart.GoToCartAsync();
31+
32+
Assert.True(await _page.IsVisibleAsync($"text={productName}"),
33+
$"{productName} not found in cart");
34+
}
35+
36+
[Test]
37+
public async Task AddMultipleProductsToCart()
38+
{
39+
var home = new HomePage(_page);
40+
var product = new ProductPage(_page);
41+
var cart = new CartPage(_page);
42+
43+
await home.NavigateToHomePageAsync();
44+
await home.ClickProductAsync("Samsung galaxy s6");
45+
await product.AddToCartAsync();
46+
await _page.GotoAsync("https://www.demoblaze.com/");
47+
await _page.WaitForSelectorAsync(".hrefch", new() { Timeout = 30000 });
48+
await home.ClickProductAsync("Nokia lumia 1520");
49+
await product.AddToCartAsync();
50+
await cart.GoToCartAsync();
51+
52+
Assert.True(await _page.IsVisibleAsync("text=Samsung galaxy s6"));
53+
Assert.True(await _page.IsVisibleAsync("text=Nokia lumia 1520"));
54+
}
55+
56+
[Test]
57+
public async Task RemoveItemFromCart()
58+
{
59+
var home = new HomePage(_page);
60+
var product = new ProductPage(_page);
61+
var cart = new CartPage(_page);
62+
63+
await home.NavigateToHomePageAsync();
64+
await home.ClickProductAsync("Samsung galaxy s6");
65+
await product.AddToCartAsync();
66+
await cart.GoToCartAsync();
67+
68+
// Assuming demoblaze cart has delete link
69+
await _page.ClickAsync("text=Delete");
70+
await _page.WaitForTimeoutAsync(2000);
71+
72+
bool stillExists = await _page.IsVisibleAsync("text=Samsung galaxy s6");
73+
Assert.False(stillExists, "Product was not removed from cart.");
74+
}
75+
76+
[OneTimeTearDown]
77+
public async Task TearDownAll()
78+
{
79+
await BrowserFactory.CloseAsync();
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)