Skip to content

Commit

Permalink
Upgrade Playwright (#38414)
Browse files Browse the repository at this point in the history
* Upgrade `playwright-sharp-tool`

https://www.nuget.org/packages/playwright-sharp-tool

Contributes towards #30882.

* Update to latest v1.16.1 from GitHub

https://github.com/microsoft/playwright-dotnet/releases/tag/v1.16.1

* Update to use Microsoft.Playwright

* Update references to reflect `Microsoft.Playwright`

* Update dotnet-tools.json

* Upgrade System.CommandLine

* Adapt to breaking API changes in Playwright

* Upgrade dotnet-serve

* Add Microsoft.Playwright.CLI

* Revert "Upgrade System.CommandLine"

This reverts commit cc46e9c.

* Fix build

The build failed. Fix the build errors and run again.
C:\h\w\AA180980\w\9ED808A4\e\RunTests\RunTests.csproj : warning NU1604: Project dependency Microsoft.Playwright does not contain an inclusive lower bound. Include a lower bound in the dependency version to ensure consistent restore results.
C:\h\w\AA180980\w\9ED808A4\e\RunTests\TestRunner.cs(124,35): error CS7036: There is no argument given that corresponds to the required formal parameter 'arguments' of 'ProcessUtil.RunAsync(string, string, string?, string?, bool, IDictionary<string, string?>?, Action<string>?, Action<string>?, Action<int>?, CancellationToken)' [C:\h\w\AA180980\w\9ED808A4\e\RunTests\RunTests.csproj]

* Remove semaphore for websocket events

* Update playwrightSettings based on breaking changes

* npx based playwright install

* npm install playwright

* Install node in helix env

* Use DOTNET_ROLL_FORWARD with `dotnet` `playwright` tool

* Cleanup

* Update playwright tool usage

* DOTNET_ROLL_FORWARD in TestRunner

* Specify project dir

* Playwright install without cli

* Update TestRunner.cs

* Update TestRunner.cs

* Re-enable helix playwright tests

* Upgrade Playwright to 1.17.3, CLI to 1.2.2

* Fix spacing

* Remove DOTNET_ROLL_FORWARD

* PR Feedback

* Remove temp project workaround.

* Fix package lowerbound issue

* Update Versions.props

* Update BrowserManager.cs

* Remove `Installing Microsoft.Playwright.CLI`

* Disable playwright tests in helix
  • Loading branch information
TanayParikh committed Jan 12, 2022
1 parent b4dc215 commit 3129207
Show file tree
Hide file tree
Showing 19 changed files with 158 additions and 181 deletions.
8 changes: 4 additions & 4 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
"isRoot": true,
"tools": {
"dotnet-serve": {
"version": "1.7.139",
"version": "1.8.15",
"commands": [
"dotnet-serve"
]
},
"playwright-sharp-tool": {
"version": "0.170.2",
"Microsoft.Playwright.CLI": {
"version": "1.2.2",
"commands": [
"playwright-sharp"
"playwright"
]
}
}
Expand Down
2 changes: 1 addition & 1 deletion eng/Dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ and are generated based on the last package release.
<LatestPackageReference Include="NuGet.Frameworks" />
<LatestPackageReference Include="NuGet.Versioning" />
<LatestPackageReference Include="Photino.NET" />
<LatestPackageReference Include="PlaywrightSharp" />
<LatestPackageReference Include="Microsoft.Playwright" />
<LatestPackageReference Include="Polly" />
<LatestPackageReference Include="Polly.Extensions.Http" />
<LatestPackageReference Include="Selenium.Support" />
Expand Down
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@
<NewtonsoftJsonVersion>13.0.1</NewtonsoftJsonVersion>
<NSwagApiDescriptionClientVersion>13.0.4</NSwagApiDescriptionClientVersion>
<PhotinoNETVersion>1.1.6</PhotinoNETVersion>
<PlaywrightSharpVersion>0.192.0</PlaywrightSharpVersion>
<MicrosoftPlaywrightVersion>1.17.3</MicrosoftPlaywrightVersion>
<PollyExtensionsHttpVersion>3.0.0</PollyExtensionsHttpVersion>
<PollyVersion>7.2.2</PollyVersion>
<SeleniumSupportVersion>4.1.0</SeleniumSupportVersion>
Expand Down
2 changes: 1 addition & 1 deletion eng/helix/content/RunTests/RunTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@

<ItemGroup>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20158.1" />
<PackageReference Condition=" '$(InstallPlaywright)' == 'true' " Include="PlaywrightSharp" Version="0.192.0" />
<PackageReference Condition=" '$(InstallPlaywright)' == 'true' " Include="Microsoft.Playwright" Version="1.17.3" />
</ItemGroup>
</Project>
11 changes: 5 additions & 6 deletions eng/helix/content/RunTests/TestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using System.Threading;
using System.Threading.Tasks;
#if INSTALLPLAYWRIGHT
using PlaywrightSharp;
using Microsoft.Playwright;
#endif

namespace RunTests
Expand Down Expand Up @@ -55,9 +55,6 @@ public bool SetupEnvironment()
var playwrightBrowsers = Environment.GetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH");
Console.WriteLine($"Setting PLAYWRIGHT_BROWSERS_PATH: {playwrightBrowsers}");
EnvironmentVariables.Add("PLAYWRIGHT_BROWSERS_PATH", playwrightBrowsers);
var playrightDriver = Environment.GetEnvironmentVariable("PLAYWRIGHT_DRIVER_PATH");
Console.WriteLine($"Setting PLAYWRIGHT_DRIVER_PATH: {playrightDriver}");
EnvironmentVariables.Add("PLAYWRIGHT_DRIVER_PATH", playrightDriver);
#else
Console.WriteLine($"Skipping setting PLAYWRIGHT_BROWSERS_PATH");
#endif
Expand Down Expand Up @@ -112,8 +109,10 @@ public async Task<bool> InstallPlaywrightAsync()
{
try
{
Console.WriteLine($"Installing Playwright to Browsers: {Environment.GetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH")} Driver: {Environment.GetEnvironmentVariable("PLAYWRIGHT_DRIVER_PATH")}");
await Playwright.InstallAsync(Environment.GetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH"), Environment.GetEnvironmentVariable("PLAYWRIGHT_DRIVER_PATH"));
Console.WriteLine($"Installing Playwright Browsers to {Environment.GetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH")}");

var exitCode = Microsoft.Playwright.Program.Main(new[] { "install" });

DisplayContents(Environment.GetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH"));
return true;
}
Expand Down
1 change: 0 additions & 1 deletion eng/helix/content/runtests.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ set DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
set DOTNET_MULTILEVEL_LOOKUP=0
set InstallPlaywright=%$installPlaywright%
set PLAYWRIGHT_BROWSERS_PATH=%CD%\ms-playwright
set PLAYWRIGHT_DRIVER_PATH=%CD%\.playwright\win-x64\native\playwright.cmd

set "PATH=%HELIX_WORKITEM_ROOT%;%PATH%;%HELIX_WORKITEM_ROOT%\node\bin"
echo Set path to: "%PATH%"
Expand Down
1 change: 0 additions & 1 deletion eng/helix/content/runtests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export PATH="$PATH:$DIR:$DIR/node/bin"
# Set playwright stuff
export PLAYWRIGHT_BROWSERS_PATH="$DIR/ms-playwright"
if [[ "$helixQueue" == *"OSX"* ]]; then
export PLAYWRIGHT_DRIVER_PATH="$DIR/.playwright/osx/native/playwright.sh"
PLAYWRIGHT_NODE_PATH=$DIR/.playwright/osx/native/node
else
export PLAYWRIGHT_DRIVER_PATH="$DIR/.playwright/unix/native/playwright.sh"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.BrowserTesting;
using Microsoft.AspNetCore.Testing;
using PlaywrightSharp;
using Microsoft.Playwright;
using ProjectTemplates.Tests.Infrastructure;
using Templates.Test.Helpers;
using Xunit;
Expand Down Expand Up @@ -136,20 +136,31 @@ public async Task BlazorServerTemplateWorks_IndividualAuth(BrowserKind browserKi

private async Task TestBasicNavigation(Project project, IPage page)
{
var socket = BrowserContextInfo.Pages[page].WebSockets.SingleOrDefault() ??
(await page.WaitForEventAsync(PageEvent.WebSocket)).WebSocket;
var socket = await page.WaitForWebSocketAsync();

var framesReceived = 0;
var framesSent = 0;

void FrameReceived(object sender, IWebSocketFrame frame) { framesReceived++; }
void FrameSent(object sender, IWebSocketFrame frame) { framesSent++; }

socket.FrameReceived += FrameReceived;
socket.FrameSent += FrameSent;

// Receive render batch
await socket.WaitForEventAsync(WebSocketEvent.FrameReceived);
await socket.WaitForEventAsync(WebSocketEvent.FrameSent);
await page.WaitForWebSocketAsync(new() { Predicate = (s) => framesReceived == 1 });
await page.WaitForWebSocketAsync(new() { Predicate = (s) => framesSent == 1 });

// JS interop call to intercept navigation
await socket.WaitForEventAsync(WebSocketEvent.FrameReceived);
await socket.WaitForEventAsync(WebSocketEvent.FrameSent);
await page.WaitForWebSocketAsync(new() { Predicate = (s) => framesReceived == 2 });
await page.WaitForWebSocketAsync(new() { Predicate = (s) => framesSent == 2 });

socket.FrameReceived -= FrameReceived;
socket.FrameSent -= FrameSent;

await page.WaitForSelectorAsync("nav");
// <title> element gets project ID injected into it during template execution
Assert.Equal("Index", (await page.GetTitleAsync()).Trim());
Assert.Equal("Index", (await page.TitleAsync()).Trim());

// Initially displays the home page
await page.WaitForSelectorAsync("h1 >> text=Hello, world!");
Expand All @@ -168,7 +179,7 @@ private async Task TestBasicNavigation(Project project, IPage page)

// Asynchronously loads and displays the table of weather forecasts
await page.WaitForSelectorAsync("table>tbody>tr");
Assert.Equal(5, (await page.QuerySelectorAllAsync("p+table>tbody>tr")).Count());
Assert.Equal(5, await page.Locator("p+table>tbody>tr").CountAsync());
}

[Theory]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<!-- https://github.com/dotnet/aspnetcore/issues/38818 -->
<BuildHelixPayload>false</BuildHelixPayload>

<!-- Properties that affect test runs -->
<!-- TestTemplateCreationFolder is the folder where the templates will be created. Will point out to $(OutputDir)$(TestTemplateCreationFolder) -->
<TestTemplateCreationFolder>TestTemplates\</TestTemplateCreationFolder>
Expand Down Expand Up @@ -47,8 +47,8 @@
<Reference Include="Microsoft.Extensions.Configuration.Json" />
<Reference Include="AngleSharp" />
<Reference Include="System.Net.Http" />
<Reference Include="PlaywrightSharp" Condition="'$(IsPlaywrightAvailable)' == 'true'" />
<Reference Include="PlaywrightSharp" ExcludeAssets="build" Condition="'$(IsPlaywrightAvailable)' != 'true'" />
<Reference Include="Microsoft.Playwright" Condition="'$(IsPlaywrightAvailable)' == 'true'" />
<Reference Include="Microsoft.Playwright" ExcludeAssets="build" Condition="'$(IsPlaywrightAvailable)' != 'true'" />
<ProjectReference Include="$(RepoRoot)src\Framework\App.Runtime\src\Microsoft.AspNetCore.App.Runtime.csproj"
Private="false"
ReferenceOutputAssembly="false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.CommandLineUtils;
using Newtonsoft.Json.Linq;
using PlaywrightSharp;
using Microsoft.Playwright;
using Templates.Test.Helpers;

namespace BlazorTemplates.Tests;
Expand Down Expand Up @@ -56,7 +56,7 @@ public async Task BlazorWasmStandaloneTemplate_Works(BrowserKind browserKind)
private static async Task<IPage> NavigateToPage(IBrowserContext browser, string listeningUri)
{
var page = await browser.NewPageAsync();
await page.GoToAsync(listeningUri, LifecycleEvent.Networkidle);
await page.GotoAsync(listeningUri, new() { WaitUntil = WaitUntilState.NetworkIdle });
return page;
}

Expand Down Expand Up @@ -133,9 +133,9 @@ public async Task BlazorWasmStandalonePwaTemplate_Works(BrowserKind browserKind)

// The PWA template supports offline use. By now, the browser should have cached everything it needs,
// so we can continue working even without the server.
await page.GoToAsync("about:blank");
await page.GotoAsync("about:blank");
await browser.SetOfflineAsync(true);
await page.GoToAsync(listeningUri);
await page.GotoAsync(listeningUri);
await TestBasicNavigation(project.ProjectName, page, skipFetchData: true);
await page.CloseAsync();
}
Expand Down Expand Up @@ -181,9 +181,9 @@ public async Task BlazorWasmHostedPwaTemplate_Works(BrowserKind browserKind)
// The PWA template supports offline use. By now, the browser should have cached everything it needs,
// so we can continue working even without the server.
// Since this is the hosted project, backend APIs won't work offline, so we need to skip "fetchdata"
await page.GoToAsync("about:blank");
await page.GotoAsync("about:blank");
await browser.SetOfflineAsync(true);
await page.GoToAsync(listeningUri);
await page.GotoAsync(listeningUri);
await TestBasicNavigation(project.ProjectName, page, skipFetchData: true);
await page.CloseAsync();
}
Expand Down Expand Up @@ -416,11 +416,11 @@ private static async Task TestBasicNavigation(string appName, IPage page, bool u
// Initially displays the home page
await page.WaitForSelectorAsync("h1 >> text=Hello, world!");

Assert.Equal("Index", (await page.GetTitleAsync()).Trim());
Assert.Equal("Index", (await page.TitleAsync()).Trim());

// Can navigate to the counter page
await Task.WhenAll(
page.WaitForNavigationAsync("**/counter"),
page.WaitForNavigationAsync(new() { UrlString = "**/counter" }),
page.WaitForSelectorAsync("h1 >> text=Counter"),
page.WaitForSelectorAsync("p >> text=Current count: 0"),
page.ClickAsync("a[href=counter]"));
Expand All @@ -433,12 +433,12 @@ private static async Task TestBasicNavigation(string appName, IPage page, bool u
if (usesAuth)
{
await Task.WhenAll(
page.WaitForNavigationAsync("**/Identity/Account/Login**", LifecycleEvent.Networkidle),
page.WaitForNavigationAsync(new() { UrlString = "**/Identity/Account/Login**", WaitUntil = WaitUntilState.NetworkIdle }),
page.ClickAsync("text=Log in"));

await Task.WhenAll(
page.WaitForSelectorAsync("[name=\"Input.Email\"]"),
page.WaitForNavigationAsync("**/Identity/Account/Register**", LifecycleEvent.Networkidle),
page.WaitForNavigationAsync(new() { UrlString = "**/Identity/Account/Register**", WaitUntil = WaitUntilState.NetworkIdle }),
page.ClickAsync("text=Register as a new user"));

var userName = $"{Guid.NewGuid()}@example.com";
Expand All @@ -450,12 +450,12 @@ private static async Task TestBasicNavigation(string appName, IPage page, bool u

// We will be redirected to the RegisterConfirmation
await Task.WhenAll(
page.WaitForNavigationAsync("**/Identity/Account/RegisterConfirmation**", LifecycleEvent.Networkidle),
page.WaitForNavigationAsync(new() { UrlString = "**/Identity/Account/RegisterConfirmation**", WaitUntil = WaitUntilState.NetworkIdle }),
page.ClickAsync("#registerSubmit"));

// We will be redirected to the ConfirmEmail
await Task.WhenAll(
page.WaitForNavigationAsync("**/Identity/Account/ConfirmEmail**", LifecycleEvent.Networkidle),
page.WaitForNavigationAsync(new() { UrlString = "**/Identity/Account/ConfirmEmail**", WaitUntil = WaitUntilState.NetworkIdle }),
page.ClickAsync("text=Click here to confirm your account"));

// Now we can login
Expand All @@ -466,21 +466,21 @@ private static async Task TestBasicNavigation(string appName, IPage page, bool u
await page.ClickAsync("#login-submit");

// Need to navigate to fetch page
await page.GoToAsync(new Uri(page.Url).GetLeftPart(UriPartial.Authority));
Assert.Equal(appName.Trim(), (await page.GetTitleAsync()).Trim());
await page.GotoAsync(new Uri(page.Url).GetLeftPart(UriPartial.Authority));
Assert.Equal(appName.Trim(), (await page.TitleAsync()).Trim());
}

if (!skipFetchData)
{
// Can navigate to the 'fetch data' page
await Task.WhenAll(
page.WaitForNavigationAsync("**/fetchdata"),
page.WaitForNavigationAsync(new() { UrlString = "**/fetchdata" }),
page.WaitForSelectorAsync("h1 >> text=Weather forecast"),
page.ClickAsync("text=Fetch data"));

// Asynchronously loads and displays the table of weather forecasts
await page.WaitForSelectorAsync("table>tbody>tr");
Assert.Equal(5, (await page.QuerySelectorAllAsync("p+table>tbody>tr")).Count());
Assert.Equal(5, await page.Locator("p+table>tbody>tr").CountAsync());
}
}

Expand Down
15 changes: 3 additions & 12 deletions src/ProjectTemplates/BlazorTemplates.Tests/playwrightSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,18 @@
"BaseArtifactsFolder": ".",
"GlobalBrowserOptions": {
"ChromiumSandbox": true,
"DumpIO": true,
"IgnoreHTTPSErrors": true,
"Headless": true,
"Timeout": 30000
},
"GlobalContextOptions": {
"RecordVideo": {
"Dir": "videos"
},
"RecordHar": {
"Path": "har"
},
"RecordVideoDir": "videos",
"RecordHarPath": "har",
"IgnoreHTTPSErrors": true
},
"BrowserOptions": {
"Chromium": {
"BrowserKind": "Chromium",
"IsEnabled": true,
"Args": {
"--ignore-certificate-errors": true
}
"IsEnabled": true
},
"Firefox": {
"BrowserKind": "Firefox",
Expand Down
4 changes: 2 additions & 2 deletions src/ProjectTemplates/Shared/AspNetProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
using Microsoft.Extensions.CommandLineUtils;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using PlaywrightSharp;
using Microsoft.Playwright;
using Xunit;
using Xunit.Abstractions;

Expand Down Expand Up @@ -109,7 +109,7 @@ public class AspNetProcess : IDisposable
public async Task VisitInBrowserAsync(IPage page)
{
_output.WriteLine($"Opening browser at {ListeningUri}...");
await page.GoToAsync(ListeningUri.AbsoluteUri);
await page.GotoAsync(ListeningUri.AbsoluteUri);
}


Expand Down
21 changes: 7 additions & 14 deletions src/Shared/BrowserTesting/src/BrowserManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using PlaywrightSharp;
using Microsoft.Playwright;

namespace Microsoft.AspNetCore.BrowserTesting;

Expand Down Expand Up @@ -47,20 +47,12 @@ private async Task InitializeAsync()

async Task InitializeCore()
{
var driverPath = Environment.GetEnvironmentVariable("PLAYWRIGHT_DRIVER_PATH");
if (!string.IsNullOrEmpty(driverPath))
{
Playwright = await PlaywrightSharp.Playwright.CreateAsync(_loggerFactory, driverExecutablePath: driverPath, debug: "pw:api");
}
else
{
Playwright = await PlaywrightSharp.Playwright.CreateAsync(_loggerFactory, debug: "pw:api");
}
Playwright = await Microsoft.Playwright.Playwright.CreateAsync();
foreach (var (browserName, options) in _browserManagerConfiguration.BrowserOptions)
{
if (!_launchBrowsers.ContainsKey(browserName))
{
var effectiveLaunchOptions = _browserManagerConfiguration.GetLaunchOptions(options.BrowserLaunchOptions);
var effectiveLaunchOptions = _browserManagerConfiguration.GetBrowserTypeLaunchOptions(options.BrowserLaunchOptions);

var browser = options.BrowserKind switch
{
Expand Down Expand Up @@ -108,10 +100,10 @@ public Task<IBrowserContext> GetBrowserInstance(string browserInstance, string c
contextInfo);
}

public Task<IBrowserContext> GetBrowserInstance(BrowserKind browserInstance, string contextName, BrowserContextOptions options, ContextInformation contextInfo) =>
public Task<IBrowserContext> GetBrowserInstance(BrowserKind browserInstance, string contextName, BrowserNewContextOptions options, ContextInformation contextInfo) =>
GetBrowserInstance(browserInstance.ToString(), contextName, options, contextInfo);

public Task<IBrowserContext> GetBrowserInstance(string browserInstance, string contextName, BrowserContextOptions options, ContextInformation contextInfo)
public Task<IBrowserContext> GetBrowserInstance(string browserInstance, string contextName, BrowserNewContextOptions options, ContextInformation contextInfo)
{
if (_launchBrowsers.TryGetValue(browserInstance, out var browser))
{
Expand All @@ -126,9 +118,10 @@ public Task<IBrowserContext> GetBrowserInstance(string browserInstance, string c
private async Task<IBrowserContext> AttachContextInfo(Task<IBrowserContext> browserContextTask, ContextInformation contextInfo)
{
var context = await browserContextTask;
context.DefaultTimeout = HasFailedTests ?
var defaultTimeout = HasFailedTests ?
_browserManagerConfiguration.TimeoutAfterFirstFailureInMilliseconds :
_browserManagerConfiguration.TimeoutInMilliseconds;
context.SetDefaultTimeout(defaultTimeout);

contextInfo.Attach(context);
return context;
Expand Down
Loading

0 comments on commit 3129207

Please sign in to comment.