From d9d68e12fa2a86cc312a42fc401dd5fea6959682 Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov Date: Wed, 25 Sep 2024 10:56:29 +0200 Subject: [PATCH 1/3] build(spec): update WebDriverBiDi types --- .github/workflows/update-bidi-types.yml | 10 +-- README.md | 2 +- src/bidiMapper/CommandProcessor.ts | 5 ++ .../modules/context/BrowsingContextImpl.ts | 2 + .../generated/webdriver-bidi.ts | 73 +++++++++++++++++++ src/protocol/generated/webdriver-bidi.ts | 58 ++++++++++++++- 6 files changed, 143 insertions(+), 7 deletions(-) diff --git a/.github/workflows/update-bidi-types.yml b/.github/workflows/update-bidi-types.yml index 387fcb69a7..fd7cc066e5 100644 --- a/.github/workflows/update-bidi-types.yml +++ b/.github/workflows/update-bidi-types.yml @@ -1,6 +1,6 @@ -# This workflow will update the WebdriverBidi types. +# This workflow will update the WebdriverBiDi types. -name: Update WebdriverBidi types +name: Update WebdriverBiDi types # Declare default permissions as read only. permissions: read-all @@ -24,7 +24,7 @@ on: jobs: build: - name: Build WebDriverBidi types + name: Build WebDriverBiDi types runs-on: ubuntu-latest steps: - name: Check out the main spec repository @@ -129,7 +129,7 @@ jobs: delete-branch: true committer: Browser Automation Bot author: Browser Automation Bot - commit-message: 'build(spec): update WebDriverBidi types' - title: 'build(spec): update WebDriverBidi types' + commit-message: 'build(spec): update WebDriverBiDi types' + title: 'build(spec): update WebDriverBiDi types' body: Automatically generated by https://github.com/GoogleChromeLabs/chromium-bidi/blob/main/.github/workflows/update-bidi-types.yml push-to-fork: browser-automation-bot/chromium-bidi diff --git a/README.md b/README.md index 1d8f726ef6..f441f96d24 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ npm install -We use [cddlconv](https://github.com/google/cddlconv) to generate our WebDriverBidi types before building. +We use [cddlconv](https://github.com/google/cddlconv) to generate our WebDriverBiDi types before building. 1. Install [Rust](https://rustup.rs/). 2. Run `cargo install --git https://github.com/google/cddlconv.git cddlconv` diff --git a/src/bidiMapper/CommandProcessor.ts b/src/bidiMapper/CommandProcessor.ts index 3270a809ee..89f309d052 100644 --- a/src/bidiMapper/CommandProcessor.ts +++ b/src/bidiMapper/CommandProcessor.ts @@ -162,6 +162,11 @@ export class CommandProcessor extends EventEmitter { return await this.#browserProcessor.removeUserContext( this.#parser.parseRemoveUserContextParams(command.params) ); + case 'browser.getClientWindows': + case 'browser.setClientWindowState': + throw new UnknownErrorException( + `Method ${command.method} is not implemented.` + ); // keep-sorted end // Browsing Context domain diff --git a/src/bidiMapper/modules/context/BrowsingContextImpl.ts b/src/bidiMapper/modules/context/BrowsingContextImpl.ts index b826ed19c2..d40b83c25a 100644 --- a/src/bidiMapper/modules/context/BrowsingContextImpl.ts +++ b/src/bidiMapper/modules/context/BrowsingContextImpl.ts @@ -386,6 +386,8 @@ export class BrowsingContextImpl { url: this.url, userContext: this.userContext, originalOpener: this.#originalOpener ?? null, + // TODO(#2646): Implement Client Window correctly + clientWindow: '', children: maxDepth > 0 ? this.directChildren.map((c) => diff --git a/src/protocol-parser/generated/webdriver-bidi.ts b/src/protocol-parser/generated/webdriver-bidi.ts index a7d72c4629..391385729d 100644 --- a/src/protocol-parser/generated/webdriver-bidi.ts +++ b/src/protocol-parser/generated/webdriver-bidi.ts @@ -351,8 +351,11 @@ export const BrowserCommandSchema = z.lazy(() => z.union([ Browser.CloseSchema, Browser.CreateUserContextSchema, + Browser.GetClientWindowsSchema, Browser.GetUserContextsSchema, Browser.RemoveUserContextSchema, + Browser.SetClientWindowStateSchema, + z.object({}), ]) ); export const BrowserResultSchema = z.lazy(() => @@ -361,6 +364,22 @@ export const BrowserResultSchema = z.lazy(() => Browser.GetUserContextsResultSchema, ]) ); +export namespace Browser { + export const ClientWindowSchema = z.lazy(() => z.string()); +} +export namespace Browser { + export const ClientWindowInfoSchema = z.lazy(() => + z.object({ + active: z.boolean(), + clientWindow: Browser.ClientWindowSchema, + height: JsUintSchema, + state: z.enum(['fullscreen', 'maximized', 'minimized', 'normal']), + width: JsUintSchema, + x: JsIntSchema, + y: JsIntSchema, + }) + ); +} export namespace Browser { export const UserContextSchema = z.lazy(() => z.string()); } @@ -392,6 +411,21 @@ export namespace Browser { () => Browser.UserContextInfoSchema ); } +export namespace Browser { + export const GetClientWindowsSchema = z.lazy(() => + z.object({ + method: z.literal('browser.getClientWindows'), + params: EmptyParamsSchema, + }) + ); +} +export namespace Browser { + export const GetClientWindowsResultSchema = z.lazy(() => + z.object({ + clientWindows: z.array(Browser.ClientWindowInfoSchema), + }) + ); +} export namespace Browser { export const GetUserContextsSchema = z.lazy(() => z.object({ @@ -422,6 +456,44 @@ export namespace Browser { }) ); } +export namespace Browser { + export const SetClientWindowStateSchema = z.lazy(() => + z.object({ + method: z.literal('browser.setClientWindowState'), + params: Browser.SetClientWindowStateParametersSchema, + }) + ); +} +export namespace Browser { + export const SetClientWindowStateParametersSchema = z.lazy(() => + z.union([ + z + .object({ + clientWindow: Browser.ClientWindowSchema, + }) + .and(Browser.ClientWindowNamedStateSchema), + Browser.ClientWindowRectStateSchema, + ]) + ); +} +export namespace Browser { + export const ClientWindowNamedStateSchema = z.lazy(() => + z.object({ + state: z.enum(['fullscreen', 'maximized', 'minimized']), + }) + ); +} +export namespace Browser { + export const ClientWindowRectStateSchema = z.lazy(() => + z.object({ + state: z.literal('normal'), + width: JsUintSchema.optional(), + height: JsUintSchema.optional(), + x: JsIntSchema.optional(), + y: JsIntSchema.optional(), + }) + ); +} export const BrowsingContextCommandSchema = z.lazy(() => z.union([ BrowsingContext.ActivateSchema, @@ -476,6 +548,7 @@ export namespace BrowsingContext { export const InfoSchema = z.lazy(() => z.object({ children: z.union([BrowsingContext.InfoListSchema, z.null()]), + clientWindow: Browser.ClientWindowSchema, context: BrowsingContext.BrowsingContextSchema, originalOpener: z.union([ BrowsingContext.BrowsingContextSchema, diff --git a/src/protocol/generated/webdriver-bidi.ts b/src/protocol/generated/webdriver-bidi.ts index 40efcbd9dc..74d9b68ef2 100644 --- a/src/protocol/generated/webdriver-bidi.ts +++ b/src/protocol/generated/webdriver-bidi.ts @@ -260,11 +260,28 @@ export namespace Session { export type BrowserCommand = | Browser.Close | Browser.CreateUserContext + | Browser.GetClientWindows | Browser.GetUserContexts - | Browser.RemoveUserContext; + | Browser.RemoveUserContext + | Browser.SetClientWindowState + | Record; export type BrowserResult = | Browser.CreateUserContextResult | Browser.GetUserContextsResult; +export namespace Browser { + export type ClientWindow = string; +} +export namespace Browser { + export type ClientWindowInfo = { + active: boolean; + clientWindow: Browser.ClientWindow; + height: JsUint; + state: 'fullscreen' | 'maximized' | 'minimized' | 'normal'; + width: JsUint; + x: JsInt; + y: JsInt; + }; +} export namespace Browser { export type UserContext = string; } @@ -288,6 +305,17 @@ export namespace Browser { export namespace Browser { export type CreateUserContextResult = Browser.UserContextInfo; } +export namespace Browser { + export type GetClientWindows = { + method: 'browser.getClientWindows'; + params: EmptyParams; + }; +} +export namespace Browser { + export type GetClientWindowsResult = { + clientWindows: [...Browser.ClientWindowInfo[]]; + }; +} export namespace Browser { export type GetUserContexts = { method: 'browser.getUserContexts'; @@ -310,6 +338,33 @@ export namespace Browser { userContext: Browser.UserContext; }; } +export namespace Browser { + export type SetClientWindowState = { + method: 'browser.setClientWindowState'; + params: Browser.SetClientWindowStateParameters; + }; +} +export namespace Browser { + export type SetClientWindowStateParameters = + | ({ + clientWindow: Browser.ClientWindow; + } & Browser.ClientWindowNamedState) + | Browser.ClientWindowRectState; +} +export namespace Browser { + export type ClientWindowNamedState = { + state: 'fullscreen' | 'maximized' | 'minimized'; + }; +} +export namespace Browser { + export type ClientWindowRectState = { + state: 'normal'; + width?: JsUint; + height?: JsUint; + x?: JsInt; + y?: JsInt; + }; +} export type BrowsingContextCommand = | BrowsingContext.Activate | BrowsingContext.CaptureScreenshot @@ -352,6 +407,7 @@ export namespace BrowsingContext { export namespace BrowsingContext { export type Info = { children: BrowsingContext.InfoList | null; + clientWindow: Browser.ClientWindow; context: BrowsingContext.BrowsingContext; originalOpener: BrowsingContext.BrowsingContext | null; url: string; From cda7b5393cacd8c161b340811f2995fc625fa436 Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov Date: Wed, 25 Sep 2024 11:23:07 +0200 Subject: [PATCH 2/3] chore: fix tests --- src/bidiMapper/CommandProcessor.ts | 5 ++- tests/browsing_context/test_close.py | 5 ++- tests/browsing_context/test_create.py | 13 ++++-- tests/browsing_context/test_get_tree.py | 18 +++++--- tests/browsing_context/test_navigate.py | 17 +++++--- .../test_nested_browsing_context.py | 42 ++++++++++++------- tests/session/test_subscription.py | 1 + 7 files changed, 68 insertions(+), 33 deletions(-) diff --git a/src/bidiMapper/CommandProcessor.ts b/src/bidiMapper/CommandProcessor.ts index 89f309d052..aba9408765 100644 --- a/src/bidiMapper/CommandProcessor.ts +++ b/src/bidiMapper/CommandProcessor.ts @@ -156,13 +156,16 @@ export class CommandProcessor extends EventEmitter { return this.#browserProcessor.close(); case 'browser.createUserContext': return await this.#browserProcessor.createUserContext(command.params); + case 'browser.getClientWindows': + throw new UnknownErrorException( + `Method ${command.method} is not implemented.` + ); case 'browser.getUserContexts': return await this.#browserProcessor.getUserContexts(); case 'browser.removeUserContext': return await this.#browserProcessor.removeUserContext( this.#parser.parseRemoveUserContextParams(command.params) ); - case 'browser.getClientWindows': case 'browser.setClientWindowState': throw new UnknownErrorException( `Method ${command.method} is not implemented.` diff --git a/tests/browsing_context/test_close.py b/tests/browsing_context/test_close.py index f1369f7ea4..528ed80a3a 100644 --- a/tests/browsing_context/test_close.py +++ b/tests/browsing_context/test_close.py @@ -58,7 +58,7 @@ async def test_browsingContext_close(websocket, context_id): 'beforeUnload': 'ignore' } }], - indirect=True) + indirect=True) @pytest.mark.parametrize("accept", [True, False]) async def test_browsingContext_close_prompt(websocket, context_id, html, accept): @@ -141,6 +141,7 @@ async def test_browsingContext_close_prompt(websocket, context_id, html, 'context': context_id, 'children': None, 'originalOpener': None, + 'clientWindow': ANY_STR, 'parent': None, # Url-encoded `url`. 'url': ANY_STR, @@ -173,7 +174,7 @@ async def test_browsingContext_close_prompt(websocket, context_id, html, 'beforeUnload': 'ignore' } }], - indirect=True) + indirect=True) @pytest.mark.parametrize("accept", [True, False]) async def test_browsingContext_navigate_prompt(websocket, context_id, html, accept): diff --git a/tests/browsing_context/test_create.py b/tests/browsing_context/test_create.py index 5901250d58..a93666bcd0 100644 --- a/tests/browsing_context/test_create.py +++ b/tests/browsing_context/test_create.py @@ -68,7 +68,8 @@ async def test_browsingContext_create_eventContextCreatedEmitted( "children": None, "parent": None, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, } } == context_created_event @@ -134,6 +135,7 @@ async def test_browsingContext_createWithNestedSameOriginContexts_eventContextCr "url": top_level_page, "userContext": "default", "originalOpener": None, + 'clientWindow': ANY_STR, "children": [ { "context": ANY_STR, @@ -141,13 +143,15 @@ async def test_browsingContext_createWithNestedSameOriginContexts_eventContextCr "url": ANY_STR, "userContext": "default", "originalOpener": None, + 'clientWindow': ANY_STR, "children": [{ "context": ANY_STR, # It's not guaranteed the nested page is already loaded. "url": ANY_STR, "userContext": "default", "children": [], - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] }, ] @@ -237,6 +241,7 @@ async def test_browsingContext_create_withUserGesture_eventsEmitted( 'params': { 'context': ANY_STR, 'url': 'about:blank', + 'clientWindow': ANY_STR, 'children': None, 'parent': None, 'userContext': 'default', @@ -319,8 +324,8 @@ async def test_browsingContext_subscribe_to_contextCreated_emits_for_existing( ], # Missing "contexts" means global subscription. **({} if global_subscription else { - "contexts": [another_context_id] - }) + "contexts": [another_context_id] + }) } }) diff --git a/tests/browsing_context/test_get_tree.py b/tests/browsing_context/test_get_tree.py index fa8ad4ef65..ae823a0dbe 100644 --- a/tests/browsing_context/test_get_tree.py +++ b/tests/browsing_context/test_get_tree.py @@ -28,7 +28,8 @@ async def test_browsingContext_getTree_contextReturned(websocket, context_id): "parent": None, "url": "about:blank", "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } @@ -56,7 +57,8 @@ async def test_browsingContext_getTreeWithRoot_contextReturned(websocket): "url": "about:blank", "children": [], "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } @@ -78,12 +80,14 @@ async def test_browsingContext_afterNavigation_getTree_contextsReturned( "url": url_all_origins, "children": [], "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }], "parent": None, "url": page_with_nested_iframe, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result @@ -99,11 +103,13 @@ async def test_browsingContext_afterNavigation_getTree_contextsReturned( "url": url_all_origins, "children": [], "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }], "parent": None, "url": another_page_with_nested_iframe, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result diff --git a/tests/browsing_context/test_navigate.py b/tests/browsing_context/test_navigate.py index 72d11cb065..27889a9ba8 100644 --- a/tests/browsing_context/test_navigate.py +++ b/tests/browsing_context/test_navigate.py @@ -239,7 +239,8 @@ async def test_browsingContext_navigateSameDocumentNavigation_waitInteractive_na "parent": None, "url": url_with_hash_1, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result @@ -256,7 +257,8 @@ async def test_browsingContext_navigateSameDocumentNavigation_waitInteractive_na "parent": None, "url": url_with_hash_2, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result @@ -283,7 +285,8 @@ async def test_browsingContext_navigateSameDocumentNavigation_waitComplete_navig "parent": None, "url": url_with_hash_1, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result @@ -299,7 +302,8 @@ async def test_browsingContext_navigateSameDocumentNavigation_waitComplete_navig "parent": None, "url": url_with_hash_2, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result @@ -322,7 +326,8 @@ async def test_navigateToPageWithHash_contextInfoUpdated( "parent": None, "url": url_with_hash_1, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } @@ -614,7 +619,7 @@ async def test_browsingContext_navigationStarted_sameDocumentNavigation( }, { 'acceptInsecureCerts': False }], - indirect=True) + indirect=True) async def test_browsingContext_acceptInsecureCertsCapability_respected( websocket, context_id, url_bad_ssl, capabilities): async def navigate(): diff --git a/tests/browsing_context/test_nested_browsing_context.py b/tests/browsing_context/test_nested_browsing_context.py index bb5a3e84a8..1e8f9f6530 100644 --- a/tests/browsing_context/test_nested_browsing_context.py +++ b/tests/browsing_context/test_nested_browsing_context.py @@ -38,7 +38,8 @@ async def test_nestedBrowsingContext_navigateToPageWithHash_contextInfoUpdated( "parent": ANY_STR, "url": url_with_hash_1, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result @@ -197,7 +198,8 @@ async def test_nestedBrowsingContext_navigateSameDocumentNavigation_waitInteract "parent": ANY_STR, "url": url_with_hash_1, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result @@ -213,7 +215,8 @@ async def test_nestedBrowsingContext_navigateSameDocumentNavigation_waitInteract "parent": ANY_STR, "url": url_with_hash_2, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result @@ -240,7 +243,8 @@ async def test_nestedBrowsingContext_navigateSameDocumentNavigation_waitComplete "parent": ANY_STR, "url": url_with_hash_1, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result @@ -256,7 +260,8 @@ async def test_nestedBrowsingContext_navigateSameDocumentNavigation_waitComplete "parent": ANY_STR, "url": url_with_hash_2, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result @@ -281,12 +286,14 @@ async def test_nestedBrowsingContext_afterNavigation_getTreeWithNestedCrossOrigi "url": url_example_another_origin, "children": [], "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }], "parent": ANY_STR, "url": another_page_with_nested_iframe, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result @@ -311,11 +318,13 @@ async def test_nestedBrowsingContext_afterNavigation_getTree_contextsReturned( "url": url_all_origins, "children": [], "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }], "parent": ANY_STR, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result @@ -332,11 +341,13 @@ async def test_nestedBrowsingContext_afterNavigation_getTree_contextsReturned( "url": url_all_origins, "children": [], "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }], "parent": ANY_STR, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result @@ -357,12 +368,14 @@ async def test_browsingContext_addAndRemoveNestedContext_contextAddedAndRemoved( "url": url_all_origins, "children": [], "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }], "parent": None, "url": page_with_nested_iframe, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result @@ -388,6 +401,7 @@ async def test_browsingContext_addAndRemoveNestedContext_contextAddedAndRemoved( "parent": None, "url": page_with_nested_iframe, "userContext": "default", - "originalOpener": None + "originalOpener": None, + 'clientWindow': ANY_STR, }] } == result diff --git a/tests/session/test_subscription.py b/tests/session/test_subscription.py index 065f84341d..cbc9989438 100644 --- a/tests/session/test_subscription.py +++ b/tests/session/test_subscription.py @@ -128,6 +128,7 @@ async def test_subscribeWithContext_subscribesToEventsInNestedContext( "children": None, "parent": context_id, "userContext": "default", + 'clientWindow': ANY_STR, "originalOpener": None } } == resp From 487291392edc10b1c2b738b341a76985d6d160e8 Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov Date: Wed, 25 Sep 2024 11:24:15 +0200 Subject: [PATCH 3/3] chore: format --- tests/browsing_context/test_close.py | 4 ++-- tests/browsing_context/test_create.py | 4 ++-- tests/browsing_context/test_navigate.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/browsing_context/test_close.py b/tests/browsing_context/test_close.py index 528ed80a3a..35c0746a25 100644 --- a/tests/browsing_context/test_close.py +++ b/tests/browsing_context/test_close.py @@ -58,7 +58,7 @@ async def test_browsingContext_close(websocket, context_id): 'beforeUnload': 'ignore' } }], - indirect=True) + indirect=True) @pytest.mark.parametrize("accept", [True, False]) async def test_browsingContext_close_prompt(websocket, context_id, html, accept): @@ -174,7 +174,7 @@ async def test_browsingContext_close_prompt(websocket, context_id, html, 'beforeUnload': 'ignore' } }], - indirect=True) + indirect=True) @pytest.mark.parametrize("accept", [True, False]) async def test_browsingContext_navigate_prompt(websocket, context_id, html, accept): diff --git a/tests/browsing_context/test_create.py b/tests/browsing_context/test_create.py index a93666bcd0..db891c5694 100644 --- a/tests/browsing_context/test_create.py +++ b/tests/browsing_context/test_create.py @@ -324,8 +324,8 @@ async def test_browsingContext_subscribe_to_contextCreated_emits_for_existing( ], # Missing "contexts" means global subscription. **({} if global_subscription else { - "contexts": [another_context_id] - }) + "contexts": [another_context_id] + }) } }) diff --git a/tests/browsing_context/test_navigate.py b/tests/browsing_context/test_navigate.py index 27889a9ba8..e44a26117c 100644 --- a/tests/browsing_context/test_navigate.py +++ b/tests/browsing_context/test_navigate.py @@ -619,7 +619,7 @@ async def test_browsingContext_navigationStarted_sameDocumentNavigation( }, { 'acceptInsecureCerts': False }], - indirect=True) + indirect=True) async def test_browsingContext_acceptInsecureCertsCapability_respected( websocket, context_id, url_bad_ssl, capabilities): async def navigate():