diff --git a/package-lock.json b/package-lock.json index e83bcb3a..581d9bc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3020,9 +3020,9 @@ } }, "node_modules/chromium-bidi": { - "version": "0.4.12", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.12.tgz", - "integrity": "sha512-yl0ngMHtYUGJa2G0lkcbPvbnUZ9WMQyMNSfYmlrGD1nHRNyI9KOGw3dOaofFugXHHToneUaSmF9iUdgCBamCjA==", + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.13.tgz", + "integrity": "sha512-9m2SY5DHI43OBQ7SMXjwp/iQaYo6iihqJ4IiD1OlrawGQTNveYYeJJt1yCqMxjp5y86m/uHxc9VooOWVlKFi4w==", "dependencies": { "mitt": "3.0.0" }, @@ -10109,7 +10109,7 @@ "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "1.4.2", - "chromium-bidi": "0.4.12", + "chromium-bidi": "0.4.13", "cross-fetch": "3.1.6", "debug": "4.3.4", "devtools-protocol": "0.0.1135028", @@ -12230,9 +12230,9 @@ } }, "chromium-bidi": { - "version": "0.4.12", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.12.tgz", - "integrity": "sha512-yl0ngMHtYUGJa2G0lkcbPvbnUZ9WMQyMNSfYmlrGD1nHRNyI9KOGw3dOaofFugXHHToneUaSmF9iUdgCBamCjA==", + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.13.tgz", + "integrity": "sha512-9m2SY5DHI43OBQ7SMXjwp/iQaYo6iihqJ4IiD1OlrawGQTNveYYeJJt1yCqMxjp5y86m/uHxc9VooOWVlKFi4w==", "requires": { "mitt": "3.0.0" } @@ -15653,7 +15653,7 @@ "version": "file:packages/puppeteer-core", "requires": { "@puppeteer/browsers": "1.4.2", - "chromium-bidi": "0.4.12", + "chromium-bidi": "0.4.13", "cross-fetch": "3.1.6", "debug": "4.3.4", "devtools-protocol": "0.0.1135028", diff --git a/packages/puppeteer-core/package.json b/packages/puppeteer-core/package.json index 9d60d135..03bbd710 100644 --- a/packages/puppeteer-core/package.json +++ b/packages/puppeteer-core/package.json @@ -133,7 +133,7 @@ "author": "The Chromium Authors", "license": "Apache-2.0", "dependencies": { - "chromium-bidi": "0.4.12", + "chromium-bidi": "0.4.13", "cross-fetch": "3.1.6", "debug": "4.3.4", "devtools-protocol": "0.0.1135028", diff --git a/packages/puppeteer-core/src/api/Page.ts b/packages/puppeteer-core/src/api/Page.ts index a5198bd0..98307a50 100644 --- a/packages/puppeteer-core/src/api/Page.ts +++ b/packages/puppeteer-core/src/api/Page.ts @@ -1548,10 +1548,9 @@ export class Page extends EventEmitter { * API usage, the navigation will resolve with `null`. */ async waitForNavigation( - options?: WaitForOptions - ): Promise; - async waitForNavigation(): Promise { - throw new Error('Not implemented'); + options: WaitForOptions = {} + ): Promise { + return await this.mainFrame().waitForNavigation(options); } /** diff --git a/packages/puppeteer-core/src/common/Page.ts b/packages/puppeteer-core/src/common/Page.ts index 30584a24..99eecb8f 100644 --- a/packages/puppeteer-core/src/common/Page.ts +++ b/packages/puppeteer-core/src/common/Page.ts @@ -885,12 +885,6 @@ export class CDPPage extends Page { return result[0]; } - override async waitForNavigation( - options: WaitForOptions = {} - ): Promise { - return await this.mainFrame().waitForNavigation(options); - } - override async waitForRequest( urlOrPredicate: string | ((req: HTTPRequest) => boolean | Promise), options: {timeout?: number} = {} diff --git a/packages/puppeteer-core/src/common/bidi/BrowserContext.ts b/packages/puppeteer-core/src/common/bidi/BrowserContext.ts index 4c80ba68..36ef2c5e 100644 --- a/packages/puppeteer-core/src/common/bidi/BrowserContext.ts +++ b/packages/puppeteer-core/src/common/bidi/BrowserContext.ts @@ -96,7 +96,10 @@ export class BrowserContext extends BrowserContextBase { const {result} = await this.#connection.send('browsingContext.create', { type: 'tab', }); - const page = new Page(this, result); + const page = new Page(this, { + context: result.context, + children: [], + }); if (this.#defaultViewport) { try { await page.setViewport(this.#defaultViewport); @@ -113,6 +116,10 @@ export class BrowserContext extends BrowserContextBase { override async close(): Promise { await this.#init.valueOrThrow(); + if (this.#isDefault) { + throw new Error('Default context cannot be closed!'); + } + for (const page of this.#pages.values()) { await page?.close().catch(error => { debugError(error); diff --git a/packages/puppeteer-core/src/common/bidi/BrowsingContext.ts b/packages/puppeteer-core/src/common/bidi/BrowsingContext.ts index 17edd3b2..632f3a93 100644 --- a/packages/puppeteer-core/src/common/bidi/BrowsingContext.ts +++ b/packages/puppeteer-core/src/common/bidi/BrowsingContext.ts @@ -17,7 +17,10 @@ import {Realm} from './Realm.js'; /** * @internal */ -const lifeCycleToSubscribedEvent = new Map([ +export const lifeCycleToSubscribedEvent = new Map< + PuppeteerLifeCycleEvent, + string +>([ ['load', 'browsingContext.load'], ['domcontentloaded', 'browsingContext.domContentLoaded'], ]); @@ -87,7 +90,7 @@ export class CDPSessionWrapper extends EventEmitter implements CDPSession { export class BrowsingContext extends Realm { #timeoutSettings: TimeoutSettings; #id: string; - #url = 'about:blank'; + #url: string; #cdpSession: CDPSession; constructor( @@ -99,7 +102,15 @@ export class BrowsingContext extends Realm { this.connection = connection; this.#timeoutSettings = timeoutSettings; this.#id = info.context; + this.#url = info.url; this.#cdpSession = new CDPSessionWrapper(this); + + this.on( + 'browsingContext.fragmentNavigated', + (info: Bidi.BrowsingContext.NavigationInfo) => { + this.#url = info.url; + } + ); } createSandboxRealm(sandbox: string): Realm { @@ -118,6 +129,10 @@ export class BrowsingContext extends Realm { return this.#cdpSession; } + navigated(url: string): void { + this.#url = url; + } + async goto( url: string, options: { diff --git a/packages/puppeteer-core/src/common/bidi/Frame.ts b/packages/puppeteer-core/src/common/bidi/Frame.ts index 4ffc147c..3f9603a4 100644 --- a/packages/puppeteer-core/src/common/bidi/Frame.ts +++ b/packages/puppeteer-core/src/common/bidi/Frame.ts @@ -14,15 +14,22 @@ * limitations under the License. */ +import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; + import {ElementHandle} from '../../api/ElementHandle.js'; import {Frame as BaseFrame} from '../../api/Frame.js'; +import {Deferred} from '../../util/Deferred.js'; import {UTILITY_WORLD_NAME} from '../FrameManager.js'; import {PuppeteerLifeCycleEvent} from '../LifecycleWatcher.js'; import {TimeoutSettings} from '../TimeoutSettings.js'; import {EvaluateFunc, EvaluateFuncWith, HandleFor, NodeFor} from '../types.js'; -import {withSourcePuppeteerURLIfNone} from '../util.js'; +import {waitForEvent, withSourcePuppeteerURLIfNone} from '../util.js'; -import {BrowsingContext} from './BrowsingContext.js'; +import { + BrowsingContext, + getWaitUntilSingle, + lifeCycleToSubscribedEvent, +} from './BrowsingContext.js'; import {HTTPResponse} from './HTTPResponse.js'; import {Page} from './Page.js'; import { @@ -39,6 +46,8 @@ import { export class Frame extends BaseFrame { #page: Page; #context: BrowsingContext; + #timeoutSettings: TimeoutSettings; + #abortDeferred = Deferred.create(); sandboxes: SandboxChart; override _id: string; @@ -51,6 +60,7 @@ export class Frame extends BaseFrame { super(); this.#page = page; this.#context = context; + this.#timeoutSettings = timeoutSettings; this._id = this.#context.id; this._parentId = parentId ?? undefined; @@ -114,17 +124,12 @@ export class Frame extends BaseFrame { override async goto( url: string, - options?: - | { - referer?: string | undefined; - referrerPolicy?: string | undefined; - timeout?: number | undefined; - waitUntil?: - | PuppeteerLifeCycleEvent - | PuppeteerLifeCycleEvent[] - | undefined; - } - | undefined + options?: { + referer?: string; + referrerPolicy?: string; + timeout?: number; + waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]; + } ): Promise { const navigationId = await this.#context.goto(url, options); return this.#page.getNavigationResponse(navigationId); @@ -155,13 +160,13 @@ export class Frame extends BaseFrame { override $( selector: Selector ): Promise> | null> { - return this.sandboxes[MAIN_SANDBOX].$(selector); + return this.mainRealm().$(selector); } override $$( selector: Selector ): Promise>>> { - return this.sandboxes[MAIN_SANDBOX].$$(selector); + return this.mainRealm().$$(selector); } override $eval< @@ -177,7 +182,7 @@ export class Frame extends BaseFrame { ...args: Params ): Promise>> { pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction); - return this.sandboxes[MAIN_SANDBOX].$eval(selector, pageFunction, ...args); + return this.mainRealm().$eval(selector, pageFunction, ...args); } override $$eval< @@ -193,14 +198,54 @@ export class Frame extends BaseFrame { ...args: Params ): Promise>> { pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction); - return this.sandboxes[MAIN_SANDBOX].$$eval(selector, pageFunction, ...args); + return this.mainRealm().$$eval(selector, pageFunction, ...args); } override $x(expression: string): Promise>> { - return this.sandboxes[MAIN_SANDBOX].$x(expression); + return this.mainRealm().$x(expression); + } + + override async waitForNavigation( + options: { + timeout?: number; + waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[]; + } = {} + ): Promise { + const { + waitUntil = 'load', + timeout = this.#timeoutSettings.navigationTimeout(), + } = options; + + const waitUntilEvent = lifeCycleToSubscribedEvent.get( + getWaitUntilSingle(waitUntil) + ) as string; + + const [info] = await Promise.all([ + waitForEvent( + this.#context, + waitUntilEvent, + () => { + return true; + }, + timeout, + this.#abortDeferred.valueOrThrow() + ), + waitForEvent( + this.#context, + Bidi.BrowsingContext.EventNames.FragmentNavigated, + () => { + return true; + }, + timeout, + this.#abortDeferred.valueOrThrow() + ), + ]); + + return this.#page.getNavigationResponse(info.navigation); } dispose(): void { + this.#abortDeferred.reject(new Error('Frame detached')); this.#context.dispose(); } } diff --git a/packages/puppeteer-core/src/common/bidi/HTTPResponse.ts b/packages/puppeteer-core/src/common/bidi/HTTPResponse.ts index acca87d5..10ac181d 100644 --- a/packages/puppeteer-core/src/common/bidi/HTTPResponse.ts +++ b/packages/puppeteer-core/src/common/bidi/HTTPResponse.ts @@ -16,6 +16,7 @@ import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; import Protocol from 'devtools-protocol'; +import {Frame} from '../../api/Frame.js'; import { HTTPResponse as BaseHTTPResponse, RemoteAddress, @@ -93,4 +94,8 @@ export class HTTPResponse extends BaseHTTPResponse { override timing(): Protocol.Network.ResourceTiming | null { return this.#timings as any; } + + override frame(): Frame | null { + return this.#request.frame(); + } } diff --git a/packages/puppeteer-core/src/common/bidi/Page.ts b/packages/puppeteer-core/src/common/bidi/Page.ts index 1683c4df..1468c22b 100644 --- a/packages/puppeteer-core/src/common/bidi/Page.ts +++ b/packages/puppeteer-core/src/common/bidi/Page.ts @@ -35,7 +35,6 @@ import {Coverage} from '../Coverage.js'; import {EmulationManager} from '../EmulationManager.js'; import {TargetCloseError} from '../Errors.js'; import {Handler} from '../EventEmitter.js'; -import {FrameManagerEmittedEvents} from '../FrameManager.js'; import {FrameTree} from '../FrameTree.js'; import {NetworkManagerEmittedEvents} from '../NetworkManager.js'; import {PDFOptions} from '../PDFOptions.js'; @@ -77,17 +76,10 @@ export class Page extends PageBase { #closedDeferred = Deferred.create(); #subscribedEvents = new Map>([ ['log.entryAdded', this.#onLogEntryAdded.bind(this)], - [ - 'browsingContext.load', - () => { - return this.emit(PageEmittedEvents.Load); - }, - ], + ['browsingContext.load', this.#onFrameLoaded.bind(this)], [ 'browsingContext.domContentLoaded', - () => { - return this.emit(PageEmittedEvents.DOMContentLoaded); - }, + this.#onFrameDOMContentLoaded.bind(this), ], ['browsingContext.contextCreated', this.#onFrameAttached.bind(this)], ['browsingContext.contextDestroyed', this.#onFrameDetached.bind(this)], @@ -96,33 +88,23 @@ export class Page extends PageBase { #networkManagerEvents = new Map>([ [ NetworkManagerEmittedEvents.Request, - event => { - return this.emit(PageEmittedEvents.Request, event); - }, + this.emit.bind(this, PageEmittedEvents.Request), ], [ NetworkManagerEmittedEvents.RequestServedFromCache, - event => { - return this.emit(PageEmittedEvents.RequestServedFromCache, event); - }, + this.emit.bind(this, PageEmittedEvents.RequestServedFromCache), ], [ NetworkManagerEmittedEvents.RequestFailed, - event => { - return this.emit(PageEmittedEvents.RequestFailed, event); - }, + this.emit.bind(this, PageEmittedEvents.RequestFailed), ], [ NetworkManagerEmittedEvents.RequestFinished, - event => { - return this.emit(PageEmittedEvents.RequestFinished, event); - }, + this.emit.bind(this, PageEmittedEvents.RequestFinished), ], [ NetworkManagerEmittedEvents.Response, - event => { - return this.emit(PageEmittedEvents.Response, event); - }, + this.emit.bind(this, PageEmittedEvents.Response), ], ]); #tracing: Tracing; @@ -132,7 +114,12 @@ export class Page extends PageBase { #touchscreen: Touchscreen; #keyboard: Keyboard; - constructor(browserContext: BrowserContext, info: {context: string}) { + constructor( + browserContext: BrowserContext, + info: Omit & { + url?: string; + } + ) { super(); this.#browserContext = browserContext; this.#connection = browserContext.connection; @@ -140,8 +127,8 @@ export class Page extends PageBase { this.#networkManager = new NetworkManager(this.#connection, this); this.#onFrameAttached({ ...info, - url: 'about:blank', - children: [], + url: info.url ?? 'about:blank', + children: info.children ?? [], }); for (const [event, subscriber] of this.#subscribedEvents) { @@ -216,6 +203,20 @@ export class Page extends PageBase { return this.#frameTree.childFrames(frameId); } + #onFrameLoaded(info: Bidi.BrowsingContext.NavigationInfo): void { + const frame = this.frame(info.context); + if (frame && this.mainFrame() === frame) { + this.emit(PageEmittedEvents.Load); + } + } + + #onFrameDOMContentLoaded(info: Bidi.BrowsingContext.NavigationInfo): void { + const frame = this.frame(info.context); + if (frame && this.mainFrame() === frame) { + this.emit(PageEmittedEvents.DOMContentLoaded); + } + } + #onFrameAttached(info: Bidi.BrowsingContext.Info): void { if ( !this.frame(info.context) && @@ -235,7 +236,7 @@ export class Page extends PageBase { info.parent ); this.#frameTree.addFrame(frame); - this.emit(FrameManagerEmittedEvents.FrameAttached, frame); + this.emit(PageEmittedEvents.FrameAttached, frame); } } @@ -247,12 +248,8 @@ export class Page extends PageBase { let frame = this.frame(frameId); // Detach all child frames first. if (frame) { - for (const child of frame.childFrames()) { - this.#removeFramesRecursively(child); - } - frame = await this.#frameTree.waitForFrame(frameId); - this.emit(FrameManagerEmittedEvents.FrameNavigated, frame); + this.emit(PageEmittedEvents.FrameNavigated, frame); } } @@ -260,6 +257,9 @@ export class Page extends PageBase { const frame = this.frame(info.context); if (frame) { + if (frame === this.mainFrame()) { + this.emit(PageEmittedEvents.Close); + } this.#removeFramesRecursively(frame); } } @@ -270,7 +270,7 @@ export class Page extends PageBase { } frame.dispose(); this.#frameTree.removeFrame(frame); - this.emit(FrameManagerEmittedEvents.FrameDetached, frame); + this.emit(PageEmittedEvents.FrameDetached, frame); } #onLogEntryAdded(event: Bidi.Log.LogEntry): void { @@ -337,12 +337,13 @@ export class Page extends PageBase { return; } this.#closedDeferred.resolve(new TargetCloseError('Page closed!')); - this.removeAllListeners(); this.#networkManager.dispose(); await this.#connection.send('browsingContext.close', { context: this.mainFrame()._id, }); + this.emit(PageEmittedEvents.Close); + this.removeAllListeners(); } override async evaluateHandle< diff --git a/test/TestExpectations.json b/test/TestExpectations.json index 7c8f6595..7df203c2 100644 --- a/test/TestExpectations.json +++ b/test/TestExpectations.json @@ -87,7 +87,7 @@ "testIdPattern": "[navigation.spec] navigation Frame.waitForNavigation *", "platforms": ["darwin", "linux", "win32"], "parameters": ["webDriverBiDi"], - "expectations": ["FAIL", "TIMEOUT"] + "expectations": ["PASS"] }, { "testIdPattern": "[navigation.spec] navigation Page.goBack *", @@ -95,12 +95,6 @@ "parameters": ["webDriverBiDi"], "expectations": ["FAIL"] }, - { - "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation *", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["FAIL", "TIMEOUT"] - }, { "testIdPattern": "[network.spec] network *", "platforms": ["darwin", "linux", "win32"], @@ -359,12 +353,6 @@ "parameters": ["webDriverBiDi"], "expectations": ["PASS"] }, - { - "testIdPattern": "[click.spec] Page.click should click on checkbox input and toggle", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["PASS"] - }, { "testIdPattern": "[click.spec] Page.click should click on checkbox label and toggle", "platforms": ["darwin", "linux", "win32"], @@ -593,12 +581,6 @@ "parameters": ["webDriverBiDi"], "expectations": ["FAIL"] }, - { - "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work right after framenavigated", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["FAIL", "TIMEOUT"] - }, { "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluateOnNewDocument *", "platforms": ["darwin", "linux", "win32"], @@ -629,18 +611,6 @@ "parameters": ["webDriverBiDi"], "expectations": ["PASS"] }, - { - "testIdPattern": "[frame.spec] Frame specs Frame Management should report frame from-inside shadow DOM", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["FAIL"] - }, - { - "testIdPattern": "[frame.spec] Frame specs Frame Management should support url fragment", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["FAIL"] - }, { "testIdPattern": "[frame.spec] Frame specs Frame.evaluate allows readonly array to be an argument", "platforms": ["darwin", "linux", "win32"], @@ -837,7 +807,7 @@ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.launch should have custom URL when launching browser", "platforms": ["darwin", "linux", "win32"], "parameters": ["webDriverBiDi"], - "expectations": ["FAIL"] + "expectations": ["SKIP"] }, { "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.launch should take fullPage screenshots when defaultViewport is null", @@ -911,6 +881,30 @@ "parameters": ["webDriverBiDi"], "expectations": ["PASS"] }, + { + "testIdPattern": "[mouse.spec] Mouse should click the document", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[mouse.spec] Mouse should not throw if clicking in parallel", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[mouse.spec] Mouse should trigger hover state", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[mouse.spec] Mouse should trigger hover state with removed window.Node", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[navigation.spec] navigation \"after each\" hook for \"should work with both domcontentloaded and load\"", "platforms": ["darwin", "linux", "win32"], @@ -959,6 +953,36 @@ "parameters": ["webDriverBiDi"], "expectations": ["FAIL"] }, + { + "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation *", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work with clicking on anchor links", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["TIMEOUT"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work with DOM history.back()/history.forward()", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL", "TIMEOUT"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work with history.pushState()", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["TIMEOUT"] + }, + { + "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work with history.replaceState()", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["TIMEOUT"] + }, { "testIdPattern": "[network.spec] network Network Events Page.Events.RequestFinished", "platforms": ["darwin", "linux", "win32"], @@ -1079,12 +1103,24 @@ "parameters": ["cdp", "firefox"], "expectations": ["SKIP"] }, + { + "testIdPattern": "[page.spec] Page Page.close should *not* run beforeunload by default", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[page.spec] Page Page.close should reject all promises when page is closed", "platforms": ["darwin", "linux", "win32"], "parameters": ["webDriverBiDi"], "expectations": ["PASS"] }, + { + "testIdPattern": "[page.spec] Page Page.Events.Close should work with page.close", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[page.spec] Page Page.Events.Console should not fail for window object", "platforms": ["darwin", "linux", "win32"], @@ -1109,6 +1145,90 @@ "parameters": ["webDriverBiDi"], "expectations": ["PASS"] }, + { + "testIdPattern": "[page.spec] Page Page.select should deselect all options when passed no values for a multiple select", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[page.spec] Page Page.select should deselect all options when passed no values for a select without multiple", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[page.spec] Page Page.select should respect event bubbling", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[page.spec] Page Page.select should return [] on no matched values", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[page.spec] Page Page.select should return [] on no values", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[page.spec] Page Page.select should return an array of matched values", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[page.spec] Page Page.select should return an array of one element when multiple is not set", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[page.spec] Page Page.select should select multiple options", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[page.spec] Page Page.select should select only first option", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[page.spec] Page Page.select should select single option", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[page.spec] Page Page.select should throw if passed in non-strings", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[page.spec] Page Page.select should throw when element is not a