Skip to content

Commit b76113d

Browse files
committed
chore: roll to 1.59.0-alpha-1774017892000
1 parent 4abdc4a commit b76113d

67 files changed

Lines changed: 1451 additions & 275 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/skills/playwright-roll/SKILL.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,31 @@ description: Roll Playwright .NET to a new version
55

66
Help the user roll to a new version of Playwright.
77
../../../ROLLING.md contains general instructions and scripts.
8+
See also [CLAUDE.md](../../../CLAUDE.md) for build, test, and architecture details.
89

9-
Start with running the roll script to update the version and regenerate the API to see the state of things.
10+
Unless the exact version is specified by the user, you need to find the latest version of the driver.
11+
Check the publish workflow here: https://github.com/microsoft/playwright/actions/workflows/publish_release.yml. The step that builds and publishes the driver contains the exact version you need.
12+
13+
Now, with the driver version known, always start with running the roll script to update the version and regenerate the API to see the state of things.
1014

1115
```bash
1216
./build.sh --roll <driver-version>
1317
```
1418

1519
Afterwards, work through the list of changes that need to be backported.
16-
You can find a list of pull requests that might need to be taking into account in the issue titled "Backport changes".
17-
Work through them one-by-one and check off the items that you have handled.
18-
Not all of them will be relevant, some might have partially been reverted, etc. - so feel free to check with the upstream release branch.
20+
You can find a list of pull requests that might need to be taking into account in the issue titled "Backport changes". Ignore the items that are already checked off.
21+
22+
Some items may be irrelevant to the .NET implementation - feel free to check with the upstream.
23+
24+
Some items may be connected, for example when the API has changed multiple times. In this case, handle them alltogether, aligning with the latest change. Check upstream to see the latest implementation.
25+
26+
Otherwise, work through items one-by-one.
1927

2028
Rolling includes:
2129
- updating client implementation to match changes in the upstream JS implementation (see ../playwright/packages/playwright-core/src/client)
2230
- adding a couple of new tests to verify new/changed functionality
2331

2432
## Tips & Tricks
2533
- Project checkouts are in the parent directory (`../`).
26-
- when updating checkboxes, store the issue content into /tmp and edit it there, then update the issue based on the file
27-
- use the "gh" cli to interact with GitHub
34+
- When updating checkboxes, store the issue content into /tmp and edit it there, then update the issue based on the file.
35+
- Use the "gh" cli to interact with GitHub.

CLAUDE.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Playwright .NET
2+
3+
## Building
4+
5+
```bash
6+
./build.sh --download-driver # download the Playwright driver
7+
dotnet build ./src # build the entire solution
8+
```
9+
10+
## Running tests
11+
12+
Follow `.github/workflows/tests.yml` for the canonical sequence.
13+
14+
```bash
15+
# Install browsers (pick one: chromium, firefox, webkit)
16+
pwsh src/Playwright/bin/Debug/netstandard2.0/playwright.ps1 install --with-deps chromium
17+
18+
# Run tests
19+
BROWSER=chromium dotnet test ./src/Playwright.Tests/Playwright.Tests.csproj -c Debug -f net8.0 --logger:"console;verbosity=detailed"
20+
```
21+
22+
Tests take ~8 minutes. Always save output to a file and grep from there:
23+
24+
```bash
25+
BROWSER=chromium dotnet test ./src/Playwright.Tests/Playwright.Tests.csproj \
26+
-c Debug -f net8.0 --logger:"console;verbosity=detailed" > /tmp/test-results.txt 2>&1
27+
grep "^ Failed" /tmp/test-results.txt # list failures
28+
tail -5 /tmp/test-results.txt # summary
29+
```
30+
31+
## Architecture
32+
33+
### Generated vs hand-written code
34+
- Public API interfaces (e.g. `src/Playwright/API/Generated/IPage.cs`) are **generated** by `../playwright/utils/doclint/generateDotnetApi.js` from the upstream API docs. Do not hand-edit these — update the generator instead.
35+
- The generator uses `classNameMap` for type mappings (e.g. `Disposable``IAsyncDisposable`, `boolean``bool`). Add entries there when a Playwright type should map to a different .NET type.
36+
- The generator skips generating interface files for types like `TimeoutException` and `IAsyncDisposable` that map to built-in .NET types.
37+
- Supplement interfaces (`src/Playwright/API/Supplements/`) are hand-written and extend the generated interfaces with .NET-specific overloads.
38+
- Internal implementations live in `src/Playwright/Core/` (namespace `Microsoft.Playwright.Core`). These implement both the generated and supplement interfaces.
39+
40+
### Key patterns
41+
- All Playwright objects extend `ChannelOwner` and communicate via `SendMessageToServerAsync`.
42+
- `Connection.cs` has a factory switch that creates the right `ChannelOwner` subclass based on `ChannelOwnerType`.
43+
- New channel object types require: enum entry in `ChannelOwnerType.cs`, case in `Connection.cs`, initializer in `Transport/Protocol/Generated/`, and a `Core/` class.
44+
- Public APIs should use .NET standard types (e.g. `IAsyncDisposable`) not custom Playwright types. Internal helpers (e.g. `Disposable` class in `Core/`) stay internal.
45+
46+
## Commits
47+
- Do not include "co-authored" block in the commit message.
48+
49+
## Rolling to a new Playwright version
50+
See [.claude/skills/playwright-roll/SKILL.md](.claude/skills/playwright-roll/SKILL.md).

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33

