I m new on Playwright and specflow, i would like to know what s the best approach for testing scenario on all browser (chromium, firefox, webkit). The current project used Selenium, but now we want to use playwright to achieve this. Do you have any idea of how to achieve this in an efficient way ? Thank you !
I tried to use tags @chromium @firefox @webkit on scenario and create a hook class :
using TechTalk.SpecFlow;
using System;
using Microsoft.Playwright;
using System.Linq;
using BoDi;
using NLog.Targets;
namespace mynamespace.Steps
{
[Binding]
public class Hooks : IDisposable
{
private readonly IObjectContainer _container;
private readonly IPlaywright _playwright;
public Hooks(IObjectContainer container, IPlaywright playwright)
{
this._container = container;
this._playwright = playwright;
}
[BeforeScenario(Order = 0)]
public void InitializePlaywrightContext()
{
// Get the browser types from the command-line parameters
var browserTypes = GetBrowserTypesFromCommandLine();
var iBrowserTypes = browserTypes.Cast<IBrowserType>().ToArray();
// Initialize the Playwright context and register it in the ObjectContainer
var playwrightContext = new PlaywrightContext(iBrowserTypes);
_container.RegisterInstanceAs(playwrightContext);
}
private IBrowserType[] GetBrowserTypesFromCommandLine()
{
var browserArg = Environment.GetEnvironmentVariable("browser");
if (string.IsNullOrEmpty(browserArg))
{
var chromium = BrowserType.Chromium.ToArray();
// If no browser argument is specified, default to Chromium
return new IBrowserType[] { _playwright.Chromium };
}
else
{
// Split the browserArg by commas to get multiple browsers
var browserNames = browserArg.ToLower().Split(new char[] { , }, StringSplitOptions.RemoveEmptyEntries);
// Map the browser names to corresponding BrowserType
IBrowserType[] browserTypes = browserNames.Select(browserName =>
{
return browserName switch
{
"chromium" => _playwright.Chromium,
"firefox" => _playwright.Firefox,
"webkit" => _playwright.Webkit,
_ => _playwright.Chromium,// If an invalid browser name is specified, default to Chromium
};
}).ToArray();
return browserTypes;
}
}
public void Dispose()
{
_container?.Dispose();
}
}
}
Then create a playwright context :
using Microsoft.Playwright;
using BoDi;
using System.Collections.Generic;
using System;
using System.Threading.Tasks;
public class PlaywrightContext
{
private readonly IBrowserType[] browserTypes;
private readonly IPage[] pages;
public IBrowser[] Browsers { get; private set; }
public IPage[] Pages { get; private set; }
public PlaywrightContext(IObjectContainer container, IBrowserType[] browserTypes)
{
this.browserTypes = browserTypes;
Initialize(container);
}
private void Initialize(IObjectContainer container)
{
Browsers = new IBrowser[browserTypes.Length];
Pages = new IPage[browserTypes.Length];
for (int i = 0; i < browserTypes.Length; i++)
{
Browsers[i] = browserTypes[i].LaunchAsync().GetAwaiter().GetResult();
Pages[i] = Browsers[i].NewPageAsync().GetAwaiter().GetResult();
container.RegisterInstanceAs(Browsers[i]);
container.RegisterInstanceAs(Pages[i]);
}
}
public IBrowser GetBrowserForBrand(string branding)
{
// Implement logic to return the corresponding browser based on branding
// For example:
if (branding == "Chrome")
{
return Browsers[0]; // Assuming Chrome is the first browser in the array
}
else if (branding == "Firefox")
{
return Browsers[1]; // Assuming Firefox is the second browser in the array
}
else if (branding == "Webkit")
{
return Browsers[2]; // Assuming Webkit is the third browser in the array
}
else
{
throw new ArgumentException($"Invalid branding: {branding}");
}
}
public async ValueTask DisposeAsync()
{
foreach (var browser in Browsers)
{
await browser?.CloseAsync();
}
}
}
But can t figure out how to use it in my step class :
namespace mynamespace.Steps
{
[Binding]
public sealed class TransactionStepsPlaywright
{
private readonly ScenarioContext _scenarioContext;
private readonly ITransactionService _transactionService;
private readonly ApiSettings _apiSettings;
private readonly ILoggingService _loggingService;
private readonly int _maxRetries = 3;
private readonly INavigationPane _navigationPane;
private readonly IOtpService _otpService;
private readonly IObjectContainer _container;
private readonly PlaywrightContext _playwrightContext;
private readonly IPlaywright _playwright; // Add IPlaywright dependency
public TransactionStepsPlaywright(ScenarioContext scenarioContext, PlaywrightContext playwrightContext, IObjectContainer container, ILoggingService loggingService, ITransactionService transactionService, IOtpService otpService, IPlaywright playwright, IOptions<ApiSettings> apiSettingsOption, INavigationPane navigationPane) // Add IPlaywright here
{
_scenarioContext = scenarioContext;
_transactionService = transactionService;
_playwright = playwright; // Use IPlaywright here
_apiSettings = apiSettingsOption.Value;
_loggingService = loggingService;
_navigationPane = navigationPane;
_otpService = otpService;
_container = container;
_playwrightContext = playwrightContext;
}
[Given(@"pw I open the login page for (.*)")]
public void GivenIOpenTheLoginPageFor(string branding)
{
try
{
var url = _apiSettings.EndPoints.FirstOrDefault(
ep => string.Compare(ep.Name, branding, StringComparison.InvariantCultureIgnoreCase) == 0
).Url;
var browser = _playwrightContext.GetBrowserForBrand(branding); // Get the corresponding browser for the given branding
var page = browser.NewPageAsync().GetAwaiter().GetResult(); // Create a new page for the browser
page.GotoAsync(url).GetAwaiter().GetResult();
Console.WriteLine($"Opened the login page for branding: {branding} in {browser.Name}.");
}
catch (Exception)
{
throw new Exception($"Could not navigate to login page for branding: {branding}.");
}
}