chore(roll): v1.61.0 - #3102

Merged
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b
Jun 15, 2026
Merged

chore(roll): v1.61.0#3102
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b

Conversation

@Skn0tt

@Skn0ttSimon Knott (Skn0tt) commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

This rolls the driver to 1cc5a90cfa3eaa430b1a991963100f95126caa47 (v1.61) and ports the new upstream APIs:

  • APIResponse.security_details / .server_addr
  • Credentials class (WebAuthn) + BrowserContext.credentials
  • WebStorage class + Page.local_storage / .session_storage
  • Screencast.start(size=), .show_actions(cursor=), ScreencastFrame.timestamp
  • BrowserType.connect_over_cdp(artifacts_dir=)

Also picks up the upstream Credentials.create(rp_id, ...) shape, so Python can keep options-bag properties optional. The assertion port keeps the previous failure messages by reading FrameExpectErrorDetails from protocol errors.

The rolling skill is updated to reference driver/playwright-src instead of ~/code/playwright.

Driver SHA: ac7cdd4bdf15f90fe7229243be6b35a53e0296d1 (v1.61.0-next)
New APIs:
- APIResponse.security_details / .server_addr
- Credentials class (WebAuthn) + BrowserContext.credentials
- WebStorage class + Page.local_storage / .session_storage
- Screencast.start(size=), .show_actions(cursor=),
ScreencastFrame.timestamp
- BrowserType.connect_over_cdp(artifacts_dir=)
Also:
- Stop forcing options-bag properties to required=False in
documentation_provider.py (Credentials.create(rp_id=) was
the only case affected)
- Update rolling skill to use driver/playwright-src
- 37 new tests (async + sync)
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in credentials.create(rp_id), this was making rp_id optional, which is incorrect.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

