using PuppeteerSharp; using Microsoft.AspNetCore.Mvc.Testing; namespace BCards.IntegrationTests.Helpers; public class PuppeteerTestHelper : IAsyncDisposable { private IBrowser? _browser; private IPage? _page; private readonly string _baseUrl; public PuppeteerTestHelper(WebApplicationFactory factory) { _baseUrl = factory.Server.BaseAddress?.ToString() ?? "https://localhost:49178"; } public async Task InitializeAsync() { // Download Chrome if not available await new BrowserFetcher().DownloadAsync(); _browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true, // Set to false for debugging Args = new[] { "--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage", "--disable-web-security", "--allow-running-insecure-content", "--ignore-certificate-errors" } }); _page = await _browser.NewPageAsync(); // Set viewport for consistent testing await _page.SetViewportAsync(new ViewPortOptions { Width = 1920, Height = 1080 }); } public IPage Page => _page ?? throw new InvalidOperationException("PuppeteerTestHelper not initialized. Call InitializeAsync first."); public async Task NavigateToAsync(string relativeUrl) { var fullUrl = new Uri(new Uri(_baseUrl), relativeUrl).ToString(); await Page.GoToAsync(fullUrl, new NavigationOptions { WaitUntil = new[] { WaitUntilNavigation.Networkidle0 } }); } public async Task GetPageContentAsync() { return await Page.GetContentAsync(); } public async Task GetPageTitleAsync() { return await Page.GetTitleAsync(); } public async Task ElementExistsAsync(string selector) { try { await Page.WaitForSelectorAsync(selector, new WaitForSelectorOptions { Timeout = 5000 }); return true; } catch (WaitTaskTimeoutException) { return false; } } public async Task ClickAsync(string selector) { await Page.WaitForSelectorAsync(selector); await Page.ClickAsync(selector); } public async Task TypeAsync(string selector, string text) { await Page.WaitForSelectorAsync(selector); await Page.TypeAsync(selector, text); } public async Task FillFormAsync(Dictionary formData) { foreach (var kvp in formData) { await Page.WaitForSelectorAsync(kvp.Key); await Page.EvaluateExpressionAsync($"document.querySelector('{kvp.Key}').value = ''"); await Page.TypeAsync(kvp.Key, kvp.Value); } } public async Task SubmitFormAsync(string formSelector) { await Page.ClickAsync($"{formSelector} button[type='submit'], {formSelector} input[type='submit']"); } public async Task WaitForNavigationAsync() { await Page.WaitForNavigationAsync(new NavigationOptions { WaitUntil = new[] { WaitUntilNavigation.Networkidle0 } }); } public async Task WaitForElementAsync(string selector, int timeoutMs = 10000) { await Page.WaitForSelectorAsync(selector, new WaitForSelectorOptions { Timeout = timeoutMs }); } public async Task GetElementTextAsync(string selector) { await Page.WaitForSelectorAsync(selector); var element = await Page.QuerySelectorAsync(selector); var text = await Page.EvaluateFunctionAsync("el => el.textContent", element); return text?.Trim() ?? string.Empty; } public async Task GetElementValueAsync(string selector) { await Page.WaitForSelectorAsync(selector); var element = await Page.QuerySelectorAsync(selector); var value = await Page.EvaluateFunctionAsync("el => el.value", element); return value ?? string.Empty; } public async Task IsElementVisibleAsync(string selector) { try { await Page.WaitForSelectorAsync(selector, new WaitForSelectorOptions { Visible = true, Timeout = 2000 }); return true; } catch (WaitTaskTimeoutException) { return false; } } public async Task TakeScreenshotAsync(string fileName) { await Page.ScreenshotAsync(fileName); } public async Task GetCurrentUrlAsync() { return Page.Url; } public async Task> GetAllElementTextsAsync(string selector) { var elements = await Page.QuerySelectorAllAsync(selector); var texts = new List(); foreach (var element in elements) { var text = await Page.EvaluateFunctionAsync("el => el.textContent", element); texts.Add(text?.Trim() ?? string.Empty); } return texts; } public async ValueTask DisposeAsync() { if (_page != null) { await _page.CloseAsync(); } if (_browser != null) { await _browser.CloseAsync(); } } }