chore: reuse context in the innerloop mode (#15719)
This commit is contained in:
parent
a198b6d753
commit
55cd3928b7
|
|
@ -40,7 +40,8 @@
|
||||||
"test-types": "node utils/generate_types/ && npx -p typescript@3.7.5 tsc -p utils/generate_types/test/tsconfig.json && tsc -p ./tests/",
|
"test-types": "node utils/generate_types/ && npx -p typescript@3.7.5 tsc -p utils/generate_types/test/tsconfig.json && tsc -p ./tests/",
|
||||||
"roll": "node utils/roll_browser.js",
|
"roll": "node utils/roll_browser.js",
|
||||||
"check-deps": "node utils/check_deps.js",
|
"check-deps": "node utils/check_deps.js",
|
||||||
"build-android-driver": "./utils/build_android_driver.sh"
|
"build-android-driver": "./utils/build_android_driver.sh",
|
||||||
|
"innerloop": "playwright launch-server --browser=chromium --config=utils/innerloop-server.config.json"
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ export abstract class Browser extends SdkObject {
|
||||||
_defaultContext: BrowserContext | null = null;
|
_defaultContext: BrowserContext | null = null;
|
||||||
private _startedClosing = false;
|
private _startedClosing = false;
|
||||||
readonly _idToVideo = new Map<string, { context: BrowserContext, artifact: Artifact }>();
|
readonly _idToVideo = new Map<string, { context: BrowserContext, artifact: Artifact }>();
|
||||||
|
private _contextForReuse: { context: BrowserContext, hash: string } | undefined;
|
||||||
|
|
||||||
constructor(options: BrowserOptions) {
|
constructor(options: BrowserOptions) {
|
||||||
super(options.rootSdkObject, 'browser');
|
super(options.rootSdkObject, 'browser');
|
||||||
|
|
@ -90,6 +91,17 @@ export abstract class Browser extends SdkObject {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async newContextForReuse(params: channels.BrowserNewContextForReuseParams, metadata: CallMetadata): Promise<{ context: BrowserContext, needsReset: boolean }> {
|
||||||
|
const hash = BrowserContext.reusableContextHash(params);
|
||||||
|
if (!this._contextForReuse || hash !== this._contextForReuse.hash || !this._contextForReuse.context.canResetForReuse()) {
|
||||||
|
if (this._contextForReuse)
|
||||||
|
await this._contextForReuse.context.close(metadata);
|
||||||
|
this._contextForReuse = { context: await this.newContext(metadata, params), hash };
|
||||||
|
return { context: this._contextForReuse.context, needsReset: false };
|
||||||
|
}
|
||||||
|
return { context: this._contextForReuse.context, needsReset: true };
|
||||||
|
}
|
||||||
|
|
||||||
_downloadCreated(page: Page, uuid: string, url: string, suggestedFilename?: string) {
|
_downloadCreated(page: Page, uuid: string, url: string, suggestedFilename?: string) {
|
||||||
const download = new Download(page, this.options.downloadsPath || '', uuid, url, suggestedFilename);
|
const download = new Download(page, this.options.downloadsPath || '', uuid, url, suggestedFilename);
|
||||||
this._downloads.set(uuid, download);
|
this._downloads.set(uuid, download);
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ import { Selectors } from '../selectors';
|
||||||
|
|
||||||
export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChannel> implements channels.BrowserChannel {
|
export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChannel> implements channels.BrowserChannel {
|
||||||
_type_Browser = true;
|
_type_Browser = true;
|
||||||
private _contextForReuse: { context: BrowserContext, hash: string } | undefined;
|
|
||||||
|
|
||||||
constructor(scope: DispatcherScope, browser: Browser) {
|
constructor(scope: DispatcherScope, browser: Browser) {
|
||||||
super(scope, browser, 'Browser', { version: browser.version(), name: browser.options.name }, true);
|
super(scope, browser, 'Browser', { version: browser.version(), name: browser.options.name }, true);
|
||||||
|
|
@ -47,22 +46,8 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
|
||||||
return { context: new BrowserContextDispatcher(this._scope, context) };
|
return { context: new BrowserContextDispatcher(this._scope, context) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for inner loop scenarios where user would like to preserve the browser window, opened page and devtools instance.
|
|
||||||
*/
|
|
||||||
async newContextForReuse(params: channels.BrowserNewContextForReuseParams, metadata: CallMetadata): Promise<channels.BrowserNewContextForReuseResult> {
|
async newContextForReuse(params: channels.BrowserNewContextForReuseParams, metadata: CallMetadata): Promise<channels.BrowserNewContextForReuseResult> {
|
||||||
const hash = BrowserContext.reusableContextHash(params);
|
return newContextForReuse(this._object, this._scope, params, metadata);
|
||||||
if (!this._contextForReuse || hash !== this._contextForReuse.hash || !this._contextForReuse.context.canResetForReuse()) {
|
|
||||||
if (this._contextForReuse)
|
|
||||||
await this._contextForReuse.context.close(metadata);
|
|
||||||
this._contextForReuse = { context: await this._object.newContext(metadata, params), hash };
|
|
||||||
} else {
|
|
||||||
const oldContextDispatcher = existingDispatcher<BrowserContextDispatcher>(this._contextForReuse.context);
|
|
||||||
oldContextDispatcher._dispose();
|
|
||||||
await this._contextForReuse.context.resetForReuse(metadata, params);
|
|
||||||
}
|
|
||||||
const context = new BrowserContextDispatcher(this._scope, this._contextForReuse.context);
|
|
||||||
return { context };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async close(): Promise<void> {
|
async close(): Promise<void> {
|
||||||
|
|
@ -118,8 +103,8 @@ export class ConnectedBrowserDispatcher extends Dispatcher<Browser, channels.Bro
|
||||||
return { context: new BrowserContextDispatcher(this._scope, context) };
|
return { context: new BrowserContextDispatcher(this._scope, context) };
|
||||||
}
|
}
|
||||||
|
|
||||||
newContextForReuse(params: channels.BrowserNewContextForReuseParams, metadata?: channels.Metadata | undefined): Promise<channels.BrowserNewContextForReuseResult> {
|
async newContextForReuse(params: channels.BrowserNewContextForReuseParams, metadata: CallMetadata): Promise<channels.BrowserNewContextForReuseResult> {
|
||||||
throw new Error('Method not implemented.');
|
return newContextForReuse(this._object, this._scope, params, metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
async close(): Promise<void> {
|
async close(): Promise<void> {
|
||||||
|
|
@ -155,3 +140,14 @@ export class ConnectedBrowserDispatcher extends Dispatcher<Browser, channels.Bro
|
||||||
await Promise.all(Array.from(this._contexts).map(context => context.close(serverSideCallMetadata())));
|
await Promise.all(Array.from(this._contexts).map(context => context.close(serverSideCallMetadata())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function newContextForReuse(browser: Browser, scope: DispatcherScope, params: channels.BrowserNewContextForReuseParams, metadata: CallMetadata): Promise<channels.BrowserNewContextForReuseResult> {
|
||||||
|
const { context, needsReset } = await browser.newContextForReuse(params, metadata);
|
||||||
|
if (needsReset) {
|
||||||
|
const oldContextDispatcher = existingDispatcher<BrowserContextDispatcher>(context);
|
||||||
|
oldContextDispatcher._dispose();
|
||||||
|
await context.resetForReuse(metadata, params);
|
||||||
|
}
|
||||||
|
const contextDispatcher = new BrowserContextDispatcher(scope, context);
|
||||||
|
return { context: contextDispatcher };
|
||||||
|
}
|
||||||
|
|
|
||||||
5
utils/innerloop-server.config.json
Normal file
5
utils/innerloop-server.config.json
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"port": 3333,
|
||||||
|
"wsPath": "/",
|
||||||
|
"headless": false
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue