chore: implement basic evaluate in BiDi (#8989)

Only the basic return values are supported.
The follow-up PRs will re-use the shared code
from the ExecutionContext and introduce Frame
in BiDi.
This commit is contained in:
Alex Rudenko 2022-09-21 08:10:50 +02:00 committed by GitHub
parent 692b48ab0e
commit 31e7b608d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 2909 additions and 531 deletions

View File

@ -0,0 +1,25 @@
---
sidebar_label: Page.addStyleTag_2
---
# Page.addStyleTag() method
**Signature:**
```typescript
class Page {
addStyleTag(
options: FrameAddStyleTagOptions
): Promise<ElementHandle<HTMLStyleElement | HTMLLinkElement>>;
}
```
## Parameters
| Parameter | Type | Description |
| --------- | ----------------------------------------------------------------- | ----------- |
| options | [FrameAddStyleTagOptions](./puppeteer.frameaddstyletagoptions.md) | |
**Returns:**
Promise&lt;[ElementHandle](./puppeteer.elementhandle.md)&lt;HTMLStyleElement \| HTMLLinkElement&gt;&gt;

View File

@ -84,6 +84,7 @@ page.off('request', logRequest);
| [addScriptTag(options)](./puppeteer.page.addscripttag.md) | | Adds a <code>&lt;script&gt;</code> tag into the page with the desired URL or content. |
| [addStyleTag(options)](./puppeteer.page.addstyletag.md) | | <p>Adds a <code>&lt;link rel=&quot;stylesheet&quot;&gt;</code> tag into the page with the desired URL or a <code>&lt;style type=&quot;text/css&quot;&gt;</code> tag with the content.</p><p>Shortcut for .</p> |
| [addStyleTag(options)](./puppeteer.page.addstyletag_1.md) | | |
| [addStyleTag(options)](./puppeteer.page.addstyletag_2.md) | | |
| [authenticate(credentials)](./puppeteer.page.authenticate.md) | | Provide credentials for <code>HTTP authentication</code>. |
| [bringToFront()](./puppeteer.page.bringtofront.md) | | Brings page to front (activates tab). |
| [browser()](./puppeteer.page.browser.md) | | Get the browser the page belongs to. |

View File

@ -19,7 +19,7 @@
import {ChildProcess} from 'child_process';
import {Protocol} from 'devtools-protocol';
import {EventEmitter} from '../common/EventEmitter.js';
import type {Page} from '../common/Page.js'; // TODO: move to ./api
import type {Page} from './Page.js'; // TODO: move to ./api
import type {Target} from '../common/Target.js'; // TODO: move to ./api
/**

2587
src/api/Page.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,7 @@ import {Protocol} from 'devtools-protocol';
import {assert} from '../util/assert.js';
import {CDPSession, Connection, ConnectionEmittedEvents} from './Connection.js';
import {waitWithTimeout} from './util.js';
import {Page} from './Page.js';
import {Page} from '../api/Page.js';
import {Viewport} from './PuppeteerViewport.js';
import {Target} from './Target.js';
import {TaskQueue} from './TaskQueue.js';

View File

@ -29,11 +29,12 @@ import {
Point,
PressOptions,
} from './JSHandle.js';
import {Page, ScreenshotOptions} from './Page.js';
import {Page, ScreenshotOptions} from '../api/Page.js';
import {getQueryHandlerAndSelector} from './QueryHandler.js';
import {EvaluateFunc, HandleFor, NodeFor} from './types.js';
import {KeyInput} from './USKeyboardLayout.js';
import {debugError, isString} from './util.js';
import {CDPPage} from './Page.js';
const applyOffsetsToQuad = (
quad: Point[],
@ -510,7 +511,7 @@ export class ElementHandle<
objectId: this.remoteObject().objectId,
})
.catch(debugError),
this.#page._client().send('Page.getLayoutMetrics'),
(this.#page as CDPPage)._client().send('Page.getLayoutMetrics'),
]);
if (!result || !result.quads.length) {
throw new Error('Node is either not clickable or not an HTMLElement');

View File

@ -31,7 +31,7 @@ import {
WaitForSelectorOptions,
} from './IsolatedWorld.js';
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
import {Page} from './Page.js';
import {Page} from '../api/Page.js';
import {getQueryHandlerAndSelector} from './QueryHandler.js';
import {EvaluateFunc, HandleFor, NodeFor} from './types.js';
import {importFS} from './util.js';

View File

@ -24,7 +24,7 @@ import {Frame} from './Frame.js';
import {FrameTree} from './FrameTree.js';
import {IsolatedWorld, MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorld.js';
import {NetworkManager} from './NetworkManager.js';
import {Page} from './Page.js';
import {Page} from '../api/Page.js';
import {Target} from './Target.js';
import {TimeoutSettings} from './TimeoutSettings.js';
import {debugError} from './util.js';

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
import {Page, PageEmittedEvents} from './Page.js';
import {Page, PageEmittedEvents} from '../api/Page.js';
import {WebWorker} from './WebWorker.js';
import {CDPSession} from './Connection.js';
import type {
@ -26,6 +26,7 @@ import {Viewport} from './PuppeteerViewport.js';
import {Protocol} from 'devtools-protocol';
import {TaskQueue} from './TaskQueue.js';
import {TargetManager} from './TargetManager.js';
import {CDPPage} from './Page.js';
/**
* @public
@ -163,7 +164,7 @@ export class Target {
? Promise.resolve(this.#session)
: this.#sessionFactory(true)
).then(client => {
return Page._create(
return CDPPage._create(
client,
this,
this.#ignoreHTTPSErrors,

View File

@ -1,9 +1,28 @@
/**
* Copyright 2022 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
Browser as BrowserBase,
BrowserCloseCallback,
BrowserContextOptions,
BrowserContext as BrowserContextBase,
} from '../../api/Browser.js';
import {Connection} from './Connection.js';
import {ChildProcess} from 'child_process';
import {BrowserContext} from './BrowserContext.js';
/**
* @internal
@ -14,6 +33,7 @@ export class Browser extends BrowserBase {
*/
static async create(opts: Options): Promise<Browser> {
// TODO: await until the connection is established.
(await opts.connection.send('session.new', {})) as {sessionId: string};
return new Browser(opts);
}
@ -43,6 +63,12 @@ export class Browser extends BrowserBase {
override process(): ChildProcess | null {
return this.#process ?? null;
}
override async createIncognitoBrowserContext(
_options?: BrowserContextOptions
): Promise<BrowserContextBase> {
return new BrowserContext(this.#connection);
}
}
interface Options {

View File

@ -0,0 +1,41 @@
/**
* Copyright 2022 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {BrowserContext as BrowserContextBase} from '../../api/Browser.js';
import {Page as PageBase} from '../../api/Page.js';
import {Connection} from './Connection.js';
import {Page} from './Page.js';
/**
* @internal
*/
export class BrowserContext extends BrowserContextBase {
#connection: Connection;
constructor(connection: Connection) {
super();
this.#connection = connection;
}
override async newPage(): Promise<PageBase> {
const result = (await this.#connection.send('browsingContext.create', {
type: 'tab',
})) as {context: string};
return new Page(this.#connection, result.context);
}
override async close(): Promise<void> {}
}

View File

@ -69,7 +69,7 @@ export class Connection extends EventEmitter {
return this.#closed;
}
send(method: string, params: object): Promise<any> {
send(method: string, params: object): Promise<CommandResponse['result']> {
const id = ++this.#lastId;
const stringifiedMessage = JSON.stringify({
id,

56
src/common/bidi/Page.ts Normal file
View File

@ -0,0 +1,56 @@
/**
* Copyright 2022 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Page as PageBase} from '../../api/Page.js';
import {Connection} from './Connection.js';
import type {EvaluateFunc} from '..//types.js';
/**
* @internal
*/
export class Page extends PageBase {
#connection: Connection;
#contextId: string;
constructor(connection: Connection, contextId: string) {
super();
this.#connection = connection;
this.#contextId = contextId;
}
override async close(): Promise<void> {
await this.#connection.send('browsingContext.close', {
context: this.#contextId,
});
}
override async evaluate<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
>(
pageFunction: Func | string,
..._args: Params
): Promise<Awaited<ReturnType<Func>>> {
// TODO: re-use evaluate logic from Execution context.
const str = `(${pageFunction.toString()})()`;
const result = (await this.#connection.send('script.evaluate', {
expression: str,
target: {context: this.#contextId},
awaitPromise: true,
})) as {result: {type: string; value: any}};
return result.result.value;
}
}

View File

@ -1,6 +1,7 @@
// AUTOGENERATED - Use `npm run generate:sources` to regenerate.
export * from './api/Browser.js';
export * from './api/Page.js';
export * from './common/Accessibility.js';
export * from './common/AriaQueryHandler.js';
export * from './common/Browser.js';

View File

@ -1960,5 +1960,35 @@
"platforms": ["darwin", "win32", "linux"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work",
"platforms": ["darwin", "win32", "linux"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["PASS"]
},
{
"testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work with function shorthands",
"platforms": ["darwin", "win32", "linux"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["SKIP"]
},
{
"testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work with unicode chars",
"platforms": ["darwin", "win32", "linux"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["SKIP"]
},
{
"testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work right after framenavigated",
"platforms": ["darwin", "win32", "linux"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["SKIP"]
},
{
"testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work from-inside an exposed function",
"platforms": ["darwin", "win32", "linux"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["SKIP"]
}
]

View File

@ -17,7 +17,7 @@
import expect from 'expect';
import {TLSSocket} from 'tls';
import {Browser, BrowserContext} from '../../lib/cjs/puppeteer/api/Browser.js';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {Page} from '../../lib/cjs/puppeteer/api/Page.js';
import {HTTPResponse} from '../../lib/cjs/puppeteer/common/HTTPResponse.js';
import {getTestState} from './mocha-utils.js';

View File

@ -22,7 +22,7 @@ import rimraf from 'rimraf';
import sinon from 'sinon';
import {TLSSocket} from 'tls';
import {promisify} from 'util';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {Page} from '../../lib/cjs/puppeteer/api/Page.js';
import {Product} from '../../lib/cjs/puppeteer/common/Product.js';
import {getTestState, itOnlyRegularInstall} from './mocha-utils.js';
import utils from './utils.js';

View File

@ -21,7 +21,7 @@ import * as path from 'path';
import rimraf from 'rimraf';
import sinon from 'sinon';
import {Browser, BrowserContext} from '../../lib/cjs/puppeteer/api/Browser.js';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {Page} from '../../lib/cjs/puppeteer/api/Page.js';
import {isErrorLike} from '../../lib/cjs/puppeteer/util/ErrorLike.js';
import {
PuppeteerLaunchOptions,

View File

@ -18,7 +18,7 @@ import utils from './utils.js';
import expect from 'expect';
import {getTestState} from './mocha-utils.js';
import {Browser, BrowserContext} from '../../lib/cjs/puppeteer/api/Browser.js';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {Page} from '../../lib/cjs/puppeteer/api/Page.js';
describe('OOPIF', function () {
/* We use a special browser for this test as we need the --site-per-process flag */

View File

@ -20,13 +20,14 @@ import path from 'path';
import sinon from 'sinon';
import {CDPSession} from '../../lib/cjs/puppeteer/common/Connection.js';
import {ConsoleMessage} from '../../lib/cjs/puppeteer/common/ConsoleMessage.js';
import {Metrics, Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {Metrics, Page} from '../../lib/cjs/puppeteer/api/Page.js';
import {
getTestState,
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import utils, {attachFrame, waitEvent} from './utils.js';
import {CDPPage} from '../../lib/cjs/puppeteer/common/Page.js';
describe('Page', function () {
setupTestBrowserHooks();
@ -2317,7 +2318,7 @@ describe('Page', function () {
describe('Page.client', function () {
it('should return the client instance', async () => {
const {page} = getTestState();
expect(page._client()).toBeInstanceOf(CDPSession);
expect((page as CDPPage)._client()).toBeInstanceOf(CDPSession);
});
});
});

View File

@ -16,7 +16,7 @@
import expect from 'expect';
import {ServerResponse} from 'http';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {Page} from '../../lib/cjs/puppeteer/api/Page.js';
import {Target} from '../../lib/cjs/puppeteer/common/Target.js';
import {
getTestState,

View File

@ -19,7 +19,7 @@ import path from 'path';
import expect from 'expect';
import {getTestState} from './mocha-utils.js';
import {Browser} from '../../lib/cjs/puppeteer/api/Browser.js';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {Page} from '../../lib/cjs/puppeteer/api/Page.js';
describe('Tracing', function () {
let outputFile!: string;

View File

@ -17,7 +17,7 @@
import expect from 'expect';
import path from 'path';
import {Frame} from '../../lib/cjs/puppeteer/common/Frame.js';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {Page} from '../../lib/cjs/puppeteer/api/Page.js';
import {EventEmitter} from '../../lib/cjs/puppeteer/common/EventEmitter.js';
import {compare} from './golden-utils.js';