@Skn0ttSimon Knott (Skn0tt) changed the title feat(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to ac7cdd4bd, port new APIs, add testsJun 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Rolls the bundled Playwright driver to a new upstream commit (ac7cdd4b…, v1.61.0-next) and ports newly added upstream APIs into the Python implementation, generated surfaces, and test suite.

Changes:

  • Ported new API surface: APIResponse.security_details() / .server_addr(), WebAuthn Credentials via BrowserContext.credentials, WebStorage via Page.local_storage / .session_storage, and new Screencast options/fields (start(size=), show_actions(cursor=), ScreencastFrame.timestamp).
  • Extended CDP connection API with BrowserType.connect_over_cdp(artifacts_dir=) and updated API generation inputs/types (ScreencastSize, VirtualCredential).
  • Updated driver pin + docs, and adjusted documentation generation to stop forcing all options-bag properties to optional.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 13 comments.

Show a summary per file
FileDescription
tests/sync/test_screencast.pyAdds sync tests for new Screencast size/cursor/timestamp APIs.
tests/sync/test_page_web_storage.pyNew sync tests for Page.local_storage / session_storage WebStorage access.
tests/sync/test_browsercontext_credentials.pyNew sync tests for BrowserContext.credentials WebAuthn seeding APIs.
tests/async/test_screencast.pyAdds async tests for new Screencast size/cursor/timestamp APIs.
tests/async/test_page_web_storage.pyNew async tests for Page.local_storage / session_storage WebStorage access.
tests/async/test_browsercontext_credentials.pyNew async tests for BrowserContext.credentials WebAuthn seeding APIs.
scripts/generate_api.pyUpdates API generation imports/registrations for newly ported impl classes and structures.
scripts/documentation_provider.pyStops force-marking options-bag properties as optional (fixes requiredness for rp_id).
scripts/build_driver.shUpdates driver source reference comment to main (driver pin still enforced via DRIVER_SHA).
README.mdUpdates embedded browser version markers to match the rolled driver.
playwright/sync_api/_generated.pyRegenerates sync surface: adds new APIs, types, and doc updates.
playwright/sync_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/async_api/_generated.pyRegenerates async surface: adds new APIs, types, and doc updates.
playwright/async_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/_impl/_web_storage.pyIntroduces WebStorage channel wrapper implementation.
playwright/_impl/_screencast.pyAdds Screencast size/cursor params and propagates timestamp into frames.
playwright/_impl/_page.pyAdds local_storage / session_storage properties backed by WebStorage instances.
playwright/_impl/_fetch.pyExposes APIResponse.security_details() / .server_addr() from initializer data.
playwright/_impl/_credentials.pyIntroduces Credentials channel wrapper implementation for WebAuthn.
playwright/_impl/_browser_type.pyAdds artifactsDir plumb-through for CDP connect.
playwright/_impl/_browser_context.pyAdds credentials property backed by new Credentials impl.
playwright/_impl/_api_structures.pyAdds ScreencastSize and VirtualCredential structures; extends ScreencastFrame with timestamp.
DRIVER_SHAUpdates pinned upstream driver commit hash.
.claude/skills/playwright-roll/SKILL.mdUpdates roll skill docs to reference driver/playwright-src checkout path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +69 to +75
def test_show_actions_should_accept_cursor_param(page: Page) -> None:
page.screencast.start(on_frame=lambda f: None)
with page.screencast.show_actions(duration=100, cursor="pointer"):
pass
with page.screencast.show_actions(duration=100, cursor="none"):
pass
page.screencast.stop()
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +56 to +66
def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
size: ScreencastSize = {"width": 800, "height": 600}
page.screencast.start(on_frame=lambda f: received.append(f), size=size)
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +78 to +88
def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
page.screencast.start(on_frame=lambda f: received.append(f))
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +75 to +89
async def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

size: ScreencastSize = {"width": 800, "height": 600}
await page.screencast.start(on_frame=on_frame, size=size)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +103 to +117
async def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

await page.screencast.start(on_frame=on_frame)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment on lines +27 to +31
async def test_local_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('foo', 'bar')")
value = await page.local_storage.get_item("foo")
assert value == "bar"
Comment on lines +34 to +41
async def test_local_storage_items(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('a', '1')")
await page.evaluate("() => localStorage.setItem('b', '2')")
items = await page.local_storage.items()
assert len(items) == 2
assert {"name": "a", "value": "1"} in items
assert {"name": "b", "value": "2"} in items
Comment on lines +60 to +64
async def test_session_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => sessionStorage.setItem('foo', 'bar')")
value = await page.session_storage.get_item("foo")
assert value == "bar"
Comment on lines +31 to +39
result = creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

Comment on lines +34 to +42
result = await creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

- WebStorage tests: seed via dedicated API (set_item) instead of evaluate
- Credentials tests: use auto-generated keys instead of fake placeholders
- Screencast tests: replace size+timestamp test with upstream's
onFrame receives viewport size; add ensureSomeFrames pattern;
remove inconsistent try/finally cleanup
…change
Upstream commit ac7cdd4bd changed FrameExpectResult from {matches, received}
to void — expect returns nothing on success and throws ExpectError on failure.
Updates:
- _assertions.py: _expect_impl now catches driver Error and uses its message
directly; removes unused parse_value and FrameExpectResult imports.
- _frame.py: guard against None result from _expect channel call; change
return type to dict (callers don't use typed fields anymore).
- _locator.py: change _expect return type to dict for consistency.
- tests/{sync,async}/test_assertions.py: update 21 error-message assertions to
match the new upstream format (e.g. 'LocatorAssertions.to_have_text: Expect
failed\nCall log:\n - Expect "to_have_text" with timeout 300ms\n…' instead
of the old Python-formatted "Locator expected to …" / "Actual value: …").
Upstream stopped recording "Wait for event" as separate trace actions.
Remove the corresponding patterns from the two trace viewer tests so
they match the actual 5 (context managers) and 1 (load state) actions
now produced by the driver.
WebKit may report viewportWidth=1002 (instead of 1000) on the
first screencast frame. Use `any()` check instead of iterating
all frames, and increase rAF cycles from 3 to 100 for more
reliable frame generation.
Comment threadplaywright/_impl/_frame.py Outdated
Comment threadplaywright/_impl/_frame.py Outdated
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

Comment threadtests/async/test_assertions.py Outdated
Comment threadplaywright/_impl/_assertions.py Outdated
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to fc893f84e, port new APIsJun 12, 2026
Comment threadplaywright/_impl/_helper.py Outdated
Comment threadplaywright/_impl/_assertions.py
Comment threadplaywright/_impl/_connection.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_tracing.py
@Skn0ttSimon Knott (Skn0tt) linked an issue Jun 15, 2026 that may be closed by this pull request
- port waitForEventInfo to __waitInfo__ so wait actions show in traces again
- source error details from errorDetails with guid replacement, keep raw call log
- restore format_call_log in assertion messages
- drop unnecessary try/finally in new screencast tests
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to fc893f84e, port new APIschore(roll): v1.61Jun 15, 2026
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): v1.61chore(roll): v1.61.0Jun 15, 2026
@Skn0tt
Simon Knott (Skn0tt) merged commit 613c3bf into microsoft:mainJun 15, 2026
34 of 36 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add size option to page.screencast.start()

3 participants

@Skn0tt@dgozman
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

chore(roll): v1.61.0 - #3102

Merged
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b
Jun 15, 2026
Merged

chore(roll): v1.61.0#3102
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b

Conversation

@Skn0tt

@Skn0ttSimon Knott (Skn0tt) commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

This rolls the driver to 1cc5a90cfa3eaa430b1a991963100f95126caa47 (v1.61) and ports the new upstream APIs:

  • APIResponse.security_details / .server_addr
  • Credentials class (WebAuthn) + BrowserContext.credentials
  • WebStorage class + Page.local_storage / .session_storage
  • Screencast.start(size=), .show_actions(cursor=), ScreencastFrame.timestamp
  • BrowserType.connect_over_cdp(artifacts_dir=)

Also picks up the upstream Credentials.create(rp_id, ...) shape, so Python can keep options-bag properties optional. The assertion port keeps the previous failure messages by reading FrameExpectErrorDetails from protocol errors.

The rolling skill is updated to reference driver/playwright-src instead of ~/code/playwright.

Driver SHA: ac7cdd4bdf15f90fe7229243be6b35a53e0296d1 (v1.61.0-next)
New APIs:
- APIResponse.security_details / .server_addr
- Credentials class (WebAuthn) + BrowserContext.credentials
- WebStorage class + Page.local_storage / .session_storage
- Screencast.start(size=), .show_actions(cursor=),
ScreencastFrame.timestamp
- BrowserType.connect_over_cdp(artifacts_dir=)
Also:
- Stop forcing options-bag properties to required=False in
documentation_provider.py (Credentials.create(rp_id=) was
the only case affected)
- Update rolling skill to use driver/playwright-src
- 37 new tests (async + sync)
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in credentials.create(rp_id), this was making rp_id optional, which is incorrect.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

@Skn0ttSimon Knott (Skn0tt) changed the title feat(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to ac7cdd4bd, port new APIs, add testsJun 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Rolls the bundled Playwright driver to a new upstream commit (ac7cdd4b…, v1.61.0-next) and ports newly added upstream APIs into the Python implementation, generated surfaces, and test suite.

Changes:

  • Ported new API surface: APIResponse.security_details() / .server_addr(), WebAuthn Credentials via BrowserContext.credentials, WebStorage via Page.local_storage / .session_storage, and new Screencast options/fields (start(size=), show_actions(cursor=), ScreencastFrame.timestamp).
  • Extended CDP connection API with BrowserType.connect_over_cdp(artifacts_dir=) and updated API generation inputs/types (ScreencastSize, VirtualCredential).
  • Updated driver pin + docs, and adjusted documentation generation to stop forcing all options-bag properties to optional.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 13 comments.

Show a summary per file
FileDescription
tests/sync/test_screencast.pyAdds sync tests for new Screencast size/cursor/timestamp APIs.
tests/sync/test_page_web_storage.pyNew sync tests for Page.local_storage / session_storage WebStorage access.
tests/sync/test_browsercontext_credentials.pyNew sync tests for BrowserContext.credentials WebAuthn seeding APIs.
tests/async/test_screencast.pyAdds async tests for new Screencast size/cursor/timestamp APIs.
tests/async/test_page_web_storage.pyNew async tests for Page.local_storage / session_storage WebStorage access.
tests/async/test_browsercontext_credentials.pyNew async tests for BrowserContext.credentials WebAuthn seeding APIs.
scripts/generate_api.pyUpdates API generation imports/registrations for newly ported impl classes and structures.
scripts/documentation_provider.pyStops force-marking options-bag properties as optional (fixes requiredness for rp_id).
scripts/build_driver.shUpdates driver source reference comment to main (driver pin still enforced via DRIVER_SHA).
README.mdUpdates embedded browser version markers to match the rolled driver.
playwright/sync_api/_generated.pyRegenerates sync surface: adds new APIs, types, and doc updates.
playwright/sync_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/async_api/_generated.pyRegenerates async surface: adds new APIs, types, and doc updates.
playwright/async_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/_impl/_web_storage.pyIntroduces WebStorage channel wrapper implementation.
playwright/_impl/_screencast.pyAdds Screencast size/cursor params and propagates timestamp into frames.
playwright/_impl/_page.pyAdds local_storage / session_storage properties backed by WebStorage instances.
playwright/_impl/_fetch.pyExposes APIResponse.security_details() / .server_addr() from initializer data.
playwright/_impl/_credentials.pyIntroduces Credentials channel wrapper implementation for WebAuthn.
playwright/_impl/_browser_type.pyAdds artifactsDir plumb-through for CDP connect.
playwright/_impl/_browser_context.pyAdds credentials property backed by new Credentials impl.
playwright/_impl/_api_structures.pyAdds ScreencastSize and VirtualCredential structures; extends ScreencastFrame with timestamp.
DRIVER_SHAUpdates pinned upstream driver commit hash.
.claude/skills/playwright-roll/SKILL.mdUpdates roll skill docs to reference driver/playwright-src checkout path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +69 to +75
def test_show_actions_should_accept_cursor_param(page: Page) -> None:
page.screencast.start(on_frame=lambda f: None)
with page.screencast.show_actions(duration=100, cursor="pointer"):
pass
with page.screencast.show_actions(duration=100, cursor="none"):
pass
page.screencast.stop()
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +56 to +66
def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
size: ScreencastSize = {"width": 800, "height": 600}
page.screencast.start(on_frame=lambda f: received.append(f), size=size)
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +78 to +88
def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
page.screencast.start(on_frame=lambda f: received.append(f))
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +75 to +89
async def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

size: ScreencastSize = {"width": 800, "height": 600}
await page.screencast.start(on_frame=on_frame, size=size)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +103 to +117
async def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

await page.screencast.start(on_frame=on_frame)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment on lines +27 to +31
async def test_local_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('foo', 'bar')")
value = await page.local_storage.get_item("foo")
assert value == "bar"
Comment on lines +34 to +41
async def test_local_storage_items(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('a', '1')")
await page.evaluate("() => localStorage.setItem('b', '2')")
items = await page.local_storage.items()
assert len(items) == 2
assert {"name": "a", "value": "1"} in items
assert {"name": "b", "value": "2"} in items
Comment on lines +60 to +64
async def test_session_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => sessionStorage.setItem('foo', 'bar')")
value = await page.session_storage.get_item("foo")
assert value == "bar"
Comment on lines +31 to +39
result = creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

Comment on lines +34 to +42
result = await creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

- WebStorage tests: seed via dedicated API (set_item) instead of evaluate
- Credentials tests: use auto-generated keys instead of fake placeholders
- Screencast tests: replace size+timestamp test with upstream's
onFrame receives viewport size; add ensureSomeFrames pattern;
remove inconsistent try/finally cleanup
…change
Upstream commit ac7cdd4bd changed FrameExpectResult from {matches, received}
to void — expect returns nothing on success and throws ExpectError on failure.
Updates:
- _assertions.py: _expect_impl now catches driver Error and uses its message
directly; removes unused parse_value and FrameExpectResult imports.
- _frame.py: guard against None result from _expect channel call; change
return type to dict (callers don't use typed fields anymore).
- _locator.py: change _expect return type to dict for consistency.
- tests/{sync,async}/test_assertions.py: update 21 error-message assertions to
match the new upstream format (e.g. 'LocatorAssertions.to_have_text: Expect
failed\nCall log:\n - Expect "to_have_text" with timeout 300ms\n…' instead
of the old Python-formatted "Locator expected to …" / "Actual value: …").
Upstream stopped recording "Wait for event" as separate trace actions.
Remove the corresponding patterns from the two trace viewer tests so
they match the actual 5 (context managers) and 1 (load state) actions
now produced by the driver.
WebKit may report viewportWidth=1002 (instead of 1000) on the
first screencast frame. Use `any()` check instead of iterating
all frames, and increase rAF cycles from 3 to 100 for more
reliable frame generation.
Comment threadplaywright/_impl/_frame.py Outdated
Comment threadplaywright/_impl/_frame.py Outdated
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

Comment threadtests/async/test_assertions.py Outdated
Comment threadplaywright/_impl/_assertions.py Outdated
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to fc893f84e, port new APIsJun 12, 2026
Comment threadplaywright/_impl/_helper.py Outdated
Comment threadplaywright/_impl/_assertions.py
Comment threadplaywright/_impl/_connection.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_tracing.py
@Skn0ttSimon Knott (Skn0tt) linked an issue Jun 15, 2026 that may be closed by this pull request
- port waitForEventInfo to __waitInfo__ so wait actions show in traces again
- source error details from errorDetails with guid replacement, keep raw call log
- restore format_call_log in assertion messages
- drop unnecessary try/finally in new screencast tests
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to fc893f84e, port new APIschore(roll): v1.61Jun 15, 2026
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): v1.61chore(roll): v1.61.0Jun 15, 2026
@Skn0tt
Simon Knott (Skn0tt) merged commit 613c3bf into microsoft:mainJun 15, 2026
34 of 36 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add size option to page.screencast.start()

3 participants

@Skn0tt@dgozman
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(roll): v1.61.0 - #3102

Merged
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b
Jun 15, 2026
Merged

chore(roll): v1.61.0#3102
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b

Conversation

@Skn0tt

@Skn0ttSimon Knott (Skn0tt) commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

This rolls the driver to 1cc5a90cfa3eaa430b1a991963100f95126caa47 (v1.61) and ports the new upstream APIs:

  • APIResponse.security_details / .server_addr
  • Credentials class (WebAuthn) + BrowserContext.credentials
  • WebStorage class + Page.local_storage / .session_storage
  • Screencast.start(size=), .show_actions(cursor=), ScreencastFrame.timestamp
  • BrowserType.connect_over_cdp(artifacts_dir=)

Also picks up the upstream Credentials.create(rp_id, ...) shape, so Python can keep options-bag properties optional. The assertion port keeps the previous failure messages by reading FrameExpectErrorDetails from protocol errors.

The rolling skill is updated to reference driver/playwright-src instead of ~/code/playwright.

Driver SHA: ac7cdd4bdf15f90fe7229243be6b35a53e0296d1 (v1.61.0-next)
New APIs:
- APIResponse.security_details / .server_addr
- Credentials class (WebAuthn) + BrowserContext.credentials
- WebStorage class + Page.local_storage / .session_storage
- Screencast.start(size=), .show_actions(cursor=),
ScreencastFrame.timestamp
- BrowserType.connect_over_cdp(artifacts_dir=)
Also:
- Stop forcing options-bag properties to required=False in
documentation_provider.py (Credentials.create(rp_id=) was
the only case affected)
- Update rolling skill to use driver/playwright-src
- 37 new tests (async + sync)
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in credentials.create(rp_id), this was making rp_id optional, which is incorrect.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

@Skn0ttSimon Knott (Skn0tt) changed the title feat(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to ac7cdd4bd, port new APIs, add testsJun 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Rolls the bundled Playwright driver to a new upstream commit (ac7cdd4b…, v1.61.0-next) and ports newly added upstream APIs into the Python implementation, generated surfaces, and test suite.

Changes:

  • Ported new API surface: APIResponse.security_details() / .server_addr(), WebAuthn Credentials via BrowserContext.credentials, WebStorage via Page.local_storage / .session_storage, and new Screencast options/fields (start(size=), show_actions(cursor=), ScreencastFrame.timestamp).
  • Extended CDP connection API with BrowserType.connect_over_cdp(artifacts_dir=) and updated API generation inputs/types (ScreencastSize, VirtualCredential).
  • Updated driver pin + docs, and adjusted documentation generation to stop forcing all options-bag properties to optional.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 13 comments.

Show a summary per file
FileDescription
tests/sync/test_screencast.pyAdds sync tests for new Screencast size/cursor/timestamp APIs.
tests/sync/test_page_web_storage.pyNew sync tests for Page.local_storage / session_storage WebStorage access.
tests/sync/test_browsercontext_credentials.pyNew sync tests for BrowserContext.credentials WebAuthn seeding APIs.
tests/async/test_screencast.pyAdds async tests for new Screencast size/cursor/timestamp APIs.
tests/async/test_page_web_storage.pyNew async tests for Page.local_storage / session_storage WebStorage access.
tests/async/test_browsercontext_credentials.pyNew async tests for BrowserContext.credentials WebAuthn seeding APIs.
scripts/generate_api.pyUpdates API generation imports/registrations for newly ported impl classes and structures.
scripts/documentation_provider.pyStops force-marking options-bag properties as optional (fixes requiredness for rp_id).
scripts/build_driver.shUpdates driver source reference comment to main (driver pin still enforced via DRIVER_SHA).
README.mdUpdates embedded browser version markers to match the rolled driver.
playwright/sync_api/_generated.pyRegenerates sync surface: adds new APIs, types, and doc updates.
playwright/sync_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/async_api/_generated.pyRegenerates async surface: adds new APIs, types, and doc updates.
playwright/async_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/_impl/_web_storage.pyIntroduces WebStorage channel wrapper implementation.
playwright/_impl/_screencast.pyAdds Screencast size/cursor params and propagates timestamp into frames.
playwright/_impl/_page.pyAdds local_storage / session_storage properties backed by WebStorage instances.
playwright/_impl/_fetch.pyExposes APIResponse.security_details() / .server_addr() from initializer data.
playwright/_impl/_credentials.pyIntroduces Credentials channel wrapper implementation for WebAuthn.
playwright/_impl/_browser_type.pyAdds artifactsDir plumb-through for CDP connect.
playwright/_impl/_browser_context.pyAdds credentials property backed by new Credentials impl.
playwright/_impl/_api_structures.pyAdds ScreencastSize and VirtualCredential structures; extends ScreencastFrame with timestamp.
DRIVER_SHAUpdates pinned upstream driver commit hash.
.claude/skills/playwright-roll/SKILL.mdUpdates roll skill docs to reference driver/playwright-src checkout path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +69 to +75
def test_show_actions_should_accept_cursor_param(page: Page) -> None:
page.screencast.start(on_frame=lambda f: None)
with page.screencast.show_actions(duration=100, cursor="pointer"):
pass
with page.screencast.show_actions(duration=100, cursor="none"):
pass
page.screencast.stop()
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +56 to +66
def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
size: ScreencastSize = {"width": 800, "height": 600}
page.screencast.start(on_frame=lambda f: received.append(f), size=size)
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +78 to +88
def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
page.screencast.start(on_frame=lambda f: received.append(f))
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +75 to +89
async def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

size: ScreencastSize = {"width": 800, "height": 600}
await page.screencast.start(on_frame=on_frame, size=size)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +103 to +117
async def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

await page.screencast.start(on_frame=on_frame)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment on lines +27 to +31
async def test_local_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('foo', 'bar')")
value = await page.local_storage.get_item("foo")
assert value == "bar"
Comment on lines +34 to +41
async def test_local_storage_items(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('a', '1')")
await page.evaluate("() => localStorage.setItem('b', '2')")
items = await page.local_storage.items()
assert len(items) == 2
assert {"name": "a", "value": "1"} in items
assert {"name": "b", "value": "2"} in items
Comment on lines +60 to +64
async def test_session_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => sessionStorage.setItem('foo', 'bar')")
value = await page.session_storage.get_item("foo")
assert value == "bar"
Comment on lines +31 to +39
result = creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

Comment on lines +34 to +42
result = await creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

- WebStorage tests: seed via dedicated API (set_item) instead of evaluate
- Credentials tests: use auto-generated keys instead of fake placeholders
- Screencast tests: replace size+timestamp test with upstream's
onFrame receives viewport size; add ensureSomeFrames pattern;
remove inconsistent try/finally cleanup
…change
Upstream commit ac7cdd4bd changed FrameExpectResult from {matches, received}
to void — expect returns nothing on success and throws ExpectError on failure.
Updates:
- _assertions.py: _expect_impl now catches driver Error and uses its message
directly; removes unused parse_value and FrameExpectResult imports.
- _frame.py: guard against None result from _expect channel call; change
return type to dict (callers don't use typed fields anymore).
- _locator.py: change _expect return type to dict for consistency.
- tests/{sync,async}/test_assertions.py: update 21 error-message assertions to
match the new upstream format (e.g. 'LocatorAssertions.to_have_text: Expect
failed\nCall log:\n - Expect "to_have_text" with timeout 300ms\n…' instead
of the old Python-formatted "Locator expected to …" / "Actual value: …").
Upstream stopped recording "Wait for event" as separate trace actions.
Remove the corresponding patterns from the two trace viewer tests so
they match the actual 5 (context managers) and 1 (load state) actions
now produced by the driver.
WebKit may report viewportWidth=1002 (instead of 1000) on the
first screencast frame. Use `any()` check instead of iterating
all frames, and increase rAF cycles from 3 to 100 for more
reliable frame generation.
Comment threadplaywright/_impl/_frame.py Outdated
Comment threadplaywright/_impl/_frame.py Outdated
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

Comment threadtests/async/test_assertions.py Outdated
Comment threadplaywright/_impl/_assertions.py Outdated
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to fc893f84e, port new APIsJun 12, 2026
Comment threadplaywright/_impl/_helper.py Outdated
Comment threadplaywright/_impl/_assertions.py
Comment threadplaywright/_impl/_connection.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_tracing.py
@Skn0ttSimon Knott (Skn0tt) linked an issue Jun 15, 2026 that may be closed by this pull request
- port waitForEventInfo to __waitInfo__ so wait actions show in traces again
- source error details from errorDetails with guid replacement, keep raw call log
- restore format_call_log in assertion messages
- drop unnecessary try/finally in new screencast tests
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to fc893f84e, port new APIschore(roll): v1.61Jun 15, 2026
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): v1.61chore(roll): v1.61.0Jun 15, 2026
@Skn0tt
Simon Knott (Skn0tt) merged commit 613c3bf into microsoft:mainJun 15, 2026
34 of 36 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add size option to page.screencast.start()

3 participants

@Skn0tt@dgozman
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(roll): v1.61.0 - #3102

Merged
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b
Jun 15, 2026
Merged

chore(roll): v1.61.0#3102
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b

Conversation

@Skn0tt

@Skn0ttSimon Knott (Skn0tt) commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

This rolls the driver to 1cc5a90cfa3eaa430b1a991963100f95126caa47 (v1.61) and ports the new upstream APIs:

  • APIResponse.security_details / .server_addr
  • Credentials class (WebAuthn) + BrowserContext.credentials
  • WebStorage class + Page.local_storage / .session_storage
  • Screencast.start(size=), .show_actions(cursor=), ScreencastFrame.timestamp
  • BrowserType.connect_over_cdp(artifacts_dir=)

Also picks up the upstream Credentials.create(rp_id, ...) shape, so Python can keep options-bag properties optional. The assertion port keeps the previous failure messages by reading FrameExpectErrorDetails from protocol errors.

The rolling skill is updated to reference driver/playwright-src instead of ~/code/playwright.

Driver SHA: ac7cdd4bdf15f90fe7229243be6b35a53e0296d1 (v1.61.0-next)
New APIs:
- APIResponse.security_details / .server_addr
- Credentials class (WebAuthn) + BrowserContext.credentials
- WebStorage class + Page.local_storage / .session_storage
- Screencast.start(size=), .show_actions(cursor=),
ScreencastFrame.timestamp
- BrowserType.connect_over_cdp(artifacts_dir=)
Also:
- Stop forcing options-bag properties to required=False in
documentation_provider.py (Credentials.create(rp_id=) was
the only case affected)
- Update rolling skill to use driver/playwright-src
- 37 new tests (async + sync)
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in credentials.create(rp_id), this was making rp_id optional, which is incorrect.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

@Skn0ttSimon Knott (Skn0tt) changed the title feat(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to ac7cdd4bd, port new APIs, add testsJun 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Rolls the bundled Playwright driver to a new upstream commit (ac7cdd4b…, v1.61.0-next) and ports newly added upstream APIs into the Python implementation, generated surfaces, and test suite.

Changes:

  • Ported new API surface: APIResponse.security_details() / .server_addr(), WebAuthn Credentials via BrowserContext.credentials, WebStorage via Page.local_storage / .session_storage, and new Screencast options/fields (start(size=), show_actions(cursor=), ScreencastFrame.timestamp).
  • Extended CDP connection API with BrowserType.connect_over_cdp(artifacts_dir=) and updated API generation inputs/types (ScreencastSize, VirtualCredential).
  • Updated driver pin + docs, and adjusted documentation generation to stop forcing all options-bag properties to optional.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 13 comments.

Show a summary per file
FileDescription
tests/sync/test_screencast.pyAdds sync tests for new Screencast size/cursor/timestamp APIs.
tests/sync/test_page_web_storage.pyNew sync tests for Page.local_storage / session_storage WebStorage access.
tests/sync/test_browsercontext_credentials.pyNew sync tests for BrowserContext.credentials WebAuthn seeding APIs.
tests/async/test_screencast.pyAdds async tests for new Screencast size/cursor/timestamp APIs.
tests/async/test_page_web_storage.pyNew async tests for Page.local_storage / session_storage WebStorage access.
tests/async/test_browsercontext_credentials.pyNew async tests for BrowserContext.credentials WebAuthn seeding APIs.
scripts/generate_api.pyUpdates API generation imports/registrations for newly ported impl classes and structures.
scripts/documentation_provider.pyStops force-marking options-bag properties as optional (fixes requiredness for rp_id).
scripts/build_driver.shUpdates driver source reference comment to main (driver pin still enforced via DRIVER_SHA).
README.mdUpdates embedded browser version markers to match the rolled driver.
playwright/sync_api/_generated.pyRegenerates sync surface: adds new APIs, types, and doc updates.
playwright/sync_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/async_api/_generated.pyRegenerates async surface: adds new APIs, types, and doc updates.
playwright/async_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/_impl/_web_storage.pyIntroduces WebStorage channel wrapper implementation.
playwright/_impl/_screencast.pyAdds Screencast size/cursor params and propagates timestamp into frames.
playwright/_impl/_page.pyAdds local_storage / session_storage properties backed by WebStorage instances.
playwright/_impl/_fetch.pyExposes APIResponse.security_details() / .server_addr() from initializer data.
playwright/_impl/_credentials.pyIntroduces Credentials channel wrapper implementation for WebAuthn.
playwright/_impl/_browser_type.pyAdds artifactsDir plumb-through for CDP connect.
playwright/_impl/_browser_context.pyAdds credentials property backed by new Credentials impl.
playwright/_impl/_api_structures.pyAdds ScreencastSize and VirtualCredential structures; extends ScreencastFrame with timestamp.
DRIVER_SHAUpdates pinned upstream driver commit hash.
.claude/skills/playwright-roll/SKILL.mdUpdates roll skill docs to reference driver/playwright-src checkout path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +69 to +75
def test_show_actions_should_accept_cursor_param(page: Page) -> None:
page.screencast.start(on_frame=lambda f: None)
with page.screencast.show_actions(duration=100, cursor="pointer"):
pass
with page.screencast.show_actions(duration=100, cursor="none"):
pass
page.screencast.stop()
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +56 to +66
def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
size: ScreencastSize = {"width": 800, "height": 600}
page.screencast.start(on_frame=lambda f: received.append(f), size=size)
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +78 to +88
def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
page.screencast.start(on_frame=lambda f: received.append(f))
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +75 to +89
async def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

size: ScreencastSize = {"width": 800, "height": 600}
await page.screencast.start(on_frame=on_frame, size=size)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +103 to +117
async def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

await page.screencast.start(on_frame=on_frame)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment on lines +27 to +31
async def test_local_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('foo', 'bar')")
value = await page.local_storage.get_item("foo")
assert value == "bar"
Comment on lines +34 to +41
async def test_local_storage_items(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('a', '1')")
await page.evaluate("() => localStorage.setItem('b', '2')")
items = await page.local_storage.items()
assert len(items) == 2
assert {"name": "a", "value": "1"} in items
assert {"name": "b", "value": "2"} in items
Comment on lines +60 to +64
async def test_session_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => sessionStorage.setItem('foo', 'bar')")
value = await page.session_storage.get_item("foo")
assert value == "bar"
Comment on lines +31 to +39
result = creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

Comment on lines +34 to +42
result = await creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

- WebStorage tests: seed via dedicated API (set_item) instead of evaluate
- Credentials tests: use auto-generated keys instead of fake placeholders
- Screencast tests: replace size+timestamp test with upstream's
onFrame receives viewport size; add ensureSomeFrames pattern;
remove inconsistent try/finally cleanup
…change
Upstream commit ac7cdd4bd changed FrameExpectResult from {matches, received}
to void — expect returns nothing on success and throws ExpectError on failure.
Updates:
- _assertions.py: _expect_impl now catches driver Error and uses its message
directly; removes unused parse_value and FrameExpectResult imports.
- _frame.py: guard against None result from _expect channel call; change
return type to dict (callers don't use typed fields anymore).
- _locator.py: change _expect return type to dict for consistency.
- tests/{sync,async}/test_assertions.py: update 21 error-message assertions to
match the new upstream format (e.g. 'LocatorAssertions.to_have_text: Expect
failed\nCall log:\n - Expect "to_have_text" with timeout 300ms\n…' instead
of the old Python-formatted "Locator expected to …" / "Actual value: …").
Upstream stopped recording "Wait for event" as separate trace actions.
Remove the corresponding patterns from the two trace viewer tests so
they match the actual 5 (context managers) and 1 (load state) actions
now produced by the driver.
WebKit may report viewportWidth=1002 (instead of 1000) on the
first screencast frame. Use `any()` check instead of iterating
all frames, and increase rAF cycles from 3 to 100 for more
reliable frame generation.
Comment threadplaywright/_impl/_frame.py Outdated
Comment threadplaywright/_impl/_frame.py Outdated
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

Comment threadtests/async/test_assertions.py Outdated
Comment threadplaywright/_impl/_assertions.py Outdated
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to fc893f84e, port new APIsJun 12, 2026
Comment threadplaywright/_impl/_helper.py Outdated
Comment threadplaywright/_impl/_assertions.py
Comment threadplaywright/_impl/_connection.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_tracing.py
@Skn0ttSimon Knott (Skn0tt) linked an issue Jun 15, 2026 that may be closed by this pull request
- port waitForEventInfo to __waitInfo__ so wait actions show in traces again
- source error details from errorDetails with guid replacement, keep raw call log
- restore format_call_log in assertion messages
- drop unnecessary try/finally in new screencast tests
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to fc893f84e, port new APIschore(roll): v1.61Jun 15, 2026
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): v1.61chore(roll): v1.61.0Jun 15, 2026
@Skn0tt
Simon Knott (Skn0tt) merged commit 613c3bf into microsoft:mainJun 15, 2026
34 of 36 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add size option to page.screencast.start()

3 participants

@Skn0tt@dgozman
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

chore(roll): v1.61.0 - #3102

Merged
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b
Jun 15, 2026
Merged

chore(roll): v1.61.0#3102
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b

Conversation

@Skn0tt

@Skn0ttSimon Knott (Skn0tt) commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

This rolls the driver to 1cc5a90cfa3eaa430b1a991963100f95126caa47 (v1.61) and ports the new upstream APIs:

  • APIResponse.security_details / .server_addr
  • Credentials class (WebAuthn) + BrowserContext.credentials
  • WebStorage class + Page.local_storage / .session_storage
  • Screencast.start(size=), .show_actions(cursor=), ScreencastFrame.timestamp
  • BrowserType.connect_over_cdp(artifacts_dir=)

Also picks up the upstream Credentials.create(rp_id, ...) shape, so Python can keep options-bag properties optional. The assertion port keeps the previous failure messages by reading FrameExpectErrorDetails from protocol errors.

The rolling skill is updated to reference driver/playwright-src instead of ~/code/playwright.

Driver SHA: ac7cdd4bdf15f90fe7229243be6b35a53e0296d1 (v1.61.0-next)
New APIs:
- APIResponse.security_details / .server_addr
- Credentials class (WebAuthn) + BrowserContext.credentials
- WebStorage class + Page.local_storage / .session_storage
- Screencast.start(size=), .show_actions(cursor=),
ScreencastFrame.timestamp
- BrowserType.connect_over_cdp(artifacts_dir=)
Also:
- Stop forcing options-bag properties to required=False in
documentation_provider.py (Credentials.create(rp_id=) was
the only case affected)
- Update rolling skill to use driver/playwright-src
- 37 new tests (async + sync)
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in credentials.create(rp_id), this was making rp_id optional, which is incorrect.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

@Skn0ttSimon Knott (Skn0tt) changed the title feat(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to ac7cdd4bd, port new APIs, add testsJun 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Rolls the bundled Playwright driver to a new upstream commit (ac7cdd4b…, v1.61.0-next) and ports newly added upstream APIs into the Python implementation, generated surfaces, and test suite.

Changes:

  • Ported new API surface: APIResponse.security_details() / .server_addr(), WebAuthn Credentials via BrowserContext.credentials, WebStorage via Page.local_storage / .session_storage, and new Screencast options/fields (start(size=), show_actions(cursor=), ScreencastFrame.timestamp).
  • Extended CDP connection API with BrowserType.connect_over_cdp(artifacts_dir=) and updated API generation inputs/types (ScreencastSize, VirtualCredential).
  • Updated driver pin + docs, and adjusted documentation generation to stop forcing all options-bag properties to optional.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 13 comments.

Show a summary per file
FileDescription
tests/sync/test_screencast.pyAdds sync tests for new Screencast size/cursor/timestamp APIs.
tests/sync/test_page_web_storage.pyNew sync tests for Page.local_storage / session_storage WebStorage access.
tests/sync/test_browsercontext_credentials.pyNew sync tests for BrowserContext.credentials WebAuthn seeding APIs.
tests/async/test_screencast.pyAdds async tests for new Screencast size/cursor/timestamp APIs.
tests/async/test_page_web_storage.pyNew async tests for Page.local_storage / session_storage WebStorage access.
tests/async/test_browsercontext_credentials.pyNew async tests for BrowserContext.credentials WebAuthn seeding APIs.
scripts/generate_api.pyUpdates API generation imports/registrations for newly ported impl classes and structures.
scripts/documentation_provider.pyStops force-marking options-bag properties as optional (fixes requiredness for rp_id).
scripts/build_driver.shUpdates driver source reference comment to main (driver pin still enforced via DRIVER_SHA).
README.mdUpdates embedded browser version markers to match the rolled driver.
playwright/sync_api/_generated.pyRegenerates sync surface: adds new APIs, types, and doc updates.
playwright/sync_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/async_api/_generated.pyRegenerates async surface: adds new APIs, types, and doc updates.
playwright/async_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/_impl/_web_storage.pyIntroduces WebStorage channel wrapper implementation.
playwright/_impl/_screencast.pyAdds Screencast size/cursor params and propagates timestamp into frames.
playwright/_impl/_page.pyAdds local_storage / session_storage properties backed by WebStorage instances.
playwright/_impl/_fetch.pyExposes APIResponse.security_details() / .server_addr() from initializer data.
playwright/_impl/_credentials.pyIntroduces Credentials channel wrapper implementation for WebAuthn.
playwright/_impl/_browser_type.pyAdds artifactsDir plumb-through for CDP connect.
playwright/_impl/_browser_context.pyAdds credentials property backed by new Credentials impl.
playwright/_impl/_api_structures.pyAdds ScreencastSize and VirtualCredential structures; extends ScreencastFrame with timestamp.
DRIVER_SHAUpdates pinned upstream driver commit hash.
.claude/skills/playwright-roll/SKILL.mdUpdates roll skill docs to reference driver/playwright-src checkout path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +69 to +75
def test_show_actions_should_accept_cursor_param(page: Page) -> None:
page.screencast.start(on_frame=lambda f: None)
with page.screencast.show_actions(duration=100, cursor="pointer"):
pass
with page.screencast.show_actions(duration=100, cursor="none"):
pass
page.screencast.stop()
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +56 to +66
def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
size: ScreencastSize = {"width": 800, "height": 600}
page.screencast.start(on_frame=lambda f: received.append(f), size=size)
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +78 to +88
def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
page.screencast.start(on_frame=lambda f: received.append(f))
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +75 to +89
async def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

size: ScreencastSize = {"width": 800, "height": 600}
await page.screencast.start(on_frame=on_frame, size=size)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +103 to +117
async def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

await page.screencast.start(on_frame=on_frame)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment on lines +27 to +31
async def test_local_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('foo', 'bar')")
value = await page.local_storage.get_item("foo")
assert value == "bar"
Comment on lines +34 to +41
async def test_local_storage_items(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('a', '1')")
await page.evaluate("() => localStorage.setItem('b', '2')")
items = await page.local_storage.items()
assert len(items) == 2
assert {"name": "a", "value": "1"} in items
assert {"name": "b", "value": "2"} in items
Comment on lines +60 to +64
async def test_session_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => sessionStorage.setItem('foo', 'bar')")
value = await page.session_storage.get_item("foo")
assert value == "bar"
Comment on lines +31 to +39
result = creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

Comment on lines +34 to +42
result = await creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

- WebStorage tests: seed via dedicated API (set_item) instead of evaluate
- Credentials tests: use auto-generated keys instead of fake placeholders
- Screencast tests: replace size+timestamp test with upstream's
onFrame receives viewport size; add ensureSomeFrames pattern;
remove inconsistent try/finally cleanup
…change
Upstream commit ac7cdd4bd changed FrameExpectResult from {matches, received}
to void — expect returns nothing on success and throws ExpectError on failure.
Updates:
- _assertions.py: _expect_impl now catches driver Error and uses its message
directly; removes unused parse_value and FrameExpectResult imports.
- _frame.py: guard against None result from _expect channel call; change
return type to dict (callers don't use typed fields anymore).
- _locator.py: change _expect return type to dict for consistency.
- tests/{sync,async}/test_assertions.py: update 21 error-message assertions to
match the new upstream format (e.g. 'LocatorAssertions.to_have_text: Expect
failed\nCall log:\n - Expect "to_have_text" with timeout 300ms\n…' instead
of the old Python-formatted "Locator expected to …" / "Actual value: …").
Upstream stopped recording "Wait for event" as separate trace actions.
Remove the corresponding patterns from the two trace viewer tests so
they match the actual 5 (context managers) and 1 (load state) actions
now produced by the driver.
WebKit may report viewportWidth=1002 (instead of 1000) on the
first screencast frame. Use `any()` check instead of iterating
all frames, and increase rAF cycles from 3 to 100 for more
reliable frame generation.
Comment threadplaywright/_impl/_frame.py Outdated
Comment threadplaywright/_impl/_frame.py Outdated
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

Comment threadtests/async/test_assertions.py Outdated
Comment threadplaywright/_impl/_assertions.py Outdated
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to fc893f84e, port new APIsJun 12, 2026
Comment threadplaywright/_impl/_helper.py Outdated
Comment threadplaywright/_impl/_assertions.py
Comment threadplaywright/_impl/_connection.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_tracing.py
@Skn0ttSimon Knott (Skn0tt) linked an issue Jun 15, 2026 that may be closed by this pull request
- port waitForEventInfo to __waitInfo__ so wait actions show in traces again
- source error details from errorDetails with guid replacement, keep raw call log
- restore format_call_log in assertion messages
- drop unnecessary try/finally in new screencast tests
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to fc893f84e, port new APIschore(roll): v1.61Jun 15, 2026
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): v1.61chore(roll): v1.61.0Jun 15, 2026
@Skn0tt
Simon Knott (Skn0tt) merged commit 613c3bf into microsoft:mainJun 15, 2026
34 of 36 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add size option to page.screencast.start()

3 participants

@Skn0tt@dgozman
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(roll): v1.61.0 - #3102

Merged
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b
Jun 15, 2026
Merged

chore(roll): v1.61.0#3102
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b

Conversation

@Skn0tt

@Skn0ttSimon Knott (Skn0tt) commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

This rolls the driver to 1cc5a90cfa3eaa430b1a991963100f95126caa47 (v1.61) and ports the new upstream APIs:

  • APIResponse.security_details / .server_addr
  • Credentials class (WebAuthn) + BrowserContext.credentials
  • WebStorage class + Page.local_storage / .session_storage
  • Screencast.start(size=), .show_actions(cursor=), ScreencastFrame.timestamp
  • BrowserType.connect_over_cdp(artifacts_dir=)

Also picks up the upstream Credentials.create(rp_id, ...) shape, so Python can keep options-bag properties optional. The assertion port keeps the previous failure messages by reading FrameExpectErrorDetails from protocol errors.

The rolling skill is updated to reference driver/playwright-src instead of ~/code/playwright.

Driver SHA: ac7cdd4bdf15f90fe7229243be6b35a53e0296d1 (v1.61.0-next)
New APIs:
- APIResponse.security_details / .server_addr
- Credentials class (WebAuthn) + BrowserContext.credentials
- WebStorage class + Page.local_storage / .session_storage
- Screencast.start(size=), .show_actions(cursor=),
ScreencastFrame.timestamp
- BrowserType.connect_over_cdp(artifacts_dir=)
Also:
- Stop forcing options-bag properties to required=False in
documentation_provider.py (Credentials.create(rp_id=) was
the only case affected)
- Update rolling skill to use driver/playwright-src
- 37 new tests (async + sync)
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in credentials.create(rp_id), this was making rp_id optional, which is incorrect.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

@Skn0ttSimon Knott (Skn0tt) changed the title feat(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to ac7cdd4bd, port new APIs, add testsJun 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Rolls the bundled Playwright driver to a new upstream commit (ac7cdd4b…, v1.61.0-next) and ports newly added upstream APIs into the Python implementation, generated surfaces, and test suite.

Changes:

  • Ported new API surface: APIResponse.security_details() / .server_addr(), WebAuthn Credentials via BrowserContext.credentials, WebStorage via Page.local_storage / .session_storage, and new Screencast options/fields (start(size=), show_actions(cursor=), ScreencastFrame.timestamp).
  • Extended CDP connection API with BrowserType.connect_over_cdp(artifacts_dir=) and updated API generation inputs/types (ScreencastSize, VirtualCredential).
  • Updated driver pin + docs, and adjusted documentation generation to stop forcing all options-bag properties to optional.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 13 comments.

Show a summary per file
FileDescription
tests/sync/test_screencast.pyAdds sync tests for new Screencast size/cursor/timestamp APIs.
tests/sync/test_page_web_storage.pyNew sync tests for Page.local_storage / session_storage WebStorage access.
tests/sync/test_browsercontext_credentials.pyNew sync tests for BrowserContext.credentials WebAuthn seeding APIs.
tests/async/test_screencast.pyAdds async tests for new Screencast size/cursor/timestamp APIs.
tests/async/test_page_web_storage.pyNew async tests for Page.local_storage / session_storage WebStorage access.
tests/async/test_browsercontext_credentials.pyNew async tests for BrowserContext.credentials WebAuthn seeding APIs.
scripts/generate_api.pyUpdates API generation imports/registrations for newly ported impl classes and structures.
scripts/documentation_provider.pyStops force-marking options-bag properties as optional (fixes requiredness for rp_id).
scripts/build_driver.shUpdates driver source reference comment to main (driver pin still enforced via DRIVER_SHA).
README.mdUpdates embedded browser version markers to match the rolled driver.
playwright/sync_api/_generated.pyRegenerates sync surface: adds new APIs, types, and doc updates.
playwright/sync_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/async_api/_generated.pyRegenerates async surface: adds new APIs, types, and doc updates.
playwright/async_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/_impl/_web_storage.pyIntroduces WebStorage channel wrapper implementation.
playwright/_impl/_screencast.pyAdds Screencast size/cursor params and propagates timestamp into frames.
playwright/_impl/_page.pyAdds local_storage / session_storage properties backed by WebStorage instances.
playwright/_impl/_fetch.pyExposes APIResponse.security_details() / .server_addr() from initializer data.
playwright/_impl/_credentials.pyIntroduces Credentials channel wrapper implementation for WebAuthn.
playwright/_impl/_browser_type.pyAdds artifactsDir plumb-through for CDP connect.
playwright/_impl/_browser_context.pyAdds credentials property backed by new Credentials impl.
playwright/_impl/_api_structures.pyAdds ScreencastSize and VirtualCredential structures; extends ScreencastFrame with timestamp.
DRIVER_SHAUpdates pinned upstream driver commit hash.
.claude/skills/playwright-roll/SKILL.mdUpdates roll skill docs to reference driver/playwright-src checkout path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +69 to +75
def test_show_actions_should_accept_cursor_param(page: Page) -> None:
page.screencast.start(on_frame=lambda f: None)
with page.screencast.show_actions(duration=100, cursor="pointer"):
pass
with page.screencast.show_actions(duration=100, cursor="none"):
pass
page.screencast.stop()
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +56 to +66
def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
size: ScreencastSize = {"width": 800, "height": 600}
page.screencast.start(on_frame=lambda f: received.append(f), size=size)
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +78 to +88
def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
page.screencast.start(on_frame=lambda f: received.append(f))
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +75 to +89
async def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

size: ScreencastSize = {"width": 800, "height": 600}
await page.screencast.start(on_frame=on_frame, size=size)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +103 to +117
async def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

await page.screencast.start(on_frame=on_frame)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment on lines +27 to +31
async def test_local_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('foo', 'bar')")
value = await page.local_storage.get_item("foo")
assert value == "bar"
Comment on lines +34 to +41
async def test_local_storage_items(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('a', '1')")
await page.evaluate("() => localStorage.setItem('b', '2')")
items = await page.local_storage.items()
assert len(items) == 2
assert {"name": "a", "value": "1"} in items
assert {"name": "b", "value": "2"} in items
Comment on lines +60 to +64
async def test_session_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => sessionStorage.setItem('foo', 'bar')")
value = await page.session_storage.get_item("foo")
assert value == "bar"
Comment on lines +31 to +39
result = creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

Comment on lines +34 to +42
result = await creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

- WebStorage tests: seed via dedicated API (set_item) instead of evaluate
- Credentials tests: use auto-generated keys instead of fake placeholders
- Screencast tests: replace size+timestamp test with upstream's
onFrame receives viewport size; add ensureSomeFrames pattern;
remove inconsistent try/finally cleanup
…change
Upstream commit ac7cdd4bd changed FrameExpectResult from {matches, received}
to void — expect returns nothing on success and throws ExpectError on failure.
Updates:
- _assertions.py: _expect_impl now catches driver Error and uses its message
directly; removes unused parse_value and FrameExpectResult imports.
- _frame.py: guard against None result from _expect channel call; change
return type to dict (callers don't use typed fields anymore).
- _locator.py: change _expect return type to dict for consistency.
- tests/{sync,async}/test_assertions.py: update 21 error-message assertions to
match the new upstream format (e.g. 'LocatorAssertions.to_have_text: Expect
failed\nCall log:\n - Expect "to_have_text" with timeout 300ms\n…' instead
of the old Python-formatted "Locator expected to …" / "Actual value: …").
Upstream stopped recording "Wait for event" as separate trace actions.
Remove the corresponding patterns from the two trace viewer tests so
they match the actual 5 (context managers) and 1 (load state) actions
now produced by the driver.
WebKit may report viewportWidth=1002 (instead of 1000) on the
first screencast frame. Use `any()` check instead of iterating
all frames, and increase rAF cycles from 3 to 100 for more
reliable frame generation.
Comment threadplaywright/_impl/_frame.py Outdated
Comment threadplaywright/_impl/_frame.py Outdated
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

Comment threadtests/async/test_assertions.py Outdated
Comment threadplaywright/_impl/_assertions.py Outdated
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to fc893f84e, port new APIsJun 12, 2026
Comment threadplaywright/_impl/_helper.py Outdated
Comment threadplaywright/_impl/_assertions.py
Comment threadplaywright/_impl/_connection.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_tracing.py
@Skn0ttSimon Knott (Skn0tt) linked an issue Jun 15, 2026 that may be closed by this pull request
- port waitForEventInfo to __waitInfo__ so wait actions show in traces again
- source error details from errorDetails with guid replacement, keep raw call log
- restore format_call_log in assertion messages
- drop unnecessary try/finally in new screencast tests
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to fc893f84e, port new APIschore(roll): v1.61Jun 15, 2026
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): v1.61chore(roll): v1.61.0Jun 15, 2026
@Skn0tt
Simon Knott (Skn0tt) merged commit 613c3bf into microsoft:mainJun 15, 2026
34 of 36 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add size option to page.screencast.start()

3 participants

@Skn0tt@dgozman
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(roll): v1.61.0 - #3102

Merged
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b
Jun 15, 2026
Merged

chore(roll): v1.61.0#3102
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b

Conversation

@Skn0tt

@Skn0ttSimon Knott (Skn0tt) commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

This rolls the driver to 1cc5a90cfa3eaa430b1a991963100f95126caa47 (v1.61) and ports the new upstream APIs:

  • APIResponse.security_details / .server_addr
  • Credentials class (WebAuthn) + BrowserContext.credentials
  • WebStorage class + Page.local_storage / .session_storage
  • Screencast.start(size=), .show_actions(cursor=), ScreencastFrame.timestamp
  • BrowserType.connect_over_cdp(artifacts_dir=)

Also picks up the upstream Credentials.create(rp_id, ...) shape, so Python can keep options-bag properties optional. The assertion port keeps the previous failure messages by reading FrameExpectErrorDetails from protocol errors.

The rolling skill is updated to reference driver/playwright-src instead of ~/code/playwright.

Driver SHA: ac7cdd4bdf15f90fe7229243be6b35a53e0296d1 (v1.61.0-next)
New APIs:
- APIResponse.security_details / .server_addr
- Credentials class (WebAuthn) + BrowserContext.credentials
- WebStorage class + Page.local_storage / .session_storage
- Screencast.start(size=), .show_actions(cursor=),
ScreencastFrame.timestamp
- BrowserType.connect_over_cdp(artifacts_dir=)
Also:
- Stop forcing options-bag properties to required=False in
documentation_provider.py (Credentials.create(rp_id=) was
the only case affected)
- Update rolling skill to use driver/playwright-src
- 37 new tests (async + sync)
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in credentials.create(rp_id), this was making rp_id optional, which is incorrect.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

@Skn0ttSimon Knott (Skn0tt) changed the title feat(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to ac7cdd4bd, port new APIs, add testsJun 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Rolls the bundled Playwright driver to a new upstream commit (ac7cdd4b…, v1.61.0-next) and ports newly added upstream APIs into the Python implementation, generated surfaces, and test suite.

Changes:

  • Ported new API surface: APIResponse.security_details() / .server_addr(), WebAuthn Credentials via BrowserContext.credentials, WebStorage via Page.local_storage / .session_storage, and new Screencast options/fields (start(size=), show_actions(cursor=), ScreencastFrame.timestamp).
  • Extended CDP connection API with BrowserType.connect_over_cdp(artifacts_dir=) and updated API generation inputs/types (ScreencastSize, VirtualCredential).
  • Updated driver pin + docs, and adjusted documentation generation to stop forcing all options-bag properties to optional.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 13 comments.

Show a summary per file
FileDescription
tests/sync/test_screencast.pyAdds sync tests for new Screencast size/cursor/timestamp APIs.
tests/sync/test_page_web_storage.pyNew sync tests for Page.local_storage / session_storage WebStorage access.
tests/sync/test_browsercontext_credentials.pyNew sync tests for BrowserContext.credentials WebAuthn seeding APIs.
tests/async/test_screencast.pyAdds async tests for new Screencast size/cursor/timestamp APIs.
tests/async/test_page_web_storage.pyNew async tests for Page.local_storage / session_storage WebStorage access.
tests/async/test_browsercontext_credentials.pyNew async tests for BrowserContext.credentials WebAuthn seeding APIs.
scripts/generate_api.pyUpdates API generation imports/registrations for newly ported impl classes and structures.
scripts/documentation_provider.pyStops force-marking options-bag properties as optional (fixes requiredness for rp_id).
scripts/build_driver.shUpdates driver source reference comment to main (driver pin still enforced via DRIVER_SHA).
README.mdUpdates embedded browser version markers to match the rolled driver.
playwright/sync_api/_generated.pyRegenerates sync surface: adds new APIs, types, and doc updates.
playwright/sync_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/async_api/_generated.pyRegenerates async surface: adds new APIs, types, and doc updates.
playwright/async_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/_impl/_web_storage.pyIntroduces WebStorage channel wrapper implementation.
playwright/_impl/_screencast.pyAdds Screencast size/cursor params and propagates timestamp into frames.
playwright/_impl/_page.pyAdds local_storage / session_storage properties backed by WebStorage instances.
playwright/_impl/_fetch.pyExposes APIResponse.security_details() / .server_addr() from initializer data.
playwright/_impl/_credentials.pyIntroduces Credentials channel wrapper implementation for WebAuthn.
playwright/_impl/_browser_type.pyAdds artifactsDir plumb-through for CDP connect.
playwright/_impl/_browser_context.pyAdds credentials property backed by new Credentials impl.
playwright/_impl/_api_structures.pyAdds ScreencastSize and VirtualCredential structures; extends ScreencastFrame with timestamp.
DRIVER_SHAUpdates pinned upstream driver commit hash.
.claude/skills/playwright-roll/SKILL.mdUpdates roll skill docs to reference driver/playwright-src checkout path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +69 to +75
def test_show_actions_should_accept_cursor_param(page: Page) -> None:
page.screencast.start(on_frame=lambda f: None)
with page.screencast.show_actions(duration=100, cursor="pointer"):
pass
with page.screencast.show_actions(duration=100, cursor="none"):
pass
page.screencast.stop()
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +56 to +66
def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
size: ScreencastSize = {"width": 800, "height": 600}
page.screencast.start(on_frame=lambda f: received.append(f), size=size)
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +78 to +88
def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
page.screencast.start(on_frame=lambda f: received.append(f))
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +75 to +89
async def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

size: ScreencastSize = {"width": 800, "height": 600}
await page.screencast.start(on_frame=on_frame, size=size)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +103 to +117
async def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

await page.screencast.start(on_frame=on_frame)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment on lines +27 to +31
async def test_local_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('foo', 'bar')")
value = await page.local_storage.get_item("foo")
assert value == "bar"
Comment on lines +34 to +41
async def test_local_storage_items(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('a', '1')")
await page.evaluate("() => localStorage.setItem('b', '2')")
items = await page.local_storage.items()
assert len(items) == 2
assert {"name": "a", "value": "1"} in items
assert {"name": "b", "value": "2"} in items
Comment on lines +60 to +64
async def test_session_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => sessionStorage.setItem('foo', 'bar')")
value = await page.session_storage.get_item("foo")
assert value == "bar"
Comment on lines +31 to +39
result = creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

Comment on lines +34 to +42
result = await creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

- WebStorage tests: seed via dedicated API (set_item) instead of evaluate
- Credentials tests: use auto-generated keys instead of fake placeholders
- Screencast tests: replace size+timestamp test with upstream's
onFrame receives viewport size; add ensureSomeFrames pattern;
remove inconsistent try/finally cleanup
…change
Upstream commit ac7cdd4bd changed FrameExpectResult from {matches, received}
to void — expect returns nothing on success and throws ExpectError on failure.
Updates:
- _assertions.py: _expect_impl now catches driver Error and uses its message
directly; removes unused parse_value and FrameExpectResult imports.
- _frame.py: guard against None result from _expect channel call; change
return type to dict (callers don't use typed fields anymore).
- _locator.py: change _expect return type to dict for consistency.
- tests/{sync,async}/test_assertions.py: update 21 error-message assertions to
match the new upstream format (e.g. 'LocatorAssertions.to_have_text: Expect
failed\nCall log:\n - Expect "to_have_text" with timeout 300ms\n…' instead
of the old Python-formatted "Locator expected to …" / "Actual value: …").
Upstream stopped recording "Wait for event" as separate trace actions.
Remove the corresponding patterns from the two trace viewer tests so
they match the actual 5 (context managers) and 1 (load state) actions
now produced by the driver.
WebKit may report viewportWidth=1002 (instead of 1000) on the
first screencast frame. Use `any()` check instead of iterating
all frames, and increase rAF cycles from 3 to 100 for more
reliable frame generation.
Comment threadplaywright/_impl/_frame.py Outdated
Comment threadplaywright/_impl/_frame.py Outdated
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

Comment threadtests/async/test_assertions.py Outdated
Comment threadplaywright/_impl/_assertions.py Outdated
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to fc893f84e, port new APIsJun 12, 2026
Comment threadplaywright/_impl/_helper.py Outdated
Comment threadplaywright/_impl/_assertions.py
Comment threadplaywright/_impl/_connection.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_tracing.py
@Skn0ttSimon Knott (Skn0tt) linked an issue Jun 15, 2026 that may be closed by this pull request
- port waitForEventInfo to __waitInfo__ so wait actions show in traces again
- source error details from errorDetails with guid replacement, keep raw call log
- restore format_call_log in assertion messages
- drop unnecessary try/finally in new screencast tests
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to fc893f84e, port new APIschore(roll): v1.61Jun 15, 2026
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): v1.61chore(roll): v1.61.0Jun 15, 2026
@Skn0tt
Simon Knott (Skn0tt) merged commit 613c3bf into microsoft:mainJun 15, 2026
34 of 36 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add size option to page.screencast.start()

3 participants

@Skn0tt@dgozman
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

chore(roll): v1.61.0 - #3102

Merged
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b
Jun 15, 2026
Merged

chore(roll): v1.61.0#3102
Simon Knott (Skn0tt) merged 11 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b

Conversation

@Skn0tt

@Skn0ttSimon Knott (Skn0tt) commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

This rolls the driver to 1cc5a90cfa3eaa430b1a991963100f95126caa47 (v1.61) and ports the new upstream APIs:

  • APIResponse.security_details / .server_addr
  • Credentials class (WebAuthn) + BrowserContext.credentials
  • WebStorage class + Page.local_storage / .session_storage
  • Screencast.start(size=), .show_actions(cursor=), ScreencastFrame.timestamp
  • BrowserType.connect_over_cdp(artifacts_dir=)

Also picks up the upstream Credentials.create(rp_id, ...) shape, so Python can keep options-bag properties optional. The assertion port keeps the previous failure messages by reading FrameExpectErrorDetails from protocol errors.

The rolling skill is updated to reference driver/playwright-src instead of ~/code/playwright.

Driver SHA: ac7cdd4bdf15f90fe7229243be6b35a53e0296d1 (v1.61.0-next)
New APIs:
- APIResponse.security_details / .server_addr
- Credentials class (WebAuthn) + BrowserContext.credentials
- WebStorage class + Page.local_storage / .session_storage
- Screencast.start(size=), .show_actions(cursor=),
ScreencastFrame.timestamp
- BrowserType.connect_over_cdp(artifacts_dir=)
Also:
- Stop forcing options-bag properties to required=False in
documentation_provider.py (Credentials.create(rp_id=) was
the only case affected)
- Update rolling skill to use driver/playwright-src
- 37 new tests (async + sync)
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in credentials.create(rp_id), this was making rp_id optional, which is incorrect.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

@Skn0ttSimon Knott (Skn0tt) changed the title feat(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to ac7cdd4bd, port new APIs, add testsJun 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Rolls the bundled Playwright driver to a new upstream commit (ac7cdd4b…, v1.61.0-next) and ports newly added upstream APIs into the Python implementation, generated surfaces, and test suite.

Changes:

  • Ported new API surface: APIResponse.security_details() / .server_addr(), WebAuthn Credentials via BrowserContext.credentials, WebStorage via Page.local_storage / .session_storage, and new Screencast options/fields (start(size=), show_actions(cursor=), ScreencastFrame.timestamp).
  • Extended CDP connection API with BrowserType.connect_over_cdp(artifacts_dir=) and updated API generation inputs/types (ScreencastSize, VirtualCredential).
  • Updated driver pin + docs, and adjusted documentation generation to stop forcing all options-bag properties to optional.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 13 comments.

Show a summary per file
FileDescription
tests/sync/test_screencast.pyAdds sync tests for new Screencast size/cursor/timestamp APIs.
tests/sync/test_page_web_storage.pyNew sync tests for Page.local_storage / session_storage WebStorage access.
tests/sync/test_browsercontext_credentials.pyNew sync tests for BrowserContext.credentials WebAuthn seeding APIs.
tests/async/test_screencast.pyAdds async tests for new Screencast size/cursor/timestamp APIs.
tests/async/test_page_web_storage.pyNew async tests for Page.local_storage / session_storage WebStorage access.
tests/async/test_browsercontext_credentials.pyNew async tests for BrowserContext.credentials WebAuthn seeding APIs.
scripts/generate_api.pyUpdates API generation imports/registrations for newly ported impl classes and structures.
scripts/documentation_provider.pyStops force-marking options-bag properties as optional (fixes requiredness for rp_id).
scripts/build_driver.shUpdates driver source reference comment to main (driver pin still enforced via DRIVER_SHA).
README.mdUpdates embedded browser version markers to match the rolled driver.
playwright/sync_api/_generated.pyRegenerates sync surface: adds new APIs, types, and doc updates.
playwright/sync_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/async_api/_generated.pyRegenerates async surface: adds new APIs, types, and doc updates.
playwright/async_api/init.pyRe-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/_impl/_web_storage.pyIntroduces WebStorage channel wrapper implementation.
playwright/_impl/_screencast.pyAdds Screencast size/cursor params and propagates timestamp into frames.
playwright/_impl/_page.pyAdds local_storage / session_storage properties backed by WebStorage instances.
playwright/_impl/_fetch.pyExposes APIResponse.security_details() / .server_addr() from initializer data.
playwright/_impl/_credentials.pyIntroduces Credentials channel wrapper implementation for WebAuthn.
playwright/_impl/_browser_type.pyAdds artifactsDir plumb-through for CDP connect.
playwright/_impl/_browser_context.pyAdds credentials property backed by new Credentials impl.
playwright/_impl/_api_structures.pyAdds ScreencastSize and VirtualCredential structures; extends ScreencastFrame with timestamp.
DRIVER_SHAUpdates pinned upstream driver commit hash.
.claude/skills/playwright-roll/SKILL.mdUpdates roll skill docs to reference driver/playwright-src checkout path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +69 to +75
def test_show_actions_should_accept_cursor_param(page: Page) -> None:
page.screencast.start(on_frame=lambda f: None)
with page.screencast.show_actions(duration=100, cursor="pointer"):
pass
with page.screencast.show_actions(duration=100, cursor="none"):
pass
page.screencast.stop()
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +56 to +66
def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
size: ScreencastSize = {"width": 800, "height": 600}
page.screencast.start(on_frame=lambda f: received.append(f), size=size)
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
Comment threadtests/sync/test_screencast.py Outdated
Comment on lines +78 to +88
def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
page.screencast.start(on_frame=lambda f: received.append(f))
page.goto(server.EMPTY_PAGE)
page.screenshot()
deadline = time.time() + 10
while not received and time.time() < deadline:
page.wait_for_timeout(100)
page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +75 to +89
async def test_start_should_accept_size_param(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

size: ScreencastSize = {"width": 800, "height": 600}
await page.screencast.start(on_frame=on_frame, size=size)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
Comment threadtests/async/test_screencast.py Outdated
Comment on lines +103 to +117
async def test_frames_should_include_timestamp(page: Page, server: Server) -> None:
received: list = []
event = asyncio.Event()

def on_frame(frame: ScreencastFrame) -> None:
received.append(frame)
event.set()

await page.screencast.start(on_frame=on_frame)
await page.goto(server.EMPTY_PAGE)
await page.screenshot()
await asyncio.wait_for(event.wait(), timeout=10)
await page.screencast.stop()
assert len(received) >= 1
assert received[0]["timestamp"] > 0
Comment on lines +27 to +31
async def test_local_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('foo', 'bar')")
value = await page.local_storage.get_item("foo")
assert value == "bar"
Comment on lines +34 to +41
async def test_local_storage_items(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => localStorage.setItem('a', '1')")
await page.evaluate("() => localStorage.setItem('b', '2')")
items = await page.local_storage.items()
assert len(items) == 2
assert {"name": "a", "value": "1"} in items
assert {"name": "b", "value": "2"} in items
Comment on lines +60 to +64
async def test_session_storage_set_and_get_item(page: Page, server: Server) -> None:
await page.goto(server.EMPTY_PAGE)
await page.evaluate("() => sessionStorage.setItem('foo', 'bar')")
value = await page.session_storage.get_item("foo")
assert value == "bar"
Comment on lines +31 to +39
result = creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

Comment on lines +34 to +42
result = await creds.create(
rp_id="localhost",
id="test-credential-id",
private_key="private-key-data",
public_key="public-key-data",
)
assert result["id"] == "test-credential-id"
assert result["rpId"] == "localhost"

- WebStorage tests: seed via dedicated API (set_item) instead of evaluate
- Credentials tests: use auto-generated keys instead of fake placeholders
- Screencast tests: replace size+timestamp test with upstream's
onFrame receives viewport size; add ensureSomeFrames pattern;
remove inconsistent try/finally cleanup
…change
Upstream commit ac7cdd4bd changed FrameExpectResult from {matches, received}
to void — expect returns nothing on success and throws ExpectError on failure.
Updates:
- _assertions.py: _expect_impl now catches driver Error and uses its message
directly; removes unused parse_value and FrameExpectResult imports.
- _frame.py: guard against None result from _expect channel call; change
return type to dict (callers don't use typed fields anymore).
- _locator.py: change _expect return type to dict for consistency.
- tests/{sync,async}/test_assertions.py: update 21 error-message assertions to
match the new upstream format (e.g. 'LocatorAssertions.to_have_text: Expect
failed\nCall log:\n - Expect "to_have_text" with timeout 300ms\n…' instead
of the old Python-formatted "Locator expected to …" / "Actual value: …").
Upstream stopped recording "Wait for event" as separate trace actions.
Remove the corresponding patterns from the two trace viewer tests so
they match the actual 5 (context managers) and 1 (load state) actions
now produced by the driver.
WebKit may report viewportWidth=1002 (instead of 1000) on the
first screencast frame. Use `any()` check instead of iterating
all frames, and increase rAF cycles from 3 to 100 for more
reliable frame generation.
Comment threadplaywright/_impl/_frame.py Outdated
Comment threadplaywright/_impl/_frame.py Outdated
option = self_or_override(option)
option_name = to_snake_case(name_or_alias(option))
option["name"] = option_name
option["required"] = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch! We never had non-optional options. Let's discuss this on the meeting.

Comment threadtests/async/test_assertions.py Outdated
Comment threadplaywright/_impl/_assertions.py Outdated
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to ac7cdd4bd, port new APIs, add testschore(roll): update driver to fc893f84e, port new APIsJun 12, 2026
Comment threadplaywright/_impl/_helper.py Outdated
Comment threadplaywright/_impl/_assertions.py
Comment threadplaywright/_impl/_connection.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_screencast.py Outdated
Comment threadtests/async/test_tracing.py
@Skn0ttSimon Knott (Skn0tt) linked an issue Jun 15, 2026 that may be closed by this pull request
- port waitForEventInfo to __waitInfo__ so wait actions show in traces again
- source error details from errorDetails with guid replacement, keep raw call log
- restore format_call_log in assertion messages
- drop unnecessary try/finally in new screencast tests
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): update driver to fc893f84e, port new APIschore(roll): v1.61Jun 15, 2026
@Skn0ttSimon Knott (Skn0tt) changed the title chore(roll): v1.61chore(roll): v1.61.0Jun 15, 2026
@Skn0tt
Simon Knott (Skn0tt) merged commit 613c3bf into microsoft:mainJun 15, 2026
34 of 36 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add size option to page.screencast.start()

3 participants

@Skn0tt@dgozman