44
| | Linux | macOS | Windows |
55
| :--- | :---: | :---: | :---: |
6-
| Chromium <!-- GEN:chromium-version -->145.0.7632.6<!-- GEN:stop --> ||||
6+
| Chromium <!-- GEN:chromium-version -->146.0.7680.31<!-- GEN:stop --> ||||
77
| WebKit <!-- GEN:webkit-version -->26.0<!-- GEN:stop --> ||||
8-
| Firefox <!-- GEN:firefox-version -->146.0.1<!-- GEN:stop --> ||||
8+
| Firefox <!-- GEN:firefox-version -->148.0.2<!-- GEN:stop --> ||||
99

1010
Playwright for .NET is the official language port of [Playwright](https://playwright.dev), the library to automate [Chromium](https://www.chromium.org/Home), [Firefox](https://www.mozilla.org/en-US/firefox/new/) and [WebKit](https://webkit.org/) with a single API. Playwright is built to enable cross-browser web automation that is **ever-green**, **capable**, **reliable** and **fast**.
1111

src/Common/Version.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<AssemblyVersion>1.58.0</AssemblyVersion>
44
<PackageVersion>$(AssemblyVersion)</PackageVersion>
5-
<DriverVersion>1.58.1</DriverVersion>
5+
<DriverVersion>1.59.0-alpha-1774017892000</DriverVersion>
66
<ReleaseVersion>$(AssemblyVersion)</ReleaseVersion>
77
<FileVersion>$(AssemblyVersion)</FileVersion>
88
<NoDefaultExcludes>true</NoDefaultExcludes>

src/Playwright.TestingHarnessTest/package-lock.json

Lines changed: 23 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Playwright.TestingHarnessTest/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "playwright.testingharnesstest",
33
"private": true,
44
"devDependencies": {
5-
"@playwright/test": "1.58.1",
5+
"@playwright/test": "1.59.0-alpha-1774017892000",
66
"@types/node": "^22.12.0",
77
"fast-xml-parser": "^4.5.0"
88
}

src/Playwright.Tests/BrowserContextStorageStateTests.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,27 @@ public async Task ShouldSerializeStorageStateWithLoneSurrogates()
177177
// It should get replaced by the utf8 replacement char (U+FFFD)
178178
StringAssert.Contains(@"""value"":""\uFFFD""", storageState);
179179
}
180+
181+
[PlaywrightTest("browsercontext-storage-state.spec.ts", "should set local storage via setStorageState")]
182+
public async Task ShouldSetLocalStorageViaSetStorageState()
183+
{
184+
await using var context = await Browser.NewContextAsync();
185+
var page = await context.NewPageAsync();
186+
await page.RouteAsync("**/*", (route) =>
187+
{
188+
route.FulfillAsync(new() { Body = "<html></html>" });
189+
});
190+
await page.GotoAsync("https://www.example.com");
191+
var localStorage = await page.EvaluateAsync<string>("window.localStorage.getItem('name1')");
192+
Assert.IsNull(localStorage);
193+
194+
using var tempDir = new TempDirectory();
195+
string path = Path.Combine(tempDir.Path, "storage-state.json");
196+
File.WriteAllText(path, @"{""cookies"":[],""origins"":[{""origin"":""https://www.example.com"",""localStorage"":[{""name"":""name1"",""value"":""value1""}]}]}");
197+
await context.SetStorageStateAsync(path);
198+
199+
await page.GotoAsync("https://www.example.com");
200+
localStorage = await page.EvaluateAsync<string>("window.localStorage.getItem('name1')");
201+
Assert.AreEqual("value1", localStorage);
202+
}
180203
}

src/Playwright.Tests/PageAddInitScriptTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,31 @@ public async Task ShouldWorkAfterACrossOriginNavigation()
134134
await Page.GotoAsync(Server.Prefix + "/tamperable.html");
135135
Assert.AreEqual(123, await Page.EvaluateAsync<int>("() => window.result"));
136136
}
137+
138+
[PlaywrightTest("page-add-init-script.spec.ts", "should remove init script after dispose")]
139+
public async Task ShouldRemoveInitScriptAfterDispose()
140+
{
141+
var disposable = await Page.AddInitScriptAsync("window.injected = 123;");
142+
await Page.GotoAsync(Server.Prefix + "/tamperable.html");
143+
Assert.AreEqual(123, await Page.EvaluateAsync<int>("() => window.result"));
144+
145+
await disposable.DisposeAsync();
146+
await Page.GotoAsync(Server.Prefix + "/tamperable.html");
147+
Assert.IsNull(await Page.EvaluateAsync("() => window.injected"));
148+
}
149+
150+
[PlaywrightTest("page-add-init-script.spec.ts", "should remove one of multiple init scripts after dispose")]
151+
public async Task ShouldRemoveOneOfMultipleInitScriptsAfterDispose()
152+
{
153+
var disposable1 = await Page.AddInitScriptAsync("window.script1 = 1;");
154+
await Page.AddInitScriptAsync("window.script2 = 2;");
155+
await Page.GotoAsync(Server.Prefix + "/tamperable.html");
156+
Assert.AreEqual(1, await Page.EvaluateAsync<int>("() => window.script1"));
157+
Assert.AreEqual(2, await Page.EvaluateAsync<int>("() => window.script2"));
158+
159+
await disposable1.DisposeAsync();
160+
await Page.GotoAsync(Server.Prefix + "/tamperable.html");
161+
Assert.IsNull(await Page.EvaluateAsync("() => window.script1"));
162+
Assert.AreEqual(2, await Page.EvaluateAsync<int>("() => window.script2"));
163+
}
137164
}

