Skip to content

chore(roll): update driver to ac7cdd4bd, port new APIs, add tests#3102

Open
Skn0tt wants to merge 5 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b
Open

chore(roll): update driver to ac7cdd4bd, port new APIs, add tests#3102
Skn0tt wants to merge 5 commits into
microsoft:mainfrom
Skn0tt:roll-to-ac7cdd4b

Conversation

@Skn0tt

@Skn0tt Skn0tt commented Jun 10, 2026

Copy link
Copy Markdown
Member

This rolls the driver to ac7cdd4bdf15f90fe7229243be6b35a53e0296d1 (latest main, v1.61.0-next) 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 removes the option["required"] = False line in documentation_provider.py that was forcing all options-bag properties to optional. Credentials.create(rp_id=) was the only case affected — it's now correctly required.

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

37 new tests (async + sync).

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
Member Author

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.

@Skn0tt Skn0tt changed the title feat(roll): update driver to ac7cdd4bd, port new APIs, add tests chore(roll): update driver to ac7cdd4bd, port new APIs, add tests Jun 10, 2026

Copilot AI 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
File Description
tests/sync/test_screencast.py Adds sync tests for new Screencast size/cursor/timestamp APIs.
tests/sync/test_page_web_storage.py New sync tests for Page.local_storage / session_storage WebStorage access.
tests/sync/test_browsercontext_credentials.py New sync tests for BrowserContext.credentials WebAuthn seeding APIs.
tests/async/test_screencast.py Adds async tests for new Screencast size/cursor/timestamp APIs.
tests/async/test_page_web_storage.py New async tests for Page.local_storage / session_storage WebStorage access.
tests/async/test_browsercontext_credentials.py New async tests for BrowserContext.credentials WebAuthn seeding APIs.
scripts/generate_api.py Updates API generation imports/registrations for newly ported impl classes and structures.
scripts/documentation_provider.py Stops force-marking options-bag properties as optional (fixes requiredness for rp_id).
scripts/build_driver.sh Updates driver source reference comment to main (driver pin still enforced via DRIVER_SHA).
README.md Updates embedded browser version markers to match the rolled driver.
playwright/sync_api/_generated.py Regenerates sync surface: adds new APIs, types, and doc updates.
playwright/sync_api/init.py Re-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/async_api/_generated.py Regenerates async surface: adds new APIs, types, and doc updates.
playwright/async_api/init.py Re-exports new public TypedDict structures (ScreencastSize, VirtualCredential).
playwright/_impl/_web_storage.py Introduces WebStorage channel wrapper implementation.
playwright/_impl/_screencast.py Adds Screencast size/cursor params and propagates timestamp into frames.
playwright/_impl/_page.py Adds local_storage / session_storage properties backed by WebStorage instances.
playwright/_impl/_fetch.py Exposes APIResponse.security_details() / .server_addr() from initializer data.
playwright/_impl/_credentials.py Introduces Credentials channel wrapper implementation for WebAuthn.
playwright/_impl/_browser_type.py Adds artifactsDir plumb-through for CDP connect.
playwright/_impl/_browser_context.py Adds credentials property backed by new Credentials impl.
playwright/_impl/_api_structures.py Adds ScreencastSize and VirtualCredential structures; extends ScreencastFrame with timestamp.
DRIVER_SHA Updates pinned upstream driver commit hash.
.claude/skills/playwright-roll/SKILL.md Updates 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 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 thread tests/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 thread tests/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 thread tests/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 thread tests/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"

Skn0tt added 4 commits June 10, 2026 11:13
- 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.
Sign up for free to 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.

2 participants