chore(console): expose window.playwright by default (#36921)
This commit is contained in:
parent
cc09a085ef
commit
60e9b0edc6
|
|
@ -2758,7 +2758,7 @@ Returns the opener for popup pages and `null` for others. If the opener has been
|
|||
## async method: Page.pause
|
||||
* since: v1.9
|
||||
|
||||
Pauses script execution. Playwright will stop executing the script and wait for the user to either press 'Resume'
|
||||
Pauses script execution. Playwright will stop executing the script and wait for the user to either press the 'Resume'
|
||||
button in the page overlay or to call `playwright.resume()` in the DevTools console.
|
||||
|
||||
User can inspect selectors or perform manual steps while paused. Resume will continue running the original script from
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ declare global {
|
|||
interface Window {
|
||||
playwright?: any;
|
||||
inspect: (element: Element | undefined) => void;
|
||||
__pw_resume: () => Promise<void>;
|
||||
__pw_resume?: () => Promise<void>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,6 +139,8 @@ export class ConsoleAPI {
|
|||
}
|
||||
|
||||
private _resume() {
|
||||
if (!this._injectedScript.window.__pw_resume)
|
||||
return false;
|
||||
this._injectedScript.window.__pw_resume().catch(() => {});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3599,8 +3599,8 @@ export interface Page {
|
|||
opener(): Promise<null|Page>;
|
||||
|
||||
/**
|
||||
* Pauses script execution. Playwright will stop executing the script and wait for the user to either press 'Resume'
|
||||
* button in the page overlay or to call `playwright.resume()` in the DevTools console.
|
||||
* Pauses script execution. Playwright will stop executing the script and wait for the user to either press the
|
||||
* 'Resume' button in the page overlay or to call `playwright.resume()` in the DevTools console.
|
||||
*
|
||||
* User can inspect selectors or perform manual steps while paused. Resume will continue running the original script
|
||||
* from the place it was paused.
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ export abstract class BrowserType extends SdkObject {
|
|||
private _validateLaunchOptions(options: types.LaunchOptions): types.LaunchOptions {
|
||||
const { devtools = false } = options;
|
||||
let { headless = !devtools, downloadsPath, proxy } = options;
|
||||
if (debugMode())
|
||||
if (debugMode() === 'inspector')
|
||||
headless = false;
|
||||
if (downloadsPath && !path.isAbsolute(downloadsPath))
|
||||
downloadsPath = path.join(process.cwd(), downloadsPath);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ export class Chromium extends BrowserType {
|
|||
constructor(parent: SdkObject) {
|
||||
super(parent, 'chromium');
|
||||
|
||||
if (debugMode())
|
||||
if (debugMode() === 'inspector')
|
||||
this._devtools = this._createDevTools();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1628,11 +1628,11 @@ export class Frame extends SdkObject {
|
|||
this._firedNetworkIdleSelf = false;
|
||||
}
|
||||
|
||||
async extendInjectedScript(source: string, arg?: any): Promise<js.JSHandle> {
|
||||
async extendInjectedScript(source: string, arg?: any) {
|
||||
const context = await this._context('main');
|
||||
const injectedScriptHandle = await context.injectedScript();
|
||||
return injectedScriptHandle.evaluateHandle((injectedScript, { source, arg }) => {
|
||||
return injectedScript.extend(source, arg);
|
||||
await injectedScriptHandle.evaluate((injectedScript, { source, arg }) => {
|
||||
injectedScript.extend(source, arg);
|
||||
}, { source, arg });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export function debugMode() {
|
|||
return 'console';
|
||||
if (_debugMode === '0' || _debugMode === 'false')
|
||||
return '';
|
||||
return _debugMode ? 'inspector' : '';
|
||||
return _debugMode ? 'inspector' : 'console';
|
||||
}
|
||||
|
||||
const _isUnderTest = getAsBooleanFromENV('PWTEST_UNDER_TEST');
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ export const nodePlatform: Platform = {
|
|||
|
||||
inspectCustom: util.inspect.custom,
|
||||
|
||||
isDebugMode: () => !!debugMode(),
|
||||
isDebugMode: () => debugMode() === 'inspector',
|
||||
|
||||
isJSDebuggerAttached: () => !!require('inspector').url(),
|
||||
|
||||
|
|
|
|||
|
|
@ -3599,8 +3599,8 @@ export interface Page {
|
|||
opener(): Promise<null|Page>;
|
||||
|
||||
/**
|
||||
* Pauses script execution. Playwright will stop executing the script and wait for the user to either press 'Resume'
|
||||
* button in the page overlay or to call `playwright.resume()` in the DevTools console.
|
||||
* Pauses script execution. Playwright will stop executing the script and wait for the user to either press the
|
||||
* 'Resume' button in the page overlay or to call `playwright.resume()` in the DevTools console.
|
||||
*
|
||||
* User can inspect selectors or perform manual steps while paused. Resume will continue running the original script
|
||||
* from the place it was paused.
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
|||
if (testIdAttribute)
|
||||
playwrightLibrary.selectors.setTestIdAttribute(testIdAttribute);
|
||||
testInfo.snapshotSuffix = process.platform;
|
||||
if (debugMode())
|
||||
if (debugMode() === 'inspector')
|
||||
(testInfo as TestInfoImpl)._setDebugMode();
|
||||
|
||||
playwright._defaultContextOptions = _combinedContextOptions;
|
||||
|
|
|
|||
|
|
@ -209,8 +209,6 @@ it('coverage should work', async ({ server, launchPersistent, browserName }) =>
|
|||
});
|
||||
|
||||
it('should respect selectors', async ({ playwright, launchPersistent }) => {
|
||||
const { page } = await launchPersistent();
|
||||
|
||||
const defaultContextCSS = () => ({
|
||||
query(root, selector) {
|
||||
return root.querySelector(selector);
|
||||
|
|
@ -221,6 +219,7 @@ it('should respect selectors', async ({ playwright, launchPersistent }) => {
|
|||
});
|
||||
await playwright.selectors.register('defaultContextCSS', defaultContextCSS);
|
||||
|
||||
const { page } = await launchPersistent();
|
||||
await page.setContent(`<div>hello</div>`);
|
||||
expect(await page.innerHTML('css=div')).toBe('hello');
|
||||
expect(await page.innerHTML('defaultContextCSS=div')).toBe('hello');
|
||||
|
|
|
|||
|
|
@ -87,11 +87,7 @@ it.describe('pause', () => {
|
|||
// @ts-ignore
|
||||
await page.pause({ __testHookKeepTestTimeout: true });
|
||||
})();
|
||||
await Promise.all([
|
||||
page.waitForFunction(() => (window as any).playwright && (window as any).playwright.resume).then(() => {
|
||||
return page.evaluate('window.playwright.resume()');
|
||||
})
|
||||
]);
|
||||
await page.waitForFunction(() => (window as any).playwright?.resume() !== false);
|
||||
await scriptPromise;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -76,15 +76,14 @@ it('should work when registered on global', async ({ browser, mode }) => {
|
|||
});
|
||||
|
||||
it('should work with path', async ({ playwright, browser, asset }) => {
|
||||
const page = await browser.newPage();
|
||||
await playwright.selectors.register('foo', { path: asset('sectionselectorengine.js') });
|
||||
const page = await browser.newPage();
|
||||
await page.setContent('<section></section>');
|
||||
expect(await page.$eval('foo=whatever', e => e.nodeName)).toBe('SECTION');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should work in main and isolated world', async ({ playwright, browser }) => {
|
||||
const page = await browser.newPage();
|
||||
const createDummySelector = () => ({
|
||||
query(root, selector) {
|
||||
return window['__answer'];
|
||||
|
|
@ -95,6 +94,7 @@ it('should work in main and isolated world', async ({ playwright, browser }) =>
|
|||
});
|
||||
await playwright.selectors.register('main', createDummySelector);
|
||||
await playwright.selectors.register('isolated', createDummySelector, { contentScript: true });
|
||||
const page = await browser.newPage();
|
||||
await page.setContent('<div><span><section></section></span></div>');
|
||||
await page.evaluate(() => window['__answer'] = document.querySelector('span'));
|
||||
// Works in main if asked.
|
||||
|
|
@ -151,7 +151,6 @@ it('should throw "already registered" error when registering', { annotation: { t
|
|||
});
|
||||
|
||||
it('should not rely on engines working from the root', async ({ playwright, browser }) => {
|
||||
const page = await browser.newPage();
|
||||
const createValueEngine = () => ({
|
||||
query(root, selector) {
|
||||
return root && root.value.includes(selector) ? root : undefined;
|
||||
|
|
@ -160,15 +159,14 @@ it('should not rely on engines working from the root', async ({ playwright, brow
|
|||
return root && root.value.includes(selector) ? [root] : [];
|
||||
},
|
||||
});
|
||||
|
||||
await playwright.selectors.register('__value', createValueEngine);
|
||||
const page = await browser.newPage();
|
||||
await page.setContent(`<input id=input1 value=value1><input id=input2 value=value2>`);
|
||||
expect(await page.$eval('input >> __value=value2', e => e.id)).toBe('input2');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should throw a nice error if the selector returns a bad value', async ({ playwright, browser }) => {
|
||||
const page = await browser.newPage();
|
||||
const createFakeEngine = () => ({
|
||||
query(root, selector) {
|
||||
return [document.body];
|
||||
|
|
@ -179,6 +177,7 @@ it('should throw a nice error if the selector returns a bad value', async ({ pla
|
|||
});
|
||||
|
||||
await playwright.selectors.register('__fake', createFakeEngine);
|
||||
const page = await browser.newPage();
|
||||
const error = await page.$('__fake=value2').catch(e => e);
|
||||
expect(error.message).toContain('Expected a Node but got [object Array]');
|
||||
await page.close();
|
||||
|
|
|
|||
|
|
@ -98,20 +98,3 @@ it('init script should run only once in iframe', async ({ page, server, browserN
|
|||
'init script: ' + (browserName === 'firefox' ? 'no url yet' : '/frames/frame.html'),
|
||||
]);
|
||||
});
|
||||
|
||||
it('init script should not observe playwright internals', async ({ server, page, trace, isAndroid }) => {
|
||||
it.skip(!!process.env.PW_CLOCK, 'clock installs globalThis.__pwClock');
|
||||
it.skip(trace === 'on', 'tracing installs __playwright_snapshot_streamer');
|
||||
it.fixme(isAndroid, 'There is probably context reuse between this test and some other test that installs a binding');
|
||||
|
||||
await page.addInitScript(() => {
|
||||
window['check'] = () => {
|
||||
const keys = Reflect.ownKeys(globalThis).map(k => k.toString());
|
||||
return keys.find(name => name.includes('playwright') || name.includes('_pw')) || 'none';
|
||||
};
|
||||
window['found'] = window['check']();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate(() => window['found'])).toBe('none');
|
||||
expect(await page.evaluate(() => window['check']())).toBe('none');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ it('should be atomic', async ({ playwright, page }) => {
|
|||
}
|
||||
});
|
||||
await playwright.selectors.register('dispatchEvent', createDummySelector);
|
||||
await page.setContent(`<div onclick="window._clicked=true">Hello</div>`);
|
||||
await page.goto(`data:text/html,<div onclick="window._clicked=true">Hello</div>`);
|
||||
await page.dispatchEvent('dispatchEvent=div', 'click');
|
||||
expect(await page.evaluate(() => window['_clicked'])).toBe(true);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ it('textContent should be atomic', async ({ playwright, page }) => {
|
|||
}
|
||||
});
|
||||
await playwright.selectors.register('textContent', createDummySelector);
|
||||
await page.setContent(`<div>Hello</div>`);
|
||||
await page.goto(`data:text/html,<div>Hello</div>`);
|
||||
const tc = await page.textContent('textContent=div');
|
||||
expect(tc).toBe('Hello');
|
||||
expect(await page.evaluate(() => document.querySelector('div').textContent)).toBe('modified');
|
||||
|
|
@ -57,7 +57,7 @@ it('innerText should be atomic', async ({ playwright, page }) => {
|
|||
}
|
||||
});
|
||||
await playwright.selectors.register('innerText', createDummySelector);
|
||||
await page.setContent(`<div>Hello</div>`);
|
||||
await page.goto(`data:text/html,<div>Hello</div>`);
|
||||
const tc = await page.innerText('innerText=div');
|
||||
expect(tc).toBe('Hello');
|
||||
expect(await page.evaluate(() => document.querySelector('div').innerText)).toBe('modified');
|
||||
|
|
@ -79,7 +79,7 @@ it('innerHTML should be atomic', async ({ playwright, page }) => {
|
|||
}
|
||||
});
|
||||
await playwright.selectors.register('innerHTML', createDummySelector);
|
||||
await page.setContent(`<div>Hello<span>world</span></div>`);
|
||||
await page.goto(`data:text/html,<div>Hello<span>world</span></div>`);
|
||||
const tc = await page.innerHTML('innerHTML=div');
|
||||
expect(tc).toBe('Hello<span>world</span>');
|
||||
expect(await page.evaluate(() => document.querySelector('div').innerHTML)).toBe('modified');
|
||||
|
|
@ -101,7 +101,7 @@ it('getAttribute should be atomic', async ({ playwright, page }) => {
|
|||
}
|
||||
});
|
||||
await playwright.selectors.register('getAttribute', createDummySelector);
|
||||
await page.setContent(`<div foo=hello></div>`);
|
||||
await page.goto(`data:text/html,<div foo=hello></div>`);
|
||||
const tc = await page.getAttribute('getAttribute=div', 'foo');
|
||||
expect(tc).toBe('hello');
|
||||
expect(await page.evaluate(() => document.querySelector('div').getAttribute('foo'))).toBe('modified');
|
||||
|
|
@ -123,7 +123,7 @@ it('isVisible should be atomic', async ({ playwright, page }) => {
|
|||
}
|
||||
});
|
||||
await playwright.selectors.register('isVisible', createDummySelector);
|
||||
await page.setContent(`<div>Hello</div>`);
|
||||
await page.goto(`data:text/html,<div>Hello</div>`);
|
||||
const result = await page.isVisible('isVisible=div');
|
||||
expect(result).toBe(true);
|
||||
expect(await page.evaluate(() => document.querySelector('div').style.display)).toBe('none');
|
||||
|
|
@ -139,6 +139,6 @@ it('should take java-style string', async ({ playwright, page }) => {
|
|||
}
|
||||
}`;
|
||||
await playwright.selectors.register('objectLiteral', createDummySelector);
|
||||
await page.setContent(`<div>Hello</div>`);
|
||||
await page.goto(`data:text/html,<div>Hello</div>`);
|
||||
await page.textContent('objectLiteral=div');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -896,7 +896,7 @@ test('page.pause() should disable test timeout', async ({ runInlineTest }) => {
|
|||
expect(result.output).toContain('success!');
|
||||
});
|
||||
|
||||
test('PWDEBUG=console should expose window.playwright', async ({ runInlineTest }) => {
|
||||
test('window.playwright should be exposed by default', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/36772' } }, async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.test.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
|
@ -907,7 +907,60 @@ test('PWDEBUG=console should expose window.playwright', async ({ runInlineTest }
|
|||
expect(bodyTag).toBe('BODY');
|
||||
});
|
||||
`,
|
||||
}, {}, { PWDEBUG: 'console' });
|
||||
}, {}, {});
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
||||
test('window.playwright should not override existing property', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/36772' } }, async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.test.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('test', async ({ page }) => {
|
||||
await page.setContent('<script>window.playwright = "foo"</script>');
|
||||
expect(await page.evaluate(() => window.playwright)).toBe('foo');
|
||||
});
|
||||
`,
|
||||
}, {}, {});
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
||||
test('PWDEBUG=0 should opt-out from exposing window.playwright', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/36772' } }, async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.test.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('test', async ({ page }) => {
|
||||
await page.setContent('<body></body>');
|
||||
expect(await page.evaluate(() => window.playwright)).toBeUndefined();
|
||||
});
|
||||
`,
|
||||
}, {}, { PWDEBUG: '0' });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
});
|
||||
|
||||
test('init script should not observe playwright internals', async ({ server, runInlineTest }) => {
|
||||
test.skip(!!process.env.PW_CLOCK, 'clock installs globalThis.__pwClock');
|
||||
const result = await runInlineTest({
|
||||
'a.test.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('test', async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
window['check'] = () => {
|
||||
const keys = Reflect.ownKeys(globalThis).map(k => k.toString());
|
||||
return keys.find(name => name.includes('playwright') || name.includes('_pw')) || 'none';
|
||||
};
|
||||
window['found'] = window['check']();
|
||||
});
|
||||
await page.goto("${server.EMPTY_PAGE}");
|
||||
expect(await page.evaluate(() => window['found'])).toBe('none');
|
||||
expect(await page.evaluate(() => window['check']())).toBe('none');
|
||||
});
|
||||
`,
|
||||
}, {}, { PWDEBUG: '0' });
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1462,20 +1462,20 @@ pw:api | Close context
|
|||
|
||||
test('reading network request / response should not be listed as step', {
|
||||
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/33558' }
|
||||
}, async ({ runInlineTest, server }) => {
|
||||
}, async ({ runInlineTest, server, page }) => {
|
||||
const result = await runInlineTest({
|
||||
'reporter.ts': stepIndentReporter,
|
||||
'playwright.config.ts': `module.exports = { reporter: './reporter' };`,
|
||||
'a.test.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('waitForResponse step nesting', async ({ page }) => {
|
||||
page.on('request', async request => {
|
||||
await request.allHeaders();
|
||||
});
|
||||
page.on('response', async response => {
|
||||
await response.text();
|
||||
});
|
||||
await page.goto('${server.EMPTY_PAGE}');
|
||||
const [request, response] = await Promise.all([
|
||||
page.waitForRequest('${server.EMPTY_PAGE}'),
|
||||
page.waitForResponse('${server.EMPTY_PAGE}'),
|
||||
page.goto('${server.EMPTY_PAGE}'),
|
||||
]);
|
||||
await request.allHeaders();
|
||||
await response.text();
|
||||
});
|
||||
`
|
||||
}, { reporter: '', workers: 1, timeout: 3000 });
|
||||
|
|
@ -1489,7 +1489,9 @@ fixture | context
|
|||
pw:api | Create context
|
||||
fixture | page
|
||||
pw:api | Create page
|
||||
pw:api |Navigate to "/empty.html" @ a.test.ts:10
|
||||
pw:api |Wait for event "request" @ a.test.ts:5
|
||||
pw:api |Wait for event "response" @ a.test.ts:6
|
||||
pw:api |Navigate to "/empty.html" @ a.test.ts:7
|
||||
hook |After Hooks
|
||||
fixture | page
|
||||
fixture | context
|
||||
|
|
|
|||
Loading…
Reference in New Issue