src/Playwright.Tests/PageEventConsoleTests.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,4 +180,55 @@ await Page.EvaluateAsync(@"() => {
180180
Assert.AreEqual(Page, message.Page);
181181
}
182182
}
183+
184+
[PlaywrightTest("page-event-console.spec.ts", "should have timestamp")]
185+
public async Task ShouldHaveTimestamp()
186+
{
187+
var before = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - 1;
188+
var message = await Page.RunAndWaitForConsoleMessageAsync(
189+
() => Page.EvaluateAsync("() => console.log('timestamp test')"));
190+
var after = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + 1;
191+
Assert.GreaterOrEqual(message.Timestamp, before);
192+
Assert.LessOrEqual(message.Timestamp, after);
193+
}
194+
195+
[PlaywrightTest("page-event-console.spec.ts", "clearConsoleMessages should work")]
196+
public async Task ClearConsoleMessagesShouldWork()
197+
{
198+
await Page.EvaluateAsync(@"() => {
199+
console.log('message1');
200+
console.log('message2');
201+
}");
202+
203+
var messages = await Page.ConsoleMessagesAsync(new() { Filter = ConsoleMessagesFilter.All });
204+
Assert.True(messages.Any(m => m.Text == "message1"));
205+
Assert.True(messages.Any(m => m.Text == "message2"));
206+
207+
await Page.ClearConsoleMessagesAsync();
208+
209+
messages = await Page.ConsoleMessagesAsync(new() { Filter = ConsoleMessagesFilter.All });
210+
Assert.IsEmpty(messages);
211+
212+
await Page.EvaluateAsync("() => console.log('message3')");
213+
messages = await Page.ConsoleMessagesAsync(new() { Filter = ConsoleMessagesFilter.All });
214+
Assert.AreEqual(1, messages.Count);
215+
Assert.AreEqual("message3", messages[0].Text);
216+
}
217+
218+
[PlaywrightTest("page-event-console.spec.ts", "consoleMessages sinceNavigation filter should work")]
219+
public async Task ConsoleMessagesSinceNavigationFilterShouldWork()
220+
{
221+
await Page.EvaluateAsync("() => console.log('before navigation')");
222+
await Page.GotoAsync(Server.EmptyPage);
223+
await Page.EvaluateAsync("() => console.log('after navigation')");
224+
225+
var all = await Page.ConsoleMessagesAsync(new() { Filter = ConsoleMessagesFilter.All });
226+
Assert.True(all.Any(m => m.Text == "before navigation"));
227+
Assert.True(all.Any(m => m.Text == "after navigation"));
228+
229+
// sinceNavigation is the default
230+
var sinceNav = await Page.ConsoleMessagesAsync();
231+
Assert.False(sinceNav.Any(m => m.Text == "before navigation"));
232+
Assert.True(sinceNav.Any(m => m.Text == "after navigation"));
233+
}
183234
}

src/Playwright.Tests/PageNetworkResponseTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,21 @@ public async Task ShouldBehaveTheSameWayForHeadersAndAllHeaders()
308308
Assert.AreEqual(response.Headers, allHeaders);
309309
}
310310

311+
[PlaywrightTest("page-network-response.spec.ts", "should return http version")]
312+
public async Task ShouldReturnHttpVersion()
313+
{
314+
var response = await Page.GotoAsync(Server.EmptyPage);
315+
Assert.AreEqual("HTTP/1.1", await response.HttpVersionAsync());
316+
}
317+
318+
[PlaywrightTest("page-network-response.spec.ts", "request.existingResponse should return the response after it is received")]
319+
public async Task RequestExistingResponseShouldReturnTheResponseAfterItIsReceived()
320+
{
321+
var response = await Page.GotoAsync(Server.EmptyPage);
322+
var request = response.Request;
323+
Assert.AreEqual(response, request.ExistingResponse);
324+
}
325+
311326
[PlaywrightTest("page-network-response.spec.ts", "should report if request was fromServiceWorker")]
312327
public async Task ShouldReportIfRequestWasFromServiceWorker()
313328
{

0 commit comments

Comments
 (0)