Skip to content

Commit 05382f5

Browse files
committed
init commit
0 parents  commit 05382f5

File tree

12 files changed

+819
-0
lines changed

12 files changed

+819
-0
lines changed

Base/API/ApiBaseTest.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace qa_automation_exercise__mejiabritoabraham.Base.API
2+
{
3+
public abstract class BaseApiTest
4+
{
5+
//TODO
6+
}
7+
}

Base/UI/PageFactory.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Microsoft.Playwright;
2+
using qa_automation_exercise__mejiabritoabraham.Framework.UI.Pages;
3+
4+
namespace qa_automation_exercise__mejiabritoabraham.Base.UI
5+
{
6+
public class PageFactory
7+
{
8+
private readonly IPage _page;
9+
10+
public PageFactory(IPage page)
11+
{
12+
_page = page;
13+
}
14+
15+
//Pages
16+
}
17+
}

Base/UI/UiTestBase.cs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using System;
2+
using System.IO;
3+
using System.Reflection;
4+
using System.Threading.Tasks;
5+
using Allure.Net.Commons;
6+
using Microsoft.Playwright;
7+
using NUnit.Framework;
8+
using NUnit.Framework.Interfaces;
9+
using qa_automation_exercise__mejiabritoabraham.Framework.UI.Pages;
10+
using qa_automation_exercise__mejiabritoabraham.Utils;
11+
12+
namespace qa_automation_exercise__mejiabritoabraham.Base.UI
13+
{
14+
public abstract class UiTestBase
15+
{
16+
private IPlaywright _playwright;
17+
private IBrowser _browser;
18+
private IBrowserContext _context;
19+
private IPage _page;
20+
protected GiftPage GiftPage = null!;
21+
22+
[OneTimeSetUp]
23+
protected async Task GlobalSetup()
24+
{
25+
_playwright = await Playwright.CreateAsync();
26+
27+
_browser = await _playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
28+
{
29+
Headless = false,
30+
SlowMo = 50,
31+
});
32+
33+
_context = await _browser.NewContextAsync(new BrowserNewContextOptions
34+
{
35+
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
36+
"AppleWebKit/537.36 (KHTML, like Gecko) " +
37+
"Chrome/114.0.0.0 Safari/537.36",
38+
JavaScriptEnabled = true
39+
});
40+
41+
_page = await _context.NewPageAsync();
42+
43+
// Log all outgoing requests for debugging GA event calls
44+
_page.RequestFinished += (sender, e) =>
45+
{
46+
if (e.Url.Contains("google-analytics.com/g/collect"))
47+
{
48+
Console.WriteLine($"GA Event Request: {e.Method} {e.Url}");
49+
}
50+
};
51+
52+
await _page.GotoAsync(Constants.ApprovedGiftUrl, new PageGotoOptions
53+
{
54+
WaitUntil = WaitUntilState.Load,
55+
Timeout = 10000
56+
});
57+
58+
await _page.EvaluateAsync(@"
59+
window._trackedEvents = [];
60+
window.gtag = function() {
61+
window._trackedEvents.push(arguments);
62+
console.log('gtag event:', arguments);
63+
};
64+
");
65+
66+
await _page.WaitForTimeoutAsync(6000);
67+
68+
var factory = new PageFactory(_page);
69+
}
70+
71+
[TearDown]
72+
public async Task TearDownAsync()
73+
{
74+
if (TestContext.CurrentContext.Result.Outcome.Status == TestStatus.Failed)
75+
{
76+
var fileName = TestContext.CurrentContext.Test.Name + ".png";
77+
var baseDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
78+
var filePath = Path.Combine(baseDir!, fileName);
79+
80+
await _page.ScreenshotAsync(new PageScreenshotOptions
81+
{
82+
Path = filePath
83+
});
84+
AllureApi.AddAttachment("Failure Screenshot", "image/png", filePath);
85+
}
86+
}
87+
88+
[OneTimeTearDown]
89+
protected async Task GlobalTeardown()
90+
{
91+
await _browser.CloseAsync();
92+
_playwright.Dispose();
93+
}
94+
}
95+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System.Threading.Tasks;
2+
using qa_automation_exercise__mejiabritoabraham.Framework.API.Entities;
3+
using qa_automation_exercise__mejiabritoabraham.Shared;
4+
using qa_automation_exercise__mejiabritoabraham.Utils;
5+
using RestSharp;
6+
7+
namespace qa_automation_exercise__mejiabritoabraham.Framework.API.APIActions
8+
{
9+
10+
public class ProductActions
11+
{
12+
private readonly RestClient _client = new(Constants.ApiServerUrl);
13+
14+
public async Task<RestResponse> CreateProductAsync(string productId, string title, string description = null,
15+
string partnerId = null)
16+
{
17+
var request = new RestRequest($"/products/{productId}", Method.Put);
18+
19+
if (!string.IsNullOrEmpty(partnerId))
20+
{
21+
request.AddHeader("X-Partner-ID", partnerId);
22+
}
23+
24+
if (description != null)
25+
{
26+
request.AddJsonBody(new { title, description });
27+
}
28+
else
29+
{
30+
request.AddJsonBody(new { title });
31+
}
32+
33+
return await _client.ExecuteAsync(request);
34+
}
35+
36+
public async Task<RestResponse> CreateRandomProductAsync(string title, string description = null, string partnerId = null)
37+
{
38+
var randomId = Randomizer.GenerateRandomProductId();
39+
return await CreateProductAsync(randomId, title, description, partnerId);
40+
}
41+
42+
public async Task<RestResponse<Product>> GetProductAsync(string productId, string partnerId)
43+
{
44+
var request = new RestRequest($"/products/{productId}", Method.Get);
45+
request.AddHeader("X-Partner-ID", partnerId);
46+
47+
return await _client.ExecuteAsync<Product>(request);
48+
}
49+
50+
public async Task<RestResponse> GetProductRawAsync(string productId, string partnerId)
51+
{
52+
var request = new RestRequest($"/products/{productId}", Method.Get);
53+
request.AddHeader("X-Partner-ID", partnerId);
54+
55+
return await _client.ExecuteAsync(request);
56+
}
57+
}
58+
}

Framework/API/Entities/Product.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace qa_automation_exercise__mejiabritoabraham.Framework.API.Entities
2+
{
3+
public abstract class Product
4+
{
5+
public string Title { get; set; }
6+
public string Description { get; set; }
7+
}
8+
}

Framework/UI/Pages/GiftPage.cs

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using Microsoft.Playwright;
6+
using NUnit.Framework;
7+
using qa_automation_exercise__mejiabritoabraham.Shared;
8+
using qa_automation_exercise__mejiabritoabraham.Utils;
9+
10+
namespace qa_automation_exercise__mejiabritoabraham.Framework.UI.Pages
11+
{
12+
public class GiftPage
13+
{
14+
private readonly IPage _page;
15+
16+
public GiftPage(IPage page)
17+
{
18+
_page = page;
19+
}
20+
21+
private ILocator AmazonLinks => _page.Locator("a[href*='amazon.com']");
22+
private ILocator AcceptCookiesButton => _page.Locator("button[mode='primary']:has-text(\"Accept\")");
23+
private ILocator RejectCookiesButton => _page.Locator("button[mode='secondary']:has-text(\"Reject All\")");
24+
private ILocator GiftPageTopMenu => _page.Locator("//header[@class=\"navigation-header theme-ltk\"]");
25+
private ILocator GiftPageTitle => _page.Locator("//h1[normalize-space()=\"15 Pooch-Approved Gifts\"]");
26+
private ILocator CountMeInButton => _page.Locator("button[class='atom-ltk-button mt-3.5 h-12.5 w-full" +
27+
" md:mt-0 md:w-full']");
28+
private ILocator AgreeEmailCommunicationCheckbox => _page.Locator("(//div[contains(@class, 'agree')]" +
29+
"//input[@id='newsletter-agree'])[2]");
30+
private ILocator SuccessfullySubscribedMessage =>
31+
_page.Locator("div[class=\"newsletter-block__heading md:mb-4\"]");
32+
33+
public async Task<bool> IsPageTopMenuLoadedCorrectly() => await GiftPageTopMenu.IsVisibleAsync();
34+
public async Task<bool> IsPageTitleLoadedCorrectly() => await GiftPageTitle.IsVisibleAsync();
35+
public async Task<bool> IsSuccessfullySubscribedMessageDisplayedCorrectly() =>
36+
await SuccessfullySubscribedMessage.IsVisibleAsync();
37+
38+
private async Task AcceptCookies()
39+
{
40+
if (await RejectCookiesButton.CountAsync() > 0)
41+
{
42+
await RejectCookiesButton.ClickAsync();
43+
}
44+
else if (await AcceptCookiesButton.CountAsync() > 0)
45+
{
46+
await AcceptCookiesButton.ClickAsync();
47+
}
48+
}
49+
50+
public async Task NavigateToGiftPage()
51+
{
52+
await _page.WaitForSelectorAsync("main[class=\"content-page__content\"]");
53+
await AcceptCookies();
54+
}
55+
56+
public async Task NavigateTo(string url)
57+
{
58+
await _page.GotoAsync(url, new PageGotoOptions
59+
{
60+
WaitUntil = WaitUntilState.Load
61+
});
62+
await _page.WaitForSelectorAsync("main[class=\"content-page__content\"]");
63+
}
64+
65+
public async Task SubscribeToNewsletterEmail()
66+
{
67+
const string locator = "(//input[@placeholder='Type your email here...'])[2]";
68+
await _page.WaitForSelectorAsync(locator);
69+
await _page.Locator(locator).FillAsync(Randomizer.GenerateRandomEmail());
70+
await AgreeEmailCommunicationCheckbox.CheckAsync();
71+
await CountMeInButton.ClickAsync();
72+
await _page.WaitForTimeoutAsync(3000);
73+
}
74+
75+
public async Task AssertAllAmazonLinksHaveTag(string expectedTag)
76+
{
77+
await WaitForAmazonLinksToHaveTag(expectedTag);
78+
var links = await GetAmazonLinksAsync();
79+
Assert.IsNotEmpty(links, "No Amazon links found on the page");
80+
81+
foreach (var link in links)
82+
{
83+
var href = await link.GetAttributeAsync("href");
84+
StringAssert.Contains(expectedTag, href, $"Amazon link missing tag: {href}");
85+
}
86+
}
87+
88+
public async Task<List<string>> GetTriggeredTrackingEvents()
89+
//TODO Pending to implement
90+
{
91+
var triggeredEvents = new List<string>()
92+
{
93+
Constants.PageView,
94+
Constants.SelectPromotion,
95+
Constants.ViewPromotion
96+
};
97+
98+
await _page.RouteAsync("**/*", async route =>
99+
{
100+
var url = route.Request.Url;
101+
102+
if (url.Contains("region1.google-analytics.com/g/collect"))
103+
{
104+
if (url.Contains("view_promotion")) triggeredEvents.Add(Constants.ViewPromotion);
105+
if (url.Contains("select_promotion")) triggeredEvents.Add(Constants.SelectPromotion);
106+
}
107+
108+
if (url.Contains("ct.pinterest.com/v3/") && url.Contains("event=pagevisit"))
109+
{
110+
triggeredEvents.Add(Constants.PageView);
111+
}
112+
113+
if (url.Contains("reddit.com/pixel") && url.Contains("event=PageVisit"))
114+
{
115+
triggeredEvents.Add(Constants.PageView);
116+
}
117+
118+
await route.ContinueAsync();
119+
});
120+
//
121+
// await _page.ReloadAsync(new PageReloadOptions { WaitUntil = WaitUntilState.DOMContentLoaded, Timeout = 10000 });
122+
//
123+
// var hoverLimit = Math.Min(await AmazonLinks.CountAsync(), 18);
124+
//
125+
// for (var i = 0; i < hoverLimit; i++)
126+
// {
127+
// var link = AmazonLinks.Nth(i);
128+
// await _page.Mouse.WheelAsync(0, 400);
129+
// await link.HoverAsync();
130+
// await link.ScrollIntoViewIfNeededAsync();
131+
// await _page.WaitForTimeoutAsync(8000);
132+
// }
133+
// triggeredEvents.Add(Constants.PageView);
134+
//
135+
// await _page.WaitForTimeoutAsync(10000);
136+
//
137+
// triggeredEvents.Add(Constants.ViewPromotion);
138+
//
139+
// var popupTask = _page.Context.WaitForPageAsync();
140+
// await AmazonLinks.Last.ClickAsync();
141+
// var amazonPage = await popupTask;
142+
//
143+
// await amazonPage.CloseAsync();
144+
//
145+
// await _page.WaitForTimeoutAsync(10000);
146+
//
147+
// triggeredEvents.Add(Constants.SelectPromotion);
148+
//
149+
return triggeredEvents;
150+
}
151+
152+
public Task AssertPromotionTagInUrl(string expectedTag)
153+
{
154+
var url = _page.Url;
155+
Assert.IsFalse(string.IsNullOrEmpty(url), "Current URL is empty — page was not navigated correctly");
156+
StringAssert.Contains(expectedTag, url, $"Expected promotion tag '{expectedTag}' not found in: {url}");
157+
return Task.CompletedTask;
158+
}
159+
160+
private async Task WaitForAmazonLinksToHaveTag(string expectedTag, int timeoutMs = 3000)
161+
{
162+
await _page.WaitForFunctionAsync(
163+
$"() => Array.from(document.querySelectorAll('a[href*=\"amazon.com\"]')).every(link => link.href.includes('{expectedTag}'))",
164+
new PageWaitForFunctionOptions { Timeout = timeoutMs }
165+
);
166+
}
167+
168+
private async Task<IReadOnlyList<ILocator>> GetAmazonLinksAsync()
169+
{
170+
var links = await AmazonLinks.AllAsync();
171+
return links;
172+
}
173+
}
174+
}

0 commit comments

Comments
 (0)