mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: do not initialize full sessions for pages used in session restore (#12886)
This commit is contained in:
parent
4aaa63beaa
commit
98ed81dc00
@ -38,9 +38,7 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
|
|||||||
}
|
}
|
||||||
|
|
||||||
async newContext(params: channels.BrowserNewContextParams, metadata: CallMetadata): Promise<channels.BrowserNewContextResult> {
|
async newContext(params: channels.BrowserNewContextParams, metadata: CallMetadata): Promise<channels.BrowserNewContextResult> {
|
||||||
const context = await this._object.newContext(params);
|
const context = await this._object.newContext(metadata, params);
|
||||||
if (params.storageState)
|
|
||||||
await context.setStorageState(metadata, params.storageState);
|
|
||||||
return { context: new BrowserContextDispatcher(this._scope, context) };
|
return { context: new BrowserContextDispatcher(this._scope, context) };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,12 +89,10 @@ export class ConnectedBrowserDispatcher extends Dispatcher<Browser, channels.Bro
|
|||||||
async newContext(params: channels.BrowserNewContextParams, metadata: CallMetadata): Promise<channels.BrowserNewContextResult> {
|
async newContext(params: channels.BrowserNewContextParams, metadata: CallMetadata): Promise<channels.BrowserNewContextResult> {
|
||||||
if (params.recordVideo)
|
if (params.recordVideo)
|
||||||
params.recordVideo.dir = this._object.options.artifactsDir;
|
params.recordVideo.dir = this._object.options.artifactsDir;
|
||||||
const context = await this._object.newContext(params);
|
const context = await this._object.newContext(metadata, params);
|
||||||
this._contexts.add(context);
|
this._contexts.add(context);
|
||||||
context._setSelectors(this.selectors);
|
context._setSelectors(this.selectors);
|
||||||
context.on(BrowserContext.Events.Close, () => this._contexts.delete(context));
|
context.on(BrowserContext.Events.Close, () => this._contexts.delete(context));
|
||||||
if (params.storageState)
|
|
||||||
await context.setStorageState(metadata, params.storageState);
|
|
||||||
return { context: new BrowserContextDispatcher(this._scope, context) };
|
return { context: new BrowserContextDispatcher(this._scope, context) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,13 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as types from './types';
|
import * as types from './types';
|
||||||
import { BrowserContext } from './browserContext';
|
import { BrowserContext, validateBrowserContextOptions } from './browserContext';
|
||||||
import { Page } from './page';
|
import { Page } from './page';
|
||||||
import { Download } from './download';
|
import { Download } from './download';
|
||||||
import { ProxySettings } from './types';
|
import { ProxySettings } from './types';
|
||||||
import { ChildProcess } from 'child_process';
|
import { ChildProcess } from 'child_process';
|
||||||
import { RecentLogsCollector } from '../utils/debugLogger';
|
import { RecentLogsCollector } from '../utils/debugLogger';
|
||||||
import { SdkObject } from './instrumentation';
|
import { CallMetadata, SdkObject } from './instrumentation';
|
||||||
import { Artifact } from './artifact';
|
import { Artifact } from './artifact';
|
||||||
import { Selectors } from './selectors';
|
import { Selectors } from './selectors';
|
||||||
|
|
||||||
@ -74,12 +74,20 @@ export abstract class Browser extends SdkObject {
|
|||||||
this.options = options;
|
this.options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract newContext(options: types.BrowserContextOptions): Promise<BrowserContext>;
|
abstract doCreateNewContext(options: types.BrowserContextOptions): Promise<BrowserContext>;
|
||||||
abstract contexts(): BrowserContext[];
|
abstract contexts(): BrowserContext[];
|
||||||
abstract isConnected(): boolean;
|
abstract isConnected(): boolean;
|
||||||
abstract version(): string;
|
abstract version(): string;
|
||||||
abstract userAgent(): string;
|
abstract userAgent(): string;
|
||||||
|
|
||||||
|
async newContext(metadata: CallMetadata, options: types.BrowserContextOptions): Promise<BrowserContext> {
|
||||||
|
validateBrowserContextOptions(options, this.options);
|
||||||
|
const context = await this.doCreateNewContext(options);
|
||||||
|
if (options.storageState)
|
||||||
|
await context.setStorageState(metadata, options.storageState);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
_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);
|
||||||
|
@ -68,6 +68,7 @@ export abstract class BrowserContext extends SdkObject {
|
|||||||
readonly fetchRequest: BrowserContextAPIRequestContext;
|
readonly fetchRequest: BrowserContextAPIRequestContext;
|
||||||
private _customCloseHandler?: () => Promise<any>;
|
private _customCloseHandler?: () => Promise<any>;
|
||||||
readonly _tempDirs: string[] = [];
|
readonly _tempDirs: string[] = [];
|
||||||
|
private _settingStorageState = false;
|
||||||
|
|
||||||
constructor(browser: Browser, options: types.BrowserContextOptions, browserContextId: string | undefined) {
|
constructor(browser: Browser, options: types.BrowserContextOptions, browserContextId: string | undefined) {
|
||||||
super(browser, 'browser-context');
|
super(browser, 'browser-context');
|
||||||
@ -375,25 +376,34 @@ export abstract class BrowserContext extends SdkObject {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSettingStorageState(): boolean {
|
||||||
|
return this._settingStorageState;
|
||||||
|
}
|
||||||
|
|
||||||
async setStorageState(metadata: CallMetadata, state: types.SetStorageState) {
|
async setStorageState(metadata: CallMetadata, state: types.SetStorageState) {
|
||||||
if (state.cookies)
|
this._settingStorageState = true;
|
||||||
await this.addCookies(state.cookies);
|
try {
|
||||||
if (state.origins && state.origins.length) {
|
if (state.cookies)
|
||||||
const internalMetadata = serverSideCallMetadata();
|
await this.addCookies(state.cookies);
|
||||||
const page = await this.newPage(internalMetadata);
|
if (state.origins && state.origins.length) {
|
||||||
await page._setServerRequestInterceptor(handler => {
|
const internalMetadata = serverSideCallMetadata();
|
||||||
handler.fulfill({ body: '<html></html>' }).catch(() => {});
|
const page = await this.newPage(internalMetadata);
|
||||||
});
|
await page._setServerRequestInterceptor(handler => {
|
||||||
for (const originState of state.origins) {
|
handler.fulfill({ body: '<html></html>' }).catch(() => {});
|
||||||
const frame = page.mainFrame();
|
});
|
||||||
await frame.goto(metadata, originState.origin);
|
for (const originState of state.origins) {
|
||||||
await frame.evaluateExpression(`
|
const frame = page.mainFrame();
|
||||||
originState => {
|
await frame.goto(metadata, originState.origin);
|
||||||
for (const { name, value } of (originState.localStorage || []))
|
await frame.evaluateExpression(`
|
||||||
localStorage.setItem(name, value);
|
originState => {
|
||||||
}`, true, originState, 'utility');
|
for (const { name, value } of (originState.localStorage || []))
|
||||||
|
localStorage.setItem(name, value);
|
||||||
|
}`, true, originState, 'utility');
|
||||||
|
}
|
||||||
|
await page.close(internalMetadata);
|
||||||
}
|
}
|
||||||
await page.close(internalMetadata);
|
} finally {
|
||||||
|
this._settingStorageState = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Browser, BrowserOptions } from '../browser';
|
import { Browser, BrowserOptions } from '../browser';
|
||||||
import { assertBrowserContextIsNotOwned, BrowserContext, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
import { assertBrowserContextIsNotOwned, BrowserContext, verifyGeolocation } from '../browserContext';
|
||||||
import { assert } from '../../utils/utils';
|
import { assert } from '../../utils/utils';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import { Page, PageBinding, PageDelegate, Worker } from '../page';
|
import { Page, PageBinding, PageDelegate, Worker } from '../page';
|
||||||
@ -92,9 +92,7 @@ export class CRBrowser extends Browser {
|
|||||||
this._session.on('Browser.downloadProgress', this._onDownloadProgress.bind(this));
|
this._session.on('Browser.downloadProgress', this._onDownloadProgress.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
async newContext(options: types.BrowserContextOptions): Promise<BrowserContext> {
|
async doCreateNewContext(options: types.BrowserContextOptions): Promise<BrowserContext> {
|
||||||
validateBrowserContextOptions(options, this.options);
|
|
||||||
|
|
||||||
let proxyBypassList = undefined;
|
let proxyBypassList = undefined;
|
||||||
if (options.proxy) {
|
if (options.proxy) {
|
||||||
if (process.env.PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK)
|
if (process.env.PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK)
|
||||||
|
@ -439,7 +439,8 @@ class FrameSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _initialize(hasUIWindow: boolean) {
|
async _initialize(hasUIWindow: boolean) {
|
||||||
if (hasUIWindow &&
|
const isSettingStorageState = this._page._browserContext.isSettingStorageState();
|
||||||
|
if (!isSettingStorageState && hasUIWindow &&
|
||||||
!this._crPage._browserContext._browser.isClank() &&
|
!this._crPage._browserContext._browser.isClank() &&
|
||||||
!this._crPage._browserContext._options.noDefaultViewport) {
|
!this._crPage._browserContext._options.noDefaultViewport) {
|
||||||
const { windowId } = await this._client.send('Browser.getWindowForTarget');
|
const { windowId } = await this._client.send('Browser.getWindowForTarget');
|
||||||
@ -447,7 +448,7 @@ class FrameSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let screencastOptions: types.PageScreencastOptions | undefined;
|
let screencastOptions: types.PageScreencastOptions | undefined;
|
||||||
if (this._isMainFrame() && this._crPage._browserContext._options.recordVideo && hasUIWindow) {
|
if (!isSettingStorageState && this._isMainFrame() && this._crPage._browserContext._options.recordVideo && hasUIWindow) {
|
||||||
const screencastId = createGuid();
|
const screencastId = createGuid();
|
||||||
const outputFile = path.join(this._crPage._browserContext._options.recordVideo.dir, screencastId + '.webm');
|
const outputFile = path.join(this._crPage._browserContext._options.recordVideo.dir, screencastId + '.webm');
|
||||||
screencastOptions = {
|
screencastOptions = {
|
||||||
@ -512,41 +513,43 @@ class FrameSession {
|
|||||||
this._networkManager.initialize(),
|
this._networkManager.initialize(),
|
||||||
this._client.send('Target.setAutoAttach', { autoAttach: true, waitForDebuggerOnStart: true, flatten: true }),
|
this._client.send('Target.setAutoAttach', { autoAttach: true, waitForDebuggerOnStart: true, flatten: true }),
|
||||||
];
|
];
|
||||||
if (this._isMainFrame())
|
if (!isSettingStorageState) {
|
||||||
promises.push(this._client.send('Emulation.setFocusEmulationEnabled', { enabled: true }));
|
if (this._isMainFrame())
|
||||||
const options = this._crPage._browserContext._options;
|
promises.push(this._client.send('Emulation.setFocusEmulationEnabled', { enabled: true }));
|
||||||
if (options.bypassCSP)
|
const options = this._crPage._browserContext._options;
|
||||||
promises.push(this._client.send('Page.setBypassCSP', { enabled: true }));
|
if (options.bypassCSP)
|
||||||
if (options.ignoreHTTPSErrors)
|
promises.push(this._client.send('Page.setBypassCSP', { enabled: true }));
|
||||||
promises.push(this._client.send('Security.setIgnoreCertificateErrors', { ignore: true }));
|
if (options.ignoreHTTPSErrors)
|
||||||
if (this._isMainFrame())
|
promises.push(this._client.send('Security.setIgnoreCertificateErrors', { ignore: true }));
|
||||||
promises.push(this._updateViewport());
|
if (this._isMainFrame())
|
||||||
if (options.hasTouch)
|
promises.push(this._updateViewport());
|
||||||
promises.push(this._client.send('Emulation.setTouchEmulationEnabled', { enabled: true }));
|
if (options.hasTouch)
|
||||||
if (options.javaScriptEnabled === false)
|
promises.push(this._client.send('Emulation.setTouchEmulationEnabled', { enabled: true }));
|
||||||
promises.push(this._client.send('Emulation.setScriptExecutionDisabled', { value: true }));
|
if (options.javaScriptEnabled === false)
|
||||||
if (options.userAgent || options.locale)
|
promises.push(this._client.send('Emulation.setScriptExecutionDisabled', { value: true }));
|
||||||
promises.push(this._client.send('Emulation.setUserAgentOverride', { userAgent: options.userAgent || '', acceptLanguage: options.locale }));
|
if (options.userAgent || options.locale)
|
||||||
if (options.locale)
|
promises.push(this._client.send('Emulation.setUserAgentOverride', { userAgent: options.userAgent || '', acceptLanguage: options.locale }));
|
||||||
promises.push(emulateLocale(this._client, options.locale));
|
if (options.locale)
|
||||||
if (options.timezoneId)
|
promises.push(emulateLocale(this._client, options.locale));
|
||||||
promises.push(emulateTimezone(this._client, options.timezoneId));
|
if (options.timezoneId)
|
||||||
if (!this._crPage._browserContext._browser.options.headful)
|
promises.push(emulateTimezone(this._client, options.timezoneId));
|
||||||
promises.push(this._setDefaultFontFamilies(this._client));
|
if (!this._crPage._browserContext._browser.options.headful)
|
||||||
promises.push(this._updateGeolocation(true));
|
promises.push(this._setDefaultFontFamilies(this._client));
|
||||||
promises.push(this._updateExtraHTTPHeaders(true));
|
promises.push(this._updateGeolocation(true));
|
||||||
promises.push(this._updateRequestInterception());
|
promises.push(this._updateExtraHTTPHeaders(true));
|
||||||
promises.push(this._updateOffline(true));
|
promises.push(this._updateRequestInterception());
|
||||||
promises.push(this._updateHttpCredentials(true));
|
promises.push(this._updateOffline(true));
|
||||||
promises.push(this._updateEmulateMedia(true));
|
promises.push(this._updateHttpCredentials(true));
|
||||||
for (const binding of this._crPage._page.allBindings())
|
promises.push(this._updateEmulateMedia(true));
|
||||||
promises.push(this._initBinding(binding));
|
for (const binding of this._crPage._page.allBindings())
|
||||||
for (const source of this._crPage._browserContext._evaluateOnNewDocumentSources)
|
promises.push(this._initBinding(binding));
|
||||||
promises.push(this._evaluateOnNewDocument(source, 'main'));
|
for (const source of this._crPage._browserContext._evaluateOnNewDocumentSources)
|
||||||
for (const source of this._crPage._page._evaluateOnNewDocumentSources)
|
promises.push(this._evaluateOnNewDocument(source, 'main'));
|
||||||
promises.push(this._evaluateOnNewDocument(source, 'main'));
|
for (const source of this._crPage._page._evaluateOnNewDocumentSources)
|
||||||
if (screencastOptions)
|
promises.push(this._evaluateOnNewDocument(source, 'main'));
|
||||||
promises.push(this._startVideoRecording(screencastOptions));
|
if (screencastOptions)
|
||||||
|
promises.push(this._startVideoRecording(screencastOptions));
|
||||||
|
}
|
||||||
promises.push(this._client.send('Runtime.runIfWaitingForDebugger'));
|
promises.push(this._client.send('Runtime.runIfWaitingForDebugger'));
|
||||||
promises.push(this._firstNonInitialNavigationCommittedPromise);
|
promises.push(this._firstNonInitialNavigationCommittedPromise);
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
import { kBrowserClosedError } from '../../utils/errors';
|
import { kBrowserClosedError } from '../../utils/errors';
|
||||||
import { assert } from '../../utils/utils';
|
import { assert } from '../../utils/utils';
|
||||||
import { Browser, BrowserOptions } from '../browser';
|
import { Browser, BrowserOptions } from '../browser';
|
||||||
import { assertBrowserContextIsNotOwned, BrowserContext, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
import { assertBrowserContextIsNotOwned, BrowserContext, verifyGeolocation } from '../browserContext';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import { Page, PageBinding, PageDelegate } from '../page';
|
import { Page, PageBinding, PageDelegate } from '../page';
|
||||||
import { ConnectionTransport } from '../transport';
|
import { ConnectionTransport } from '../transport';
|
||||||
@ -76,8 +76,7 @@ export class FFBrowser extends Browser {
|
|||||||
return !this._connection._closed;
|
return !this._connection._closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
async newContext(options: types.BrowserContextOptions): Promise<BrowserContext> {
|
async doCreateNewContext(options: types.BrowserContextOptions): Promise<BrowserContext> {
|
||||||
validateBrowserContextOptions(options, this.options);
|
|
||||||
if (options.isMobile)
|
if (options.isMobile)
|
||||||
throw new Error('options.isMobile is not supported in Firefox');
|
throw new Error('options.isMobile is not supported in Firefox');
|
||||||
const { browserContextId } = await this._connection.send('Browser.createBrowserContext', { removeOnDetach: true });
|
const { browserContextId } = await this._connection.send('Browser.createBrowserContext', { removeOnDetach: true });
|
||||||
|
@ -257,6 +257,7 @@ export type BrowserContextOptions = {
|
|||||||
omitContent?: boolean,
|
omitContent?: boolean,
|
||||||
path: string
|
path: string
|
||||||
},
|
},
|
||||||
|
storageState?: SetStorageState,
|
||||||
strictSelectors?: boolean,
|
strictSelectors?: boolean,
|
||||||
proxy?: ProxySettings,
|
proxy?: ProxySettings,
|
||||||
baseURL?: string,
|
baseURL?: string,
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Browser, BrowserOptions } from '../browser';
|
import { Browser, BrowserOptions } from '../browser';
|
||||||
import { assertBrowserContextIsNotOwned, BrowserContext, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
import { assertBrowserContextIsNotOwned, BrowserContext, verifyGeolocation } from '../browserContext';
|
||||||
import { eventsHelper, RegisteredListener } from '../../utils/eventsHelper';
|
import { eventsHelper, RegisteredListener } from '../../utils/eventsHelper';
|
||||||
import { assert } from '../../utils/utils';
|
import { assert } from '../../utils/utils';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
@ -79,8 +79,7 @@ export class WKBrowser extends Browser {
|
|||||||
this._didClose();
|
this._didClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
async newContext(options: types.BrowserContextOptions): Promise<BrowserContext> {
|
async doCreateNewContext(options: types.BrowserContextOptions): Promise<BrowserContext> {
|
||||||
validateBrowserContextOptions(options, this.options);
|
|
||||||
const createOptions = options.proxy ? {
|
const createOptions = options.proxy ? {
|
||||||
proxyServer: options.proxy.server,
|
proxyServer: options.proxy.server,
|
||||||
proxyBypassList: options.proxy.bypass
|
proxyBypassList: options.proxy.bypass
|
||||||
|
@ -114,6 +114,8 @@ export class WKPage implements PageDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _initializePageProxySession() {
|
private async _initializePageProxySession() {
|
||||||
|
if (this._page._browserContext.isSettingStorageState())
|
||||||
|
return;
|
||||||
const promises: Promise<any>[] = [
|
const promises: Promise<any>[] = [
|
||||||
this._pageProxySession.send('Dialog.enable'),
|
this._pageProxySession.send('Dialog.enable'),
|
||||||
this._pageProxySession.send('Emulation.setActiveAndFocused', { active: true }),
|
this._pageProxySession.send('Emulation.setActiveAndFocused', { active: true }),
|
||||||
@ -183,6 +185,10 @@ export class WKPage implements PageDelegate {
|
|||||||
promises.push(session.send('Network.setInterceptionEnabled', { enabled: true }));
|
promises.push(session.send('Network.setInterceptionEnabled', { enabled: true }));
|
||||||
promises.push(session.send('Network.addInterception', { url: '.*', stage: 'request', isRegex: true }));
|
promises.push(session.send('Network.addInterception', { url: '.*', stage: 'request', isRegex: true }));
|
||||||
}
|
}
|
||||||
|
if (this._page._browserContext.isSettingStorageState()) {
|
||||||
|
await Promise.all(promises);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const contextOptions = this._browserContext._options;
|
const contextOptions = this._browserContext._options;
|
||||||
if (contextOptions.userAgent)
|
if (contextOptions.userAgent)
|
||||||
|
@ -48,8 +48,8 @@ it('should capture local storage', async ({ contextFactory }) => {
|
|||||||
}]);
|
}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set local storage', async ({ browser }) => {
|
it('should set local storage', async ({ contextFactory }) => {
|
||||||
const context = await browser.newContext({
|
const context = await contextFactory({
|
||||||
storageState: {
|
storageState: {
|
||||||
cookies: [],
|
cookies: [],
|
||||||
origins: [
|
origins: [
|
||||||
@ -162,3 +162,35 @@ it('should not emit events about internal page', async ({ contextFactory }) => {
|
|||||||
await context.storageState();
|
await context.storageState();
|
||||||
expect(events).toHaveLength(0);
|
expect(events).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not restore localStorage twice', async ({ contextFactory }) => {
|
||||||
|
const context = await contextFactory({
|
||||||
|
storageState: {
|
||||||
|
cookies: [],
|
||||||
|
origins: [
|
||||||
|
{
|
||||||
|
origin: 'https://www.example.com',
|
||||||
|
localStorage: [{
|
||||||
|
name: 'name1',
|
||||||
|
value: 'value1'
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.route('**/*', route => {
|
||||||
|
route.fulfill({ body: '<html></html>' }).catch(() => {});
|
||||||
|
});
|
||||||
|
await page.goto('https://www.example.com');
|
||||||
|
const localStorage1 = await page.evaluate('window.localStorage');
|
||||||
|
expect(localStorage1).toEqual({ name1: 'value1' });
|
||||||
|
|
||||||
|
await page.evaluate(() => window.localStorage['name1'] = 'value2');
|
||||||
|
|
||||||
|
await page.goto('https://www.example.com');
|
||||||
|
const localStorage2 = await page.evaluate('window.localStorage');
|
||||||
|
expect(localStorage2).toEqual({ name1: 'value2' });
|
||||||
|
|
||||||
|
await context.close();
|
||||||
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user