test: migrate some helpers to fixtures, use testOutputDir (#3926)
This commit is contained in:
parent
9de39b1e85
commit
4e2d75d9f7
|
|
@ -15,9 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
import utils from './utils';
|
||||
import { it, expect, verifyViewport } from './playwright.fixtures';
|
||||
|
||||
it('should create new context', async function({browser}) {
|
||||
expect(browser.contexts().length).toBe(0);
|
||||
|
|
@ -90,7 +88,7 @@ it('should isolate localStorage and cookies', async function({browser, server})
|
|||
it('should propagate default viewport to the page', async ({ browser }) => {
|
||||
const context = await browser.newContext({ viewport: { width: 456, height: 789 } });
|
||||
const page = await context.newPage();
|
||||
await utils.verifyViewport(page, 456, 789);
|
||||
await verifyViewport(page, 456, 789);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
|
|
@ -99,7 +97,7 @@ it('should make a copy of default viewport', async ({ browser }) => {
|
|||
const context = await browser.newContext({ viewport });
|
||||
viewport.width = 567;
|
||||
const page = await context.newPage();
|
||||
await utils.verifyViewport(page, 456, 789);
|
||||
await verifyViewport(page, 456, 789);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -14,9 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
import * as utils from './utils';
|
||||
import { it, expect, attachFrame } from './playwright.fixtures';
|
||||
|
||||
it('should bypass CSP meta tag', async ({browser, server}) => {
|
||||
// Make sure CSP prohibits addScriptTag.
|
||||
|
|
@ -83,7 +81,7 @@ it('should bypass CSP in iframes as well', async ({browser, server}) => {
|
|||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = await utils.attachFrame(page, 'frame1', server.PREFIX + '/csp.html');
|
||||
const frame = await attachFrame(page, 'frame1', server.PREFIX + '/csp.html');
|
||||
await frame.addScriptTag({content: 'window["__injected"] = 42;'}).catch(e => void e);
|
||||
expect(await frame.evaluate('window["__injected"]')).toBe(undefined);
|
||||
await context.close();
|
||||
|
|
@ -94,7 +92,7 @@ it('should bypass CSP in iframes as well', async ({browser, server}) => {
|
|||
const context = await browser.newContext({ bypassCSP: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = await utils.attachFrame(page, 'frame1', server.PREFIX + '/csp.html');
|
||||
const frame = await attachFrame(page, 'frame1', server.PREFIX + '/csp.html');
|
||||
await frame.addScriptTag({content: 'window["__injected"] = 42;'}).catch(e => void e);
|
||||
expect(await frame.evaluate('window["__injected"]')).toBe(42);
|
||||
await context.close();
|
||||
|
|
|
|||
|
|
@ -14,9 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
import utils from './utils';
|
||||
import { it, expect, attachFrame } from './playwright.fixtures';
|
||||
|
||||
it('should work', async ({browser, server}) => {
|
||||
{
|
||||
|
|
@ -49,7 +47,7 @@ it('should work for subframes', async ({browser, server}) => {
|
|||
const page = await context.newPage();
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
utils.attachFrame(page, 'frame1', server.EMPTY_PAGE),
|
||||
attachFrame(page, 'frame1', server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['user-agent']).toBe('foobar');
|
||||
await context.close();
|
||||
|
|
|
|||
|
|
@ -14,18 +14,16 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
import utils from './utils';
|
||||
import { it, expect, verifyViewport } from './playwright.fixtures';
|
||||
|
||||
it('should get the proper default viewport size', async ({page, server}) => {
|
||||
await utils.verifyViewport(page, 1280, 720);
|
||||
await verifyViewport(page, 1280, 720);
|
||||
});
|
||||
|
||||
it('should set the proper viewport size', async ({page, server}) => {
|
||||
await utils.verifyViewport(page, 1280, 720);
|
||||
await verifyViewport(page, 1280, 720);
|
||||
await page.setViewportSize({width: 123, height: 456});
|
||||
await utils.verifyViewport(page, 123, 456);
|
||||
await verifyViewport(page, 123, 456);
|
||||
});
|
||||
|
||||
it('should return correct outerWidth and outerHeight', async ({page}) => {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
*/
|
||||
|
||||
import { options } from './playwright.fixtures';
|
||||
import utils from './utils';
|
||||
import { serverFixtures } from './remoteServer.fixture';
|
||||
const { it, expect, describe } = serverFixtures;
|
||||
|
||||
|
|
@ -175,13 +174,13 @@ describe('connect', suite => {
|
|||
}
|
||||
});
|
||||
// Register one engine before connecting.
|
||||
await utils.registerEngine(playwright, 'mycss1', mycss);
|
||||
await playwright.selectors.register('mycss1', mycss);
|
||||
|
||||
const browser1 = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() });
|
||||
const context1 = await browser1.newContext();
|
||||
|
||||
// Register another engine after creating context.
|
||||
await utils.registerEngine(playwright, 'mycss2', mycss);
|
||||
await playwright.selectors.register('mycss2', mycss);
|
||||
|
||||
const page1 = await context1.newPage();
|
||||
await page1.setContent(`<div>hello</div>`);
|
||||
|
|
@ -192,7 +191,7 @@ describe('connect', suite => {
|
|||
const browser2 = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() });
|
||||
|
||||
// Register third engine after second connect.
|
||||
await utils.registerEngine(playwright, 'mycss3', mycss);
|
||||
await playwright.selectors.register('mycss3', mycss);
|
||||
|
||||
const page2 = await browser2.newPage();
|
||||
await page2.setContent(`<div>hello</div>`);
|
||||
|
|
|
|||
|
|
@ -16,9 +16,7 @@
|
|||
import { it, expect, options } from '../playwright.fixtures';
|
||||
|
||||
import path from 'path';
|
||||
import utils from '../utils';
|
||||
import type { ChromiumBrowser, ChromiumBrowserContext } from '../..';
|
||||
const { makeUserDataDir, removeUserDataDir } = utils;
|
||||
|
||||
it('should throw with remote-debugging-pipe argument', (test, parameters) => {
|
||||
test.skip(options.WIRE || !options.CHROMIUM(parameters));
|
||||
|
|
@ -58,8 +56,8 @@ it('should open devtools when "devtools: true" option is given', (test, paramete
|
|||
|
||||
it('should return background pages', (test, parameters) => {
|
||||
test.skip(!options.CHROMIUM(parameters));
|
||||
}, async ({browserType, defaultBrowserOptions}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
}, async ({browserType, defaultBrowserOptions, createUserDataDir}) => {
|
||||
const userDataDir = await createUserDataDir();
|
||||
const extensionPath = path.join(__dirname, '..', 'assets', 'simple-extension');
|
||||
const extensionOptions = {...defaultBrowserOptions,
|
||||
headless: false,
|
||||
|
|
@ -77,7 +75,6 @@ it('should return background pages', (test, parameters) => {
|
|||
expect(context.backgroundPages()).toContain(backgroundPage);
|
||||
expect(context.pages()).not.toContain(backgroundPage);
|
||||
await context.close();
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
|
||||
it('should not create pages automatically', (test, parameters) => {
|
||||
|
|
|
|||
|
|
@ -26,11 +26,8 @@ const fixtures = playwrightFixtures.declareTestFixtures<TestState>();
|
|||
const { it, expect, describe, defineTestFixture } = fixtures;
|
||||
|
||||
|
||||
defineTestFixture('outputTraceFile', async ({tmpDir}, test) => {
|
||||
const outputTraceFile = path.join(tmpDir, `trace.json`);
|
||||
await test(outputTraceFile);
|
||||
if (fs.existsSync(outputTraceFile))
|
||||
fs.unlinkSync(outputTraceFile);
|
||||
defineTestFixture('outputTraceFile', async ({testOutputDir}, test) => {
|
||||
await test(path.join(testOutputDir, `trace.json`));
|
||||
});
|
||||
|
||||
describe('oopif', (suite, parameters) => {
|
||||
|
|
@ -43,8 +40,8 @@ describe('oopif', (suite, parameters) => {
|
|||
expect(fs.existsSync(outputTraceFile)).toBe(true);
|
||||
});
|
||||
|
||||
it('should create directories as needed', async ({browser, page, server, tmpDir}) => {
|
||||
const filePath = path.join(tmpDir, 'these', 'are', 'directories');
|
||||
it('should create directories as needed', async ({browser, page, server, testOutputDir}) => {
|
||||
const filePath = path.join(testOutputDir, 'these', 'are', 'directories', 'trace.json');
|
||||
await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: filePath});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await (browser as ChromiumBrowser).stopTracing();
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import utils from './utils';
|
||||
import { it, expect, options, attachFrame } from './playwright.fixtures';
|
||||
|
||||
async function giveItAChanceToClick(page) {
|
||||
for (let i = 0; i < 5; i++)
|
||||
|
|
@ -315,7 +314,7 @@ it('should click links which cause navigation', async ({page, server}) => {
|
|||
it('should click the button inside an iframe', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<div style="width:100px;height:100px">spacer</div>');
|
||||
await utils.attachFrame(page, 'button-test', server.PREFIX + '/input/button.html');
|
||||
await attachFrame(page, 'button-test', server.PREFIX + '/input/button.html');
|
||||
const frame = page.frames()[1];
|
||||
const button = await frame.$('button');
|
||||
await button.click();
|
||||
|
|
@ -331,7 +330,7 @@ it('should click the button with fixed position inside an iframe', (test, parame
|
|||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.setContent('<div style="width:100px;height:2000px">spacer</div>');
|
||||
await utils.attachFrame(page, 'button-test', server.CROSS_PROCESS_PREFIX + '/input/button.html');
|
||||
await attachFrame(page, 'button-test', server.CROSS_PROCESS_PREFIX + '/input/button.html');
|
||||
const frame = page.frames()[1];
|
||||
await frame.$eval('button', button => button.style.setProperty('position', 'fixed'));
|
||||
await frame.click('button');
|
||||
|
|
@ -343,7 +342,7 @@ it('should click the button with deviceScaleFactor set', async ({browser, server
|
|||
const page = await context.newPage();
|
||||
expect(await page.evaluate(() => window.devicePixelRatio)).toBe(5);
|
||||
await page.setContent('<div style="width:100px;height:100px">spacer</div>');
|
||||
await utils.attachFrame(page, 'button-test', server.PREFIX + '/input/button.html');
|
||||
await attachFrame(page, 'button-test', server.PREFIX + '/input/button.html');
|
||||
const frame = page.frames()[1];
|
||||
const button = await frame.$('button');
|
||||
await button.click();
|
||||
|
|
|
|||
|
|
@ -15,9 +15,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
import { it, expect, verifyViewport } from './playwright.fixtures';
|
||||
import fs from 'fs';
|
||||
import utils from './utils';
|
||||
|
||||
it('context.cookies() should work', async ({server, launchPersistent}) => {
|
||||
const {page} = await launchPersistent();
|
||||
|
|
@ -119,9 +118,9 @@ it('should(not) block third party cookies', async ({server, launchPersistent, is
|
|||
|
||||
it('should support viewport option', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({viewport: { width: 456, height: 789 }});
|
||||
await utils.verifyViewport(page, 456, 789);
|
||||
await verifyViewport(page, 456, 789);
|
||||
const page2 = await context.newPage();
|
||||
await utils.verifyViewport(page2, 456, 789);
|
||||
await verifyViewport(page2, 456, 789);
|
||||
});
|
||||
|
||||
it('should support deviceScaleFactor option', async ({launchPersistent}) => {
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import fs from 'fs';
|
||||
import utils from './utils';
|
||||
const { removeUserDataDir, makeUserDataDir } = utils;
|
||||
|
||||
it('should support hasTouch option', async ({server, launchPersistent}) => {
|
||||
const {page} = await launchPersistent({hasTouch: true});
|
||||
|
|
@ -81,20 +79,18 @@ it('should support extraHTTPHeaders option', (test, parameters) => {
|
|||
|
||||
it('should accept userDataDir', (test, parameters) => {
|
||||
test.flaky(options.CHROMIUM(parameters));
|
||||
}, async ({launchPersistent, tmpDir}) => {
|
||||
const {context} = await launchPersistent();
|
||||
// Note: we need an open page to make sure its functional.
|
||||
expect(fs.readdirSync(tmpDir).length).toBeGreaterThan(0);
|
||||
}, async ({createUserDataDir, browserType, defaultBrowserOptions}) => {
|
||||
const userDataDir = await createUserDataDir();
|
||||
const context = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||
await context.close();
|
||||
expect(fs.readdirSync(tmpDir).length).toBeGreaterThan(0);
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(tmpDir);
|
||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should restore state from userDataDir', (test, parameters) => {
|
||||
test.slow();
|
||||
}, async ({browserType, defaultBrowserOptions, server, launchPersistent}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
}, async ({browserType, defaultBrowserOptions, server, createUserDataDir}) => {
|
||||
const userDataDir = await createUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
|
@ -107,23 +103,19 @@ it('should restore state from userDataDir', (test, parameters) => {
|
|||
expect(await page2.evaluate(() => localStorage.hey)).toBe('hello');
|
||||
await browserContext2.close();
|
||||
|
||||
const userDataDir2 = await makeUserDataDir();
|
||||
const userDataDir2 = await createUserDataDir();
|
||||
const browserContext3 = await browserType.launchPersistentContext(userDataDir2, defaultBrowserOptions);
|
||||
const page3 = await browserContext3.newPage();
|
||||
await page3.goto(server.EMPTY_PAGE);
|
||||
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('hello');
|
||||
await browserContext3.close();
|
||||
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
|
||||
it('should restore cookies from userDataDir', (test, parameters) => {
|
||||
test.slow();
|
||||
test.flaky(options.CHROMIUM(parameters));
|
||||
}, async ({browserType, defaultBrowserOptions, server, launchPersistent}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
}, async ({browserType, defaultBrowserOptions, server, createUserDataDir}) => {
|
||||
const userDataDir = await createUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
|
@ -140,16 +132,12 @@ it('should restore cookies from userDataDir', (test, parameters) => {
|
|||
expect(await page2.evaluate(() => document.cookie)).toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext2.close();
|
||||
|
||||
const userDataDir2 = await makeUserDataDir();
|
||||
const userDataDir2 = await createUserDataDir();
|
||||
const browserContext3 = await browserType.launchPersistentContext(userDataDir2, defaultBrowserOptions);
|
||||
const page3 = await browserContext3.newPage();
|
||||
await page3.goto(server.EMPTY_PAGE);
|
||||
expect(await page3.evaluate(() => document.cookie)).not.toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext3.close();
|
||||
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
|
||||
it('should have default URL when launching browser', async ({launchPersistent}) => {
|
||||
|
|
@ -160,22 +148,23 @@ it('should have default URL when launching browser', async ({launchPersistent})
|
|||
|
||||
it('should throw if page argument is passed', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters));
|
||||
}, async ({browserType, defaultBrowserOptions, server, tmpDir}) => {
|
||||
}, async ({browserType, defaultBrowserOptions, server, createUserDataDir}) => {
|
||||
const options = {...defaultBrowserOptions, args: [server.EMPTY_PAGE] };
|
||||
const error = await browserType.launchPersistentContext(tmpDir, options).catch(e => e);
|
||||
const error = await browserType.launchPersistentContext(await createUserDataDir(), options).catch(e => e);
|
||||
expect(error.message).toContain('can not specify page');
|
||||
});
|
||||
|
||||
it('should have passed URL when launching with ignoreDefaultArgs: true', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({browserType, defaultBrowserOptions, server, tmpDir, toImpl}) => {
|
||||
const args = toImpl(browserType)._defaultArgs(defaultBrowserOptions, 'persistent', tmpDir, 0).filter(a => a !== 'about:blank');
|
||||
}, async ({browserType, defaultBrowserOptions, server, createUserDataDir, toImpl}) => {
|
||||
const userDataDir = await createUserDataDir();
|
||||
const args = toImpl(browserType)._defaultArgs(defaultBrowserOptions, 'persistent', userDataDir, 0).filter(a => a !== 'about:blank');
|
||||
const options = {
|
||||
...defaultBrowserOptions,
|
||||
args: [...args, server.EMPTY_PAGE],
|
||||
ignoreDefaultArgs: true,
|
||||
};
|
||||
const browserContext = await browserType.launchPersistentContext(tmpDir, options);
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, options);
|
||||
if (!browserContext.pages().length)
|
||||
await browserContext.waitForEvent('page');
|
||||
await browserContext.pages()[0].waitForLoadState();
|
||||
|
|
@ -186,18 +175,18 @@ it('should have passed URL when launching with ignoreDefaultArgs: true', (test,
|
|||
|
||||
it('should handle timeout', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({browserType, defaultBrowserOptions, tmpDir}) => {
|
||||
}, async ({browserType, defaultBrowserOptions, createUserDataDir}) => {
|
||||
const options = { ...defaultBrowserOptions, timeout: 5000, __testHookBeforeCreateBrowser: () => new Promise(f => setTimeout(f, 6000)) };
|
||||
const error = await browserType.launchPersistentContext(tmpDir, options).catch(e => e);
|
||||
const error = await browserType.launchPersistentContext(await createUserDataDir(), options).catch(e => e);
|
||||
expect(error.message).toContain(`browserType.launchPersistentContext: Timeout 5000ms exceeded.`);
|
||||
});
|
||||
|
||||
it('should handle exception', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({browserType, defaultBrowserOptions, tmpDir}) => {
|
||||
}, async ({browserType, defaultBrowserOptions, createUserDataDir}) => {
|
||||
const e = new Error('Dummy');
|
||||
const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; } };
|
||||
const error = await browserType.launchPersistentContext(tmpDir, options).catch(e => e);
|
||||
const error = await browserType.launchPersistentContext(await createUserDataDir(), options).catch(e => e);
|
||||
expect(error.message).toContain('Dummy');
|
||||
});
|
||||
|
||||
|
|
@ -240,7 +229,7 @@ it('should respect selectors', async ({playwright, launchPersistent}) => {
|
|||
return Array.from(root.querySelectorAll(selector));
|
||||
}
|
||||
});
|
||||
await utils.registerEngine(playwright, 'defaultContextCSS', defaultContextCSS);
|
||||
await playwright.selectors.register('defaultContextCSS', defaultContextCSS);
|
||||
|
||||
await page.setContent(`<div>hello</div>`);
|
||||
expect(await page.innerHTML('css=div')).toBe('hello');
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
|
||||
import utils from './utils';
|
||||
|
||||
it('should dispatch click event', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.dispatchEvent('button', 'click');
|
||||
|
|
@ -121,7 +119,7 @@ it('should be atomic', async ({playwright, page}) => {
|
|||
return result;
|
||||
}
|
||||
});
|
||||
await utils.registerEngine(playwright, 'dispatchEvent', createDummySelector);
|
||||
await playwright.selectors.register('dispatchEvent', createDummySelector);
|
||||
await page.setContent(`<div onclick="window._clicked=true">Hello</div>`);
|
||||
await page.dispatchEvent('dispatchEvent=div', 'click');
|
||||
expect(await page.evaluate(() => window['_clicked'])).toBe(true);
|
||||
|
|
|
|||
|
|
@ -62,28 +62,28 @@ it('should report downloads with acceptDownloads: true', async ({browser, server
|
|||
await page.close();
|
||||
});
|
||||
|
||||
it('should save to user-specified path', async ({tmpDir, browser, server}) => {
|
||||
it('should save to user-specified path', async ({testOutputDir, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(tmpDir, 'download.txt');
|
||||
const userPath = path.join(testOutputDir, 'download.txt');
|
||||
await download.saveAs(userPath);
|
||||
expect(fs.existsSync(userPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(userPath).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should save to user-specified path without updating original path', async ({tmpDir, browser, server}) => {
|
||||
it('should save to user-specified path without updating original path', async ({testOutputDir, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(tmpDir, 'download.txt');
|
||||
const userPath = path.join(testOutputDir, 'download.txt');
|
||||
await download.saveAs(userPath);
|
||||
expect(fs.existsSync(userPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(userPath).toString()).toBe('Hello world');
|
||||
|
|
@ -94,50 +94,51 @@ it('should save to user-specified path without updating original path', async ({
|
|||
await page.close();
|
||||
});
|
||||
|
||||
it('should save to two different paths with multiple saveAs calls', async ({tmpDir, browser, server}) => {
|
||||
it('should save to two different paths with multiple saveAs calls', async ({testOutputDir, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(tmpDir, 'download.txt');
|
||||
const userPath = path.join(testOutputDir, 'download.txt');
|
||||
await download.saveAs(userPath);
|
||||
expect(fs.existsSync(userPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(userPath).toString()).toBe('Hello world');
|
||||
|
||||
const anotherUserPath = path.join(tmpDir, 'download (2).txt');
|
||||
const anotherUserPath = path.join(testOutputDir, 'download (2).txt');
|
||||
await download.saveAs(anotherUserPath);
|
||||
expect(fs.existsSync(anotherUserPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(anotherUserPath).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should save to overwritten filepath', async ({tmpDir, browser, server}) => {
|
||||
it('should save to overwritten filepath', async ({testOutputDir, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(tmpDir, 'download.txt');
|
||||
const dir = path.join(testOutputDir, 'downloads');
|
||||
const userPath = path.join(dir, 'download.txt');
|
||||
await download.saveAs(userPath);
|
||||
expect((await util.promisify(fs.readdir)(tmpDir)).length).toBe(1);
|
||||
expect((await util.promisify(fs.readdir)(dir)).length).toBe(1);
|
||||
await download.saveAs(userPath);
|
||||
expect((await util.promisify(fs.readdir)(tmpDir)).length).toBe(1);
|
||||
expect((await util.promisify(fs.readdir)(dir)).length).toBe(1);
|
||||
expect(fs.existsSync(userPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(userPath).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should create subdirectories when saving to non-existent user-specified path', async ({tmpDir, browser, server}) => {
|
||||
it('should create subdirectories when saving to non-existent user-specified path', async ({testOutputDir, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const nestedPath = path.join(tmpDir, 'these', 'are', 'directories', 'download.txt');
|
||||
const nestedPath = path.join(testOutputDir, 'these', 'are', 'directories', 'download.txt');
|
||||
await download.saveAs(nestedPath);
|
||||
expect(fs.existsSync(nestedPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(nestedPath).toString()).toBe('Hello world');
|
||||
|
|
@ -146,7 +147,7 @@ it('should create subdirectories when saving to non-existent user-specified path
|
|||
|
||||
it('should save when connected remotely', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({tmpDir, server, browserType, remoteServer}) => {
|
||||
}, async ({testOutputDir, server, browserType, remoteServer}) => {
|
||||
const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() });
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
|
|
@ -154,7 +155,7 @@ it('should save when connected remotely', (test, parameters) => {
|
|||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const nestedPath = path.join(tmpDir, 'these', 'are', 'directories', 'download.txt');
|
||||
const nestedPath = path.join(testOutputDir, 'these', 'are', 'directories', 'download.txt');
|
||||
await download.saveAs(nestedPath);
|
||||
expect(fs.existsSync(nestedPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(nestedPath).toString()).toBe('Hello world');
|
||||
|
|
@ -163,27 +164,27 @@ it('should save when connected remotely', (test, parameters) => {
|
|||
await browser.close();
|
||||
});
|
||||
|
||||
it('should error when saving with downloads disabled', async ({tmpDir, browser, server}) => {
|
||||
it('should error when saving with downloads disabled', async ({testOutputDir, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: false });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(tmpDir, 'download.txt');
|
||||
const userPath = path.join(testOutputDir, 'download.txt');
|
||||
const { message } = await download.saveAs(userPath).catch(e => e);
|
||||
expect(message).toContain('Pass { acceptDownloads: true } when you are creating your browser context');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should error when saving after deletion', async ({tmpDir, browser, server}) => {
|
||||
it('should error when saving after deletion', async ({testOutputDir, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(tmpDir, 'download.txt');
|
||||
const userPath = path.join(testOutputDir, 'download.txt');
|
||||
await download.delete();
|
||||
const { message } = await download.saveAs(userPath).catch(e => e);
|
||||
expect(message).toContain('Download already deleted. Save before deleting.');
|
||||
|
|
@ -192,7 +193,7 @@ it('should error when saving after deletion', async ({tmpDir, browser, server})
|
|||
|
||||
it('should error when saving after deletion when connected remotely', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({tmpDir, server, browserType, remoteServer}) => {
|
||||
}, async ({testOutputDir, server, browserType, remoteServer}) => {
|
||||
const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() });
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
|
|
@ -200,7 +201,7 @@ it('should error when saving after deletion when connected remotely', (test, par
|
|||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(tmpDir, 'download.txt');
|
||||
const userPath = path.join(testOutputDir, 'download.txt');
|
||||
await download.delete();
|
||||
const { message } = await download.saveAs(userPath).catch(e => e);
|
||||
expect(message).toContain('Download already deleted. Save before deleting.');
|
||||
|
|
|
|||
|
|
@ -16,10 +16,7 @@
|
|||
|
||||
import { playwrightFixtures } from './playwright.fixtures';
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import {mkdtempAsync, removeFolderAsync} from './utils';
|
||||
import type { Browser, BrowserContext } from '..';
|
||||
|
||||
type TestState = {
|
||||
|
|
@ -29,7 +26,7 @@ type TestState = {
|
|||
const fixtures = playwrightFixtures.declareTestFixtures<TestState>();
|
||||
const { it, expect, defineTestFixture } = fixtures;
|
||||
|
||||
defineTestFixture('downloadsBrowser', async ({server, browserType, defaultBrowserOptions, tmpDir}, test) => {
|
||||
defineTestFixture('downloadsBrowser', async ({server, browserType, defaultBrowserOptions, testOutputDir}, test) => {
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
|
||||
|
|
@ -37,35 +34,30 @@ defineTestFixture('downloadsBrowser', async ({server, browserType, defaultBrowse
|
|||
});
|
||||
const browser = await browserType.launch({
|
||||
...defaultBrowserOptions,
|
||||
downloadsPath: tmpDir,
|
||||
downloadsPath: testOutputDir,
|
||||
});
|
||||
await test(browser);
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
defineTestFixture('persistentDownloadsContext', async ({server, browserType, defaultBrowserOptions, tmpDir}, test) => {
|
||||
const userDataDir = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-'));
|
||||
defineTestFixture('persistentDownloadsContext', async ({server, launchPersistent, testOutputDir}, test) => {
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
const context = await browserType.launchPersistentContext(
|
||||
userDataDir,
|
||||
const { context, page } = await launchPersistent(
|
||||
{
|
||||
...defaultBrowserOptions,
|
||||
downloadsPath: tmpDir,
|
||||
downloadsPath: testOutputDir,
|
||||
acceptDownloads: true
|
||||
}
|
||||
);
|
||||
const page = context.pages()[0];
|
||||
page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
await test(context);
|
||||
await context.close();
|
||||
await removeFolderAsync(userDataDir);
|
||||
});
|
||||
|
||||
it('should keep downloadsPath folder', async ({downloadsBrowser, tmpDir, server}) => {
|
||||
it('should keep downloadsPath folder', async ({downloadsBrowser, testOutputDir, server}) => {
|
||||
const page = await downloadsBrowser.newPage();
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
|
|
@ -77,7 +69,7 @@ it('should keep downloadsPath folder', async ({downloadsBrowser, tmpDir, server}
|
|||
await download.path().catch(e => void 0);
|
||||
await page.close();
|
||||
await downloadsBrowser.close();
|
||||
expect(fs.existsSync(tmpDir)).toBeTruthy();
|
||||
expect(fs.existsSync(testOutputDir)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should delete downloads when context closes', async ({downloadsBrowser, server}) => {
|
||||
|
|
@ -94,7 +86,7 @@ it('should delete downloads when context closes', async ({downloadsBrowser, serv
|
|||
|
||||
});
|
||||
|
||||
it('should report downloads in downloadsPath folder', async ({downloadsBrowser, tmpDir, server}) => {
|
||||
it('should report downloads in downloadsPath folder', async ({downloadsBrowser, testOutputDir, server}) => {
|
||||
const page = await downloadsBrowser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
|
|
@ -102,11 +94,11 @@ it('should report downloads in downloadsPath folder', async ({downloadsBrowser,
|
|||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(path.startsWith(tmpDir)).toBeTruthy();
|
||||
expect(path.startsWith(testOutputDir)).toBeTruthy();
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should accept downloads', async ({persistentDownloadsContext, tmpDir, server}) => {
|
||||
it('should accept downloads', async ({persistentDownloadsContext, testOutputDir, server}) => {
|
||||
const page = persistentDownloadsContext.pages()[0];
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
|
|
@ -115,7 +107,7 @@ it('should accept downloads', async ({persistentDownloadsContext, tmpDir, server
|
|||
expect(download.url()).toBe(`${server.PREFIX}/download`);
|
||||
expect(download.suggestedFilename()).toBe(`file.txt`);
|
||||
const path = await download.path();
|
||||
expect(path.startsWith(tmpDir)).toBeTruthy();
|
||||
expect(path.startsWith(testOutputDir)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not delete downloads when the context closes', async ({persistentDownloadsContext}) => {
|
||||
|
|
|
|||
|
|
@ -15,13 +15,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
import utils from './utils';
|
||||
import { it, expect, attachFrame } from './playwright.fixtures';
|
||||
|
||||
it('should work', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const elementHandle = await page.$('#frame1');
|
||||
const frame = await elementHandle.contentFrame();
|
||||
expect(frame).toBe(page.frames()[1]);
|
||||
|
|
@ -29,7 +27,7 @@ it('should work', async ({ page, server }) => {
|
|||
|
||||
it('should work for cross-process iframes', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
await attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const elementHandle = await page.$('#frame1');
|
||||
const frame = await elementHandle.contentFrame();
|
||||
expect(frame).toBe(page.frames()[1]);
|
||||
|
|
@ -37,7 +35,7 @@ it('should work for cross-process iframes', async ({ page, server }) => {
|
|||
|
||||
it('should work for cross-frame evaluations', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => window.top.document.querySelector('#frame1'));
|
||||
expect(await elementHandle.contentFrame()).toBe(frame);
|
||||
|
|
@ -45,7 +43,7 @@ it('should work for cross-frame evaluations', async ({ page, server }) => {
|
|||
|
||||
it('should return null for non-iframes', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.body);
|
||||
expect(await elementHandle.contentFrame()).toBe(null);
|
||||
|
|
@ -53,7 +51,7 @@ it('should return null for non-iframes', async ({ page, server }) => {
|
|||
|
||||
it('should return null for document.documentElement', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.documentElement);
|
||||
expect(await elementHandle.contentFrame()).toBe(null);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
*/
|
||||
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
import utils from './utils';
|
||||
|
||||
it('should have a nice preview', async ({ page, server }) => {
|
||||
await page.goto(`${server.PREFIX}/dom.html`);
|
||||
|
|
@ -86,7 +85,7 @@ it('textContent should be atomic', async ({ playwright, page }) => {
|
|||
return result;
|
||||
}
|
||||
});
|
||||
await utils.registerEngine(playwright, 'textContent', createDummySelector);
|
||||
await playwright.selectors.register('textContent', createDummySelector);
|
||||
await page.setContent(`<div>Hello</div>`);
|
||||
const tc = await page.textContent('textContent=div');
|
||||
expect(tc).toBe('Hello');
|
||||
|
|
@ -109,7 +108,7 @@ it('innerText should be atomic', async ({ playwright, page }) => {
|
|||
return result;
|
||||
}
|
||||
});
|
||||
await utils.registerEngine(playwright, 'innerText', createDummySelector);
|
||||
await playwright.selectors.register('innerText', createDummySelector);
|
||||
await page.setContent(`<div>Hello</div>`);
|
||||
const tc = await page.innerText('innerText=div');
|
||||
expect(tc).toBe('Hello');
|
||||
|
|
@ -132,7 +131,7 @@ it('innerHTML should be atomic', async ({ playwright, page }) => {
|
|||
return result;
|
||||
}
|
||||
});
|
||||
await utils.registerEngine(playwright, 'innerHTML', createDummySelector);
|
||||
await playwright.selectors.register('innerHTML', createDummySelector);
|
||||
await page.setContent(`<div>Hello<span>world</span></div>`);
|
||||
const tc = await page.innerHTML('innerHTML=div');
|
||||
expect(tc).toBe('Hello<span>world</span>');
|
||||
|
|
@ -155,7 +154,7 @@ it('getAttribute should be atomic', async ({ playwright, page }) => {
|
|||
return result;
|
||||
}
|
||||
});
|
||||
await utils.registerEngine(playwright, 'getAttribute', createDummySelector);
|
||||
await playwright.selectors.register('getAttribute', createDummySelector);
|
||||
await page.setContent(`<div foo=hello></div>`);
|
||||
const tc = await page.getAttribute('getAttribute=div', 'foo');
|
||||
expect(tc).toBe('hello');
|
||||
|
|
|
|||
|
|
@ -15,12 +15,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import utils from './utils';
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect, options, attachFrame } from './playwright.fixtures';
|
||||
|
||||
it('should work', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.body);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
|
|
@ -28,7 +27,7 @@ it('should work', async ({ page, server }) => {
|
|||
|
||||
it('should work for cross-process iframes', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
await attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.body);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
|
|
@ -38,7 +37,7 @@ it('should work for document', (test, parameters) => {
|
|||
test.flaky(options.WIN(parameters) && options.WEBKIT(parameters));
|
||||
}, async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
|
|
@ -46,7 +45,7 @@ it('should work for document', (test, parameters) => {
|
|||
|
||||
it('should work for iframe elements', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
const elementHandle = await frame.evaluateHandle(() => document.querySelector('#frame1'));
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
|
|
@ -54,7 +53,7 @@ it('should work for iframe elements', async ({ page, server }) => {
|
|||
|
||||
it('should work for cross-frame evaluations', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
const elementHandle = await frame.evaluateHandle(() => document.querySelector('iframe').contentWindow.document.body);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame.childFrames()[0]);
|
||||
|
|
|
|||
|
|
@ -15,9 +15,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, describe, options } from './playwright.fixtures';
|
||||
import { it, expect, describe, options, verifyViewport } from './playwright.fixtures';
|
||||
|
||||
import utils from './utils';
|
||||
import {PNG} from 'pngjs';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
|
@ -77,7 +76,7 @@ describe('element screenshot', (suite, parameters) => {
|
|||
const screenshots = await Promise.all(promises);
|
||||
expect(screenshots[2]).toMatchImage(golden('screenshot-element-larger-than-viewport.png'));
|
||||
|
||||
await utils.verifyViewport(page, 500, 500);
|
||||
await verifyViewport(page, 500, 500);
|
||||
});
|
||||
|
||||
it('should capture full element when larger than viewport', async ({page, golden}) => {
|
||||
|
|
@ -104,7 +103,7 @@ describe('element screenshot', (suite, parameters) => {
|
|||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toMatchImage(golden('screenshot-element-larger-than-viewport.png'));
|
||||
|
||||
await utils.verifyViewport(page, 500, 500);
|
||||
await verifyViewport(page, 500, 500);
|
||||
});
|
||||
|
||||
it('should scroll element into view', async ({page, golden}) => {
|
||||
|
|
@ -282,10 +281,10 @@ describe('element screenshot', (suite, parameters) => {
|
|||
it('should restore default viewport after fullPage screenshot', async ({ browser }) => {
|
||||
const context = await browser.newContext({ viewport: { width: 456, height: 789 } });
|
||||
const page = await context.newPage();
|
||||
await utils.verifyViewport(page, 456, 789);
|
||||
await verifyViewport(page, 456, 789);
|
||||
const screenshot = await page.screenshot({ fullPage: true });
|
||||
expect(screenshot).toBeInstanceOf(Buffer);
|
||||
await utils.verifyViewport(page, 456, 789);
|
||||
await verifyViewport(page, 456, 789);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
|
|
@ -298,7 +297,7 @@ describe('element screenshot', (suite, parameters) => {
|
|||
const __testHookBeforeScreenshot = () => { throw new Error('oh my'); };
|
||||
const error = await page.screenshot({ fullPage: true, __testHookBeforeScreenshot } as any).catch(e => e);
|
||||
expect(error.message).toContain('oh my');
|
||||
await utils.verifyViewport(page, 350, 360);
|
||||
await verifyViewport(page, 350, 360);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
|
|
@ -311,10 +310,10 @@ describe('element screenshot', (suite, parameters) => {
|
|||
const __testHookAfterScreenshot = () => new Promise(f => setTimeout(f, 5000));
|
||||
const error = await page.screenshot({ fullPage: true, __testHookAfterScreenshot, timeout: 3000 } as any).catch(e => e);
|
||||
expect(error.message).toContain('page.screenshot: Timeout 3000ms exceeded');
|
||||
await utils.verifyViewport(page, 350, 360);
|
||||
await verifyViewport(page, 350, 360);
|
||||
await page.setViewportSize({ width: 400, height: 400 });
|
||||
await page.waitForTimeout(3000); // Give it some time to wrongly restore previous viewport.
|
||||
await utils.verifyViewport(page, 400, 400);
|
||||
await verifyViewport(page, 400, 400);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
|
|
@ -358,7 +357,7 @@ describe('element screenshot', (suite, parameters) => {
|
|||
const __testHookBeforeScreenshot = () => { throw new Error('oh my'); };
|
||||
const error = await elementHandle.screenshot({ __testHookBeforeScreenshot } as any).catch(e => e);
|
||||
expect(error.message).toContain('oh my');
|
||||
await utils.verifyViewport(page, 350, 360);
|
||||
await verifyViewport(page, 350, 360);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
|
|
@ -384,12 +383,12 @@ describe('element screenshot', (suite, parameters) => {
|
|||
expect(screenshot).toBeInstanceOf(Buffer);
|
||||
});
|
||||
|
||||
it('path option should create subdirectories', async ({page, server, golden, tmpDir}) => {
|
||||
it('path option should create subdirectories', async ({page, server, golden, testOutputDir}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await page.evaluate(() => window.scrollBy(50, 100));
|
||||
const elementHandle = await page.$('.box:nth-of-type(3)');
|
||||
const outputPath = path.join(tmpDir, 'these', 'are', 'directories', 'screenshot.png');
|
||||
const outputPath = path.join(testOutputDir, 'these', 'are', 'directories', 'screenshot.png');
|
||||
await elementHandle.screenshot({path: outputPath});
|
||||
expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('screenshot-element-bounding-box.png'));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import utils from './utils';
|
||||
import { it, expect, options, attachFrame } from './playwright.fixtures';
|
||||
|
||||
it('should think that it is focused by default', async ({page}) => {
|
||||
expect(await page.evaluate('document.hasFocus()')).toBe(true);
|
||||
|
|
@ -127,8 +126,8 @@ it('should not affect screenshots', (test, parameters) => {
|
|||
it('should change focused iframe', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [frame1, frame2] = await Promise.all([
|
||||
utils.attachFrame(page, 'frame1', server.PREFIX + '/input/textarea.html'),
|
||||
utils.attachFrame(page, 'frame2', server.PREFIX + '/input/textarea.html'),
|
||||
attachFrame(page, 'frame1', server.PREFIX + '/input/textarea.html'),
|
||||
attachFrame(page, 'frame2', server.PREFIX + '/input/textarea.html'),
|
||||
]);
|
||||
function logger() {
|
||||
self['_events'] = [];
|
||||
|
|
|
|||
|
|
@ -15,14 +15,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect, options, attachFrame, detachFrame } from './playwright.fixtures';
|
||||
|
||||
import utils from './utils';
|
||||
import type { Frame } from '../src/client/frame';
|
||||
|
||||
it('should have different execution contexts', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
expect(page.frames().length).toBe(2);
|
||||
await page.frames()[0].evaluate(() => window['FOO'] = 'foo');
|
||||
await page.frames()[1].evaluate(() => window['FOO'] = 'bar');
|
||||
|
|
@ -99,15 +98,15 @@ it('should allow cross-frame element handles', async ({ page, server }) => {
|
|||
|
||||
it('should not allow cross-frame element handles when frames do not script each other', async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const frame = await attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const bodyHandle = await frame.$('body');
|
||||
const error = await page.evaluate(body => body.innerHTML, bodyHandle).catch(e => e);
|
||||
expect(error.message).toContain('Unable to adopt element handle from a different document');
|
||||
});
|
||||
|
||||
it('should throw for detached frames', async ({page, server}) => {
|
||||
const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await utils.detachFrame(page, 'frame1');
|
||||
const frame1 = await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await detachFrame(page, 'frame1');
|
||||
let error = null;
|
||||
await frame1.evaluate(() => 7 * 8).catch(e => error = e);
|
||||
expect(error.message).toContain('Execution Context is not available in detached frame');
|
||||
|
|
@ -115,7 +114,7 @@ it('should throw for detached frames', async ({page, server}) => {
|
|||
|
||||
it('should be isolated between frames', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
expect(page.frames().length).toBe(2);
|
||||
const [frame1, frame2] = page.frames();
|
||||
expect(frame1 !== frame2).toBeTruthy();
|
||||
|
|
|
|||
|
|
@ -15,15 +15,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
import utils from './utils';
|
||||
import { it, expect, attachFrame } from './playwright.fixtures';
|
||||
|
||||
it('should work', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
const frame3 = await utils.attachFrame(page, 'frame3', server.EMPTY_PAGE);
|
||||
const frame1 = await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
const frame3 = await attachFrame(page, 'frame3', server.EMPTY_PAGE);
|
||||
const frame1handle1 = await page.$('#frame1');
|
||||
const frame1handle2 = await frame1.frameElement();
|
||||
const frame3handle1 = await page.$('#frame3');
|
||||
|
|
@ -35,7 +33,7 @@ it('should work', async ({page, server}) => {
|
|||
|
||||
it('should work with contentFrame', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const handle = await frame.frameElement();
|
||||
const contentFrame = await handle.contentFrame();
|
||||
expect(contentFrame).toBe(frame);
|
||||
|
|
@ -43,7 +41,7 @@ it('should work with contentFrame', async ({page, server}) => {
|
|||
|
||||
it('should throw when detached', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame1 = await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await page.$eval('#frame1', e => e.remove());
|
||||
const error = await frame1.frameElement().catch(e => e);
|
||||
expect(error.message).toContain('Frame has been detached.');
|
||||
|
|
|
|||
|
|
@ -15,9 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
import utils from './utils';
|
||||
import { it, expect, attachFrame } from './playwright.fixtures';
|
||||
|
||||
it('should navigate subframes', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
|
|
@ -53,9 +51,9 @@ it('should return matching responses', async ({page, server}) => {
|
|||
await page.goto(server.EMPTY_PAGE);
|
||||
// Attach three frames.
|
||||
const frames = [
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE),
|
||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE),
|
||||
await utils.attachFrame(page, 'frame3', server.EMPTY_PAGE),
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE),
|
||||
await attachFrame(page, 'frame2', server.EMPTY_PAGE),
|
||||
await attachFrame(page, 'frame3', server.EMPTY_PAGE),
|
||||
];
|
||||
const serverResponses = [];
|
||||
server.setRoute('/0.html', (req, res) => serverResponses.push(res));
|
||||
|
|
|
|||
|
|
@ -15,12 +15,28 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import utils from './utils';
|
||||
import { it, expect, options, attachFrame, detachFrame } from './playwright.fixtures';
|
||||
import type { Frame } from '../index';
|
||||
|
||||
function dumpFrames(frame: Frame, indentation: string = ''): string[] {
|
||||
let description = frame.url().replace(/:\d{4}\//, ':<PORT>/');
|
||||
if (frame.name())
|
||||
description += ' (' + frame.name() + ')';
|
||||
const result = [indentation + description];
|
||||
const childFrames = frame.childFrames();
|
||||
childFrames.sort((a, b) => {
|
||||
if (a.url() !== b.url())
|
||||
return a.url() < b.url() ? -1 : 1;
|
||||
return a.name() < b.name() ? -1 : 1;
|
||||
});
|
||||
for (const child of childFrames)
|
||||
result.push(...dumpFrames(child, ' ' + indentation));
|
||||
return result;
|
||||
}
|
||||
|
||||
it('should handle nested frames', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/frames/nested-frames.html');
|
||||
expect(utils.dumpFrames(page.mainFrame())).toEqual([
|
||||
expect(dumpFrames(page.mainFrame())).toEqual([
|
||||
'http://localhost:<PORT>/frames/nested-frames.html',
|
||||
' http://localhost:<PORT>/frames/frame.html (aframe)',
|
||||
' http://localhost:<PORT>/frames/two-frames.html (2frames)',
|
||||
|
|
@ -34,7 +50,7 @@ it('should send events when frames are manipulated dynamically', async ({page, s
|
|||
// validate frameattached events
|
||||
const attachedFrames = [];
|
||||
page.on('frameattached', frame => attachedFrames.push(frame));
|
||||
await utils.attachFrame(page, 'frame1', './assets/frame.html');
|
||||
await attachFrame(page, 'frame1', './assets/frame.html');
|
||||
expect(attachedFrames.length).toBe(1);
|
||||
expect(attachedFrames[0].url()).toContain('/assets/frame.html');
|
||||
|
||||
|
|
@ -52,7 +68,7 @@ it('should send events when frames are manipulated dynamically', async ({page, s
|
|||
// validate framedetached events
|
||||
const detachedFrames = [];
|
||||
page.on('framedetached', frame => detachedFrames.push(frame));
|
||||
await utils.detachFrame(page, 'frame1');
|
||||
await detachFrame(page, 'frame1');
|
||||
expect(detachedFrames.length).toBe(1);
|
||||
expect(detachedFrames[0].isDetached()).toBe(true);
|
||||
});
|
||||
|
|
@ -136,7 +152,7 @@ it('should report frame from-inside shadow DOM', async ({page, server}) => {
|
|||
});
|
||||
|
||||
it('should report frame.name()', async ({page, server}) => {
|
||||
await utils.attachFrame(page, 'theFrameId', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'theFrameId', server.EMPTY_PAGE);
|
||||
await page.evaluate(url => {
|
||||
const frame = document.createElement('iframe');
|
||||
frame.name = 'theFrameName';
|
||||
|
|
@ -150,15 +166,15 @@ it('should report frame.name()', async ({page, server}) => {
|
|||
});
|
||||
|
||||
it('should report frame.parent()', async ({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
expect(page.frames()[0].parentFrame()).toBe(null);
|
||||
expect(page.frames()[1].parentFrame()).toBe(page.mainFrame());
|
||||
expect(page.frames()[2].parentFrame()).toBe(page.mainFrame());
|
||||
});
|
||||
|
||||
it('should report different frame instance when frame re-attaches', async ({page, server}) => {
|
||||
const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame1 = await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
window['frame'] = document.querySelector('#frame1');
|
||||
window['frame'].remove();
|
||||
|
|
|
|||
|
|
@ -16,11 +16,8 @@
|
|||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
|
||||
import utils from './utils';
|
||||
const { makeUserDataDir, removeUserDataDir } = utils;
|
||||
|
||||
it('should have default url when launching browser', async ({browserType, defaultBrowserOptions, tmpDir}) => {
|
||||
const browserContext = await browserType.launchPersistentContext(tmpDir, {...defaultBrowserOptions, headless: false });
|
||||
it('should have default url when launching browser', async ({browserType, defaultBrowserOptions, createUserDataDir}) => {
|
||||
const browserContext = await browserType.launchPersistentContext(await createUserDataDir(), {...defaultBrowserOptions, headless: false });
|
||||
const urls = browserContext.pages().map(page => page.url());
|
||||
expect(urls).toEqual(['about:blank']);
|
||||
await browserContext.close();
|
||||
|
|
@ -30,9 +27,9 @@ it('headless should be able to read cookies written by headful', (test, paramete
|
|||
test.fail(options.WIN(parameters) && options.CHROMIUM(parameters));
|
||||
test.flaky(options.FIREFOX(parameters));
|
||||
test.slow();
|
||||
}, async ({browserType, defaultBrowserOptions, server}) => {
|
||||
}, async ({browserType, defaultBrowserOptions, server, createUserDataDir}) => {
|
||||
// see https://github.com/microsoft/playwright/issues/717
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const userDataDir = await createUserDataDir();
|
||||
// Write a cookie in headful chrome
|
||||
const headfulContext = await browserType.launchPersistentContext(userDataDir, {...defaultBrowserOptions, headless: false});
|
||||
const headfulPage = await headfulContext.newPage();
|
||||
|
|
@ -45,15 +42,13 @@ it('headless should be able to read cookies written by headful', (test, paramete
|
|||
await headlessPage.goto(server.EMPTY_PAGE);
|
||||
const cookie = await headlessPage.evaluate(() => document.cookie);
|
||||
await headlessContext.close();
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
expect(cookie).toBe('foo=true');
|
||||
});
|
||||
|
||||
it('should close browser with beforeunload page', (test, parameters) => {
|
||||
test.slow();
|
||||
}, async ({browserType, defaultBrowserOptions, server, tmpDir}) => {
|
||||
const browserContext = await browserType.launchPersistentContext(tmpDir, {...defaultBrowserOptions, headless: false});
|
||||
}, async ({browserType, defaultBrowserOptions, server, createUserDataDir}) => {
|
||||
const browserContext = await browserType.launchPersistentContext(await createUserDataDir(), {...defaultBrowserOptions, headless: false});
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.PREFIX + '/beforeunload.html');
|
||||
// We have to interact with a page so that 'beforeunload' handlers
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import utils from './utils';
|
||||
import { it, expect, options, attachFrame } from './playwright.fixtures';
|
||||
|
||||
it('should type into a textarea', async ({page}) => {
|
||||
await page.evaluate(() => {
|
||||
|
|
@ -305,7 +304,7 @@ it('should type emoji', async ({page, server}) => {
|
|||
|
||||
it('should type emoji into an iframe', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'emoji-test', server.PREFIX + '/input/textarea.html');
|
||||
await attachFrame(page, 'emoji-test', server.PREFIX + '/input/textarea.html');
|
||||
const frame = page.frames()[1];
|
||||
const textarea = await frame.$('textarea');
|
||||
await textarea.type('👹 Tokyo street Japan 🇯🇵');
|
||||
|
|
|
|||
|
|
@ -16,16 +16,14 @@
|
|||
*/
|
||||
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
import path from 'path';
|
||||
import utils from './utils';
|
||||
|
||||
it('should require top-level Errors', async ({}) => {
|
||||
const Errors = require(path.join(utils.projectRoot(), '/lib/utils/errors.js'));
|
||||
const Errors = require('../lib/utils/errors.js');
|
||||
expect(String(Errors.TimeoutError)).toContain('TimeoutError');
|
||||
});
|
||||
|
||||
it('should require top-level DeviceDescriptors', async ({playwright}) => {
|
||||
const Devices = require(path.join(utils.projectRoot(), '/lib/server/deviceDescriptors.js')).DeviceDescriptors;
|
||||
const Devices = require('../lib/server/deviceDescriptors.js').DeviceDescriptors;
|
||||
expect(Devices['iPhone 6']).toBeTruthy();
|
||||
expect(Devices['iPhone 6']).toEqual(playwright.devices['iPhone 6']);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import utils from './utils';
|
||||
import { it, expect, options, attachFrame } from './playwright.fixtures';
|
||||
|
||||
it('should work for main frame navigation request', async ({page, server}) => {
|
||||
const requests = [];
|
||||
|
|
@ -30,7 +29,7 @@ it('should work for subframe navigation request', async ({page, server}) => {
|
|||
await page.goto(server.EMPTY_PAGE);
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
expect(requests.length).toBe(1);
|
||||
expect(requests[0].frame()).toBe(page.frames()[1]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
import utils from './utils';
|
||||
import { it, expect, attachFrame } from './playwright.fixtures';
|
||||
|
||||
it('should emulate type', async ({page, server}) => {
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
|
||||
|
|
@ -110,7 +109,7 @@ it('should work in popup', async ({browser, server}) => {
|
|||
it('should work in cross-process iframe', async ({browser, server}) => {
|
||||
const page = await browser.newPage({ colorScheme: 'dark' });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
await attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const frame = page.frames()[1];
|
||||
expect(await frame.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
await page.close();
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
import utils from './utils';
|
||||
import { it, expect, attachFrame } from './playwright.fixtures';
|
||||
|
||||
it('should fire for navigation requests', async ({page, server}) => {
|
||||
const requests = [];
|
||||
|
|
@ -29,7 +28,7 @@ it('should fire for iframes', async ({page, server}) => {
|
|||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
expect(requests.length).toBe(2);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
import utils from './utils';
|
||||
import path from 'path';
|
||||
import url from 'url';
|
||||
|
||||
|
|
@ -174,7 +173,7 @@ it('should fail when navigating to bad url', async ({page, isChromium, isWebKit}
|
|||
expect(error.message).toContain('Invalid url');
|
||||
});
|
||||
|
||||
it('should fail when navigating to bad SSL', async ({page, httpsServer, browserName}) => {
|
||||
it('should fail when navigating to bad SSL', async ({page, httpsServer, expectedSSLError}) => {
|
||||
// Make sure that network events do not emit 'undefined'.
|
||||
// @see https://crbug.com/750469
|
||||
page.on('request', request => expect(request).toBeTruthy());
|
||||
|
|
@ -182,15 +181,15 @@ it('should fail when navigating to bad SSL', async ({page, httpsServer, browserN
|
|||
page.on('requestfailed', request => expect(request).toBeTruthy());
|
||||
let error = null;
|
||||
await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e);
|
||||
utils.expectSSLError(browserName, error.message);
|
||||
expect(error.message).toContain(expectedSSLError);
|
||||
});
|
||||
|
||||
it('should fail when navigating to bad SSL after redirects', async ({page, server, httpsServer, browserName}) => {
|
||||
it('should fail when navigating to bad SSL after redirects', async ({page, server, httpsServer, expectedSSLError}) => {
|
||||
server.setRedirect('/redirect/1.html', '/redirect/2.html');
|
||||
server.setRedirect('/redirect/2.html', '/empty.html');
|
||||
let error = null;
|
||||
await page.goto(httpsServer.PREFIX + '/redirect/1.html').catch(e => error = e);
|
||||
utils.expectSSLError(browserName, error.message);
|
||||
expect(error.message).toContain(expectedSSLError);
|
||||
});
|
||||
|
||||
it('should not crash when navigating to bad SSL after a cross origin navigation', async ({page, server, httpsServer}) => {
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, describe, options } from './playwright.fixtures';
|
||||
import utils from './utils';
|
||||
import { it, expect, describe, options, verifyViewport } from './playwright.fixtures';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
|
|
@ -122,7 +121,7 @@ describe('page screenshot', (suite, parameters) => {
|
|||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshot = await page.screenshot({ fullPage: true });
|
||||
expect(screenshot).toBeInstanceOf(Buffer);
|
||||
await utils.verifyViewport(page, 500, 500);
|
||||
await verifyViewport(page, 500, 500);
|
||||
});
|
||||
|
||||
it('should run in parallel in multiple pages', async ({server, context, golden}) => {
|
||||
|
|
@ -262,32 +261,32 @@ describe('page screenshot', (suite, parameters) => {
|
|||
expect(await page.screenshot()).toMatchImage(golden('screenshot-iframe.png'));
|
||||
});
|
||||
|
||||
it('path option should work', async ({page, server, golden, tmpDir}) => {
|
||||
it('path option should work', async ({page, server, golden, testOutputDir}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const outputPath = path.join(tmpDir, 'screenshot.png');
|
||||
const outputPath = path.join(testOutputDir, 'screenshot.png');
|
||||
await page.screenshot({path: outputPath});
|
||||
expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('screenshot-sanity.png'));
|
||||
});
|
||||
|
||||
it('path option should create subdirectories', async ({page, server, golden, tmpDir}) => {
|
||||
it('path option should create subdirectories', async ({page, server, golden, testOutputDir}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const outputPath = path.join(tmpDir, 'these', 'are', 'directories', 'screenshot.png');
|
||||
const outputPath = path.join(testOutputDir, 'these', 'are', 'directories', 'screenshot.png');
|
||||
await page.screenshot({path: outputPath});
|
||||
expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('screenshot-sanity.png'));
|
||||
});
|
||||
|
||||
it('path option should detect jpeg', async ({page, server, golden, tmpDir}) => {
|
||||
it('path option should detect jpeg', async ({page, server, golden, testOutputDir}) => {
|
||||
await page.setViewportSize({ width: 100, height: 100 });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const outputPath = path.join(tmpDir, 'screenshot.jpg');
|
||||
const outputPath = path.join(testOutputDir, 'screenshot.jpg');
|
||||
const screenshot = await page.screenshot({omitBackground: true, path: outputPath});
|
||||
expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('white.jpg'));
|
||||
expect(screenshot).toMatchImage(golden('white.jpg'));
|
||||
});
|
||||
|
||||
it('path option should throw for unsupported mime type', async ({page, server, golden, tmpDir}) => {
|
||||
it('path option should throw for unsupported mime type', async ({page}) => {
|
||||
const error = await page.screenshot({ path: 'file.txt' }).catch(e => e);
|
||||
expect(error.message).toContain('path: unsupported mime type "text/plain"');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,9 +16,7 @@
|
|||
*/
|
||||
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
import utils from './utils';
|
||||
import type { Frame } from '..';
|
||||
import type { Frame } from '../index';
|
||||
|
||||
it('should work', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
|
@ -72,14 +70,14 @@ it('should work with clicking on anchor links', async ({page, server}) => {
|
|||
expect(page.url()).toBe(server.EMPTY_PAGE + '#foobar');
|
||||
});
|
||||
|
||||
it('should work with clicking on links which do not commit navigation', async ({page, server, httpsServer, browserName}) => {
|
||||
it('should work with clicking on links which do not commit navigation', async ({page, server, httpsServer, expectedSSLError}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<a href='${httpsServer.EMPTY_PAGE}'>foobar</a>`);
|
||||
const [error] = await Promise.all([
|
||||
page.waitForNavigation().catch(e => e),
|
||||
page.click('a'),
|
||||
]);
|
||||
utils.expectSSLError(browserName, error.message);
|
||||
expect(error.message).toContain(expectedSSLError);
|
||||
});
|
||||
|
||||
it('should work with history.pushState()', async ({page, server}) => {
|
||||
|
|
|
|||
|
|
@ -22,11 +22,10 @@ import path from 'path';
|
|||
|
||||
it('should be able to save file', (test, parameters) => {
|
||||
test.skip(!(options.HEADLESS && options.CHROMIUM(parameters)), 'Printing to pdf is currently only supported in headless chromium.');
|
||||
}, async ({page, tmpDir}) => {
|
||||
const outputFile = path.join(tmpDir, 'output.pdf');
|
||||
}, async ({page, testOutputDir}) => {
|
||||
const outputFile = path.join(testOutputDir, 'output.pdf');
|
||||
await page.pdf({path: outputFile});
|
||||
expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0);
|
||||
fs.unlinkSync(outputFile);
|
||||
});
|
||||
|
||||
it('should only have pdf in chromium', (test, parameters) => {
|
||||
|
|
|
|||
|
|
@ -17,16 +17,19 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import util from 'util';
|
||||
import childProcess from 'child_process';
|
||||
import type { LaunchOptions, BrowserType, Browser, BrowserContext, Page, BrowserServer, BrowserContextOptions } from '../index';
|
||||
import type { LaunchOptions, BrowserType, Browser, BrowserContext, Page, Frame, BrowserServer, BrowserContextOptions } from '../index';
|
||||
import { TestServer } from '../utils/testserver';
|
||||
import { Connection } from '../lib/client/connection';
|
||||
import { Transport } from '../lib/protocol/transport';
|
||||
import { installCoverageHooks } from './coverage';
|
||||
import { mkdtempAsync, removeFolderAsync } from './utils';
|
||||
import { fixtures as baseFixtures } from '@playwright/test-runner';
|
||||
import assert from 'assert';
|
||||
|
||||
const mkdtempAsync = util.promisify(fs.mkdtemp);
|
||||
const removeFolderAsync = util.promisify(require('rimraf'));
|
||||
|
||||
type PlaywrightParameters = {
|
||||
platform: 'win32' | 'linux' | 'darwin'
|
||||
browserName: string;
|
||||
|
|
@ -48,21 +51,24 @@ type PlaywrightWorkerFixtures = {
|
|||
isWindows: boolean;
|
||||
isMac: boolean;
|
||||
isLinux: boolean;
|
||||
expectedSSLError: string;
|
||||
};
|
||||
|
||||
type PlaywrightFixtures = {
|
||||
type PlaywrightTestFixtures = {
|
||||
context: BrowserContext;
|
||||
server: TestServer;
|
||||
page: Page;
|
||||
httpsServer: TestServer;
|
||||
browserServer: BrowserServer;
|
||||
testOutputDir: string;
|
||||
createUserDataDir: () => Promise<string>;
|
||||
launchPersistent: (options?: Parameters<BrowserType<Browser>['launchPersistentContext']>[1]) => Promise<{context: BrowserContext, page: Page}>;
|
||||
};
|
||||
|
||||
const fixtures = baseFixtures
|
||||
.declareParameters<PlaywrightParameters>()
|
||||
.declareWorkerFixtures<PlaywrightWorkerFixtures>()
|
||||
.declareTestFixtures<PlaywrightFixtures>();
|
||||
.declareTestFixtures<PlaywrightTestFixtures>();
|
||||
const { defineTestFixture, defineWorkerFixture, defineParameter, generateParametrizedTests } = fixtures;
|
||||
|
||||
export const playwrightFixtures = fixtures;
|
||||
|
|
@ -236,12 +242,42 @@ defineWorkerFixture('golden', async ({browserName}, test) => {
|
|||
await test(p => path.join(browserName, p));
|
||||
});
|
||||
|
||||
defineTestFixture('context', async ({browser}, runTest, info) => {
|
||||
defineWorkerFixture('expectedSSLError', async ({browserName, platform}, runTest) => {
|
||||
let expectedSSLError: string;
|
||||
if (browserName === 'chromium') {
|
||||
expectedSSLError = 'net::ERR_CERT_AUTHORITY_INVALID';
|
||||
} else if (browserName === 'webkit') {
|
||||
if (platform === 'darwin')
|
||||
expectedSSLError = 'The certificate for this server is invalid';
|
||||
else if (platform === 'win32')
|
||||
expectedSSLError = 'SSL peer certificate or SSH remote key was not OK';
|
||||
else
|
||||
expectedSSLError = 'Unacceptable TLS certificate';
|
||||
} else {
|
||||
expectedSSLError = 'SSL_ERROR_UNKNOWN';
|
||||
}
|
||||
await runTest(expectedSSLError);
|
||||
});
|
||||
|
||||
defineTestFixture('testOutputDir', async ({}, runTest, info) => {
|
||||
const { test, config } = info;
|
||||
const relativePath = path.relative(config.testDir, test.file).replace(/\.spec\.[jt]s/, '');
|
||||
const sanitizedTitle = test.title.replace(/[^\w\d]+/g, '_');
|
||||
const testOutputDir = path.join(config.outputDir, relativePath, sanitizedTitle);
|
||||
await fs.promises.mkdir(testOutputDir, { recursive: true });
|
||||
await runTest(testOutputDir);
|
||||
const files = await fs.promises.readdir(testOutputDir);
|
||||
if (!files.length) {
|
||||
// Do not leave an empty useless directory.
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeFolderAsync(testOutputDir).catch(e => {});
|
||||
}
|
||||
});
|
||||
|
||||
defineTestFixture('context', async ({browser, testOutputDir}, runTest, info) => {
|
||||
const { config } = info;
|
||||
const contextOptions: BrowserContextOptions = {
|
||||
relativeArtifactsPath: path.join(relativePath, sanitizedTitle),
|
||||
relativeArtifactsPath: path.relative(config.outputDir, testOutputDir),
|
||||
recordTrace: !!options.TRACING,
|
||||
};
|
||||
const context = await browser.newContext(contextOptions);
|
||||
|
|
@ -249,24 +285,37 @@ defineTestFixture('context', async ({browser}, runTest, info) => {
|
|||
await context.close();
|
||||
});
|
||||
|
||||
defineTestFixture('page', async ({context}, runTest, info) => {
|
||||
defineTestFixture('page', async ({context, testOutputDir}, runTest, info) => {
|
||||
const page = await context.newPage();
|
||||
await runTest(page);
|
||||
const { test, config, result } = info;
|
||||
if (result.status === 'failed' || result.status === 'timedOut') {
|
||||
const relativePath = path.relative(config.testDir, test.file).replace(/\.spec\.[jt]s/, '');
|
||||
const sanitizedTitle = test.title.replace(/[^\w\d]+/g, '_');
|
||||
const assetPath = path.join(config.outputDir, relativePath, sanitizedTitle) + '-failed.png';
|
||||
await page.screenshot({ timeout: 5000, path: assetPath });
|
||||
}
|
||||
const { result } = info;
|
||||
if (result.status === 'failed' || result.status === 'timedOut')
|
||||
await page.screenshot({ timeout: 5000, path: path.join(testOutputDir, 'test-failed.png') });
|
||||
});
|
||||
|
||||
defineTestFixture('launchPersistent', async ({tmpDir, defaultBrowserOptions, browserType}, test) => {
|
||||
defineTestFixture('createUserDataDir', async ({testOutputDir}, runTest, info) => {
|
||||
let counter = 0;
|
||||
const dirs: string[] = [];
|
||||
async function createUserDataDir() {
|
||||
const dir = path.join(testOutputDir, `user-data-dir-${counter++}`);
|
||||
dirs.push(dir);
|
||||
await fs.promises.mkdir(dir, { recursive: true });
|
||||
return dir;
|
||||
}
|
||||
await runTest(createUserDataDir);
|
||||
// Remove user data dirs, because we cannot upload them as test result artifacts.
|
||||
// - Firefox removes lock file later, repsumably from another watchdog process?
|
||||
// - WebKit has circular symlinks that makes CI go crazy.
|
||||
await Promise.all(dirs.map(dir => removeFolderAsync(dir).catch(e => {})));
|
||||
});
|
||||
|
||||
defineTestFixture('launchPersistent', async ({createUserDataDir, defaultBrowserOptions, browserType}, test) => {
|
||||
let context;
|
||||
async function launchPersistent(options) {
|
||||
if (context)
|
||||
throw new Error('can only launch one persitent context');
|
||||
context = await browserType.launchPersistentContext(tmpDir, {...defaultBrowserOptions, ...options});
|
||||
const userDataDir = await createUserDataDir();
|
||||
context = await browserType.launchPersistentContext(userDataDir, {...defaultBrowserOptions, ...options});
|
||||
const page = context.pages()[0];
|
||||
return {context, page};
|
||||
}
|
||||
|
|
@ -296,3 +345,28 @@ function valueFromEnv(name, defaultValue) {
|
|||
return defaultValue;
|
||||
return JSON.parse(process.env[name]);
|
||||
}
|
||||
|
||||
export async function attachFrame(page: Page, frameId: string, url: string): Promise<Frame> {
|
||||
const handle = await page.evaluateHandle(async ({ frameId, url }) => {
|
||||
const frame = document.createElement('iframe');
|
||||
frame.src = url;
|
||||
frame.id = frameId;
|
||||
document.body.appendChild(frame);
|
||||
await new Promise(x => frame.onload = x);
|
||||
return frame;
|
||||
}, { frameId, url });
|
||||
return handle.asElement().contentFrame();
|
||||
}
|
||||
|
||||
export async function detachFrame(page: Page, frameId: string) {
|
||||
await page.evaluate(frameId => {
|
||||
document.getElementById(frameId).remove();
|
||||
}, frameId);
|
||||
}
|
||||
|
||||
export async function verifyViewport(page: Page, width: number, height: number) {
|
||||
expect(page.viewportSize().width).toBe(width);
|
||||
expect(page.viewportSize().height).toBe(height);
|
||||
expect(await page.evaluate('window.innerWidth')).toBe(width);
|
||||
expect(await page.evaluate('window.innerHeight')).toBe(height);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
import path from 'path';
|
||||
import utils from './utils';
|
||||
|
||||
it('should work', async ({playwright, browser}) => {
|
||||
const createTagSelector = () => ({
|
||||
|
|
@ -33,11 +32,11 @@ it('should work', async ({playwright, browser}) => {
|
|||
}
|
||||
});
|
||||
// Register one engine before creating context.
|
||||
await utils.registerEngine(playwright, 'tag', `(${createTagSelector.toString()})()`);
|
||||
await playwright.selectors.register('tag', `(${createTagSelector.toString()})()`);
|
||||
|
||||
const context = await browser.newContext();
|
||||
// Register another engine after creating context.
|
||||
await utils.registerEngine(playwright, 'tag2', `(${createTagSelector.toString()})()`);
|
||||
await playwright.selectors.register('tag2', `(${createTagSelector.toString()})()`);
|
||||
|
||||
const page = await context.newPage();
|
||||
await page.setContent('<div><span></span></div><div></div>');
|
||||
|
|
@ -60,7 +59,7 @@ it('should work', async ({playwright, browser}) => {
|
|||
});
|
||||
|
||||
it('should work with path', async ({playwright, page}) => {
|
||||
await utils.registerEngine(playwright, 'foo', { path: path.join(__dirname, 'assets/sectionselectorengine.js') });
|
||||
await playwright.selectors.register('foo', { path: path.join(__dirname, 'assets/sectionselectorengine.js') });
|
||||
await page.setContent('<section></section>');
|
||||
expect(await page.$eval('foo=whatever', e => e.nodeName)).toBe('SECTION');
|
||||
});
|
||||
|
|
@ -75,8 +74,8 @@ it('should work in main and isolated world', async ({playwright, page}) => {
|
|||
return [document.body, document.documentElement, window['__answer']];
|
||||
}
|
||||
});
|
||||
await utils.registerEngine(playwright, 'main', createDummySelector);
|
||||
await utils.registerEngine(playwright, 'isolated', createDummySelector, { contentScript: true });
|
||||
await playwright.selectors.register('main', createDummySelector);
|
||||
await playwright.selectors.register('isolated', createDummySelector, { contentScript: true });
|
||||
await page.setContent('<div><span><section></section></span></div>');
|
||||
await page.evaluate(() => window['__answer'] = document.querySelector('span'));
|
||||
// Works in main if asked.
|
||||
|
|
@ -117,8 +116,8 @@ it('should handle errors', async ({playwright, page}) => {
|
|||
expect(error.message).toBe('Selector engine name may only contain [a-zA-Z0-9_] characters');
|
||||
|
||||
// Selector names are case-sensitive.
|
||||
await utils.registerEngine(playwright, 'dummy', createDummySelector);
|
||||
await utils.registerEngine(playwright, 'duMMy', createDummySelector);
|
||||
await playwright.selectors.register('dummy', createDummySelector);
|
||||
await playwright.selectors.register('duMMy', createDummySelector);
|
||||
|
||||
error = await playwright.selectors.register('dummy', createDummySelector).catch(e => e);
|
||||
expect(error.message).toBe('"dummy" selector engine has been already registered');
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, describe, options } from './playwright.fixtures';
|
||||
import { attachFrame } from './utils';
|
||||
import { it, expect, describe, options, attachFrame } from './playwright.fixtures';
|
||||
|
||||
async function checkSlowMo(toImpl, page, task) {
|
||||
let didSlowMo = false;
|
||||
|
|
|
|||
245
test/utils.js
245
test/utils.js
|
|
@ -1,245 +0,0 @@
|
|||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const util = require('util');
|
||||
const os = require('os');
|
||||
const removeFolder = require('rimraf');
|
||||
const { fixtures } = require('@playwright/test-runner');
|
||||
const { expect } = fixtures;
|
||||
|
||||
const {FlakinessDashboard} = require('../utils/flakiness-dashboard');
|
||||
const PROJECT_ROOT = fs.existsSync(path.join(__dirname, '..', 'package.json')) ? path.join(__dirname, '..') : path.join(__dirname, '..', '..');
|
||||
|
||||
let platform = os.platform();
|
||||
|
||||
const utils = module.exports = {
|
||||
mkdtempAsync: util.promisify(fs.mkdtemp),
|
||||
|
||||
removeFolderAsync: util.promisify(removeFolder),
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
projectRoot: function() {
|
||||
return PROJECT_ROOT;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {!Page} page
|
||||
* @param {string} frameId
|
||||
* @param {string} url
|
||||
* @return {!Playwright.Frame}
|
||||
*/
|
||||
attachFrame: async function(page, frameId, url) {
|
||||
const handle = await page.evaluateHandle(async ({ frameId, url }) => {
|
||||
const frame = document.createElement('iframe');
|
||||
frame.src = url;
|
||||
frame.id = frameId;
|
||||
document.body.appendChild(frame);
|
||||
await new Promise(x => frame.onload = x);
|
||||
return frame;
|
||||
}, { frameId, url });
|
||||
return handle.asElement().contentFrame();
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {!Page} page
|
||||
* @param {string} frameId
|
||||
*/
|
||||
detachFrame: async function(page, frameId) {
|
||||
await page.evaluate(frameId => {
|
||||
document.getElementById(frameId).remove();
|
||||
}, frameId);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {!Frame} frame
|
||||
* @param {string=} indentation
|
||||
* @return {Array<string>}
|
||||
*/
|
||||
dumpFrames: function(frame, indentation) {
|
||||
indentation = indentation || '';
|
||||
let description = frame.url().replace(/:\d{4}\//, ':<PORT>/');
|
||||
if (frame.name())
|
||||
description += ' (' + frame.name() + ')';
|
||||
const result = [indentation + description];
|
||||
const childFrames = frame.childFrames();
|
||||
childFrames.sort((a, b) => {
|
||||
if (a.url() !== b.url())
|
||||
return a.url() < b.url() ? -1 : 1;
|
||||
return a.name() < b.name() ? -1 : 1;
|
||||
});
|
||||
for (const child of childFrames)
|
||||
result.push(...utils.dumpFrames(child, ' ' + indentation));
|
||||
return result;
|
||||
},
|
||||
|
||||
verifyViewport: async (page, width, height) => {
|
||||
expect(page.viewportSize().width).toBe(width);
|
||||
expect(page.viewportSize().height).toBe(height);
|
||||
expect(await page.evaluate('window.innerWidth')).toBe(width);
|
||||
expect(await page.evaluate('window.innerHeight')).toBe(height);
|
||||
},
|
||||
|
||||
registerEngine: async (playwright, name, script, options) => {
|
||||
try {
|
||||
await playwright.selectors.register(name, script, options);
|
||||
} catch (e) {
|
||||
if (!e.message.includes('has been already registered'))
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
|
||||
initializeFlakinessDashboardIfNeeded: async function(testRunner) {
|
||||
// Generate testIDs for all tests and verify they don't clash.
|
||||
// This will add |test.testId| for every test.
|
||||
//
|
||||
// NOTE: we do this on CI's so that problems arise on PR trybots.
|
||||
if (process.env.CI)
|
||||
generateTestIDs(testRunner);
|
||||
// FLAKINESS_DASHBOARD_PASSWORD is an encrypted/secured variable.
|
||||
// Encrypted variables get a special treatment in CI's when handling PRs so that
|
||||
// secrets are not leaked to untrusted code.
|
||||
// - AppVeyor DOES NOT decrypt secured variables for PRs
|
||||
// - Travis DOES NOT decrypt encrypted variables for PRs
|
||||
// - Cirrus CI DOES NOT decrypt encrypted variables for PRs *unless* PR is sent
|
||||
// from someone who has WRITE ACCESS to the repo.
|
||||
//
|
||||
// Since we don't want to run flakiness dashboard for PRs on all CIs, we
|
||||
// check existence of FLAKINESS_DASHBOARD_PASSWORD and absence of
|
||||
// CIRRUS_BASE_SHA env variables.
|
||||
if (!process.env.FLAKINESS_DASHBOARD_PASSWORD || process.env.CIRRUS_BASE_SHA)
|
||||
return;
|
||||
const {sha, timestamp} = await FlakinessDashboard.getCommitDetails(__dirname, 'HEAD');
|
||||
const dashboard = new FlakinessDashboard({
|
||||
commit: {
|
||||
sha,
|
||||
timestamp,
|
||||
url: `https://github.com/Microsoft/playwright/commit/${sha}`,
|
||||
},
|
||||
build: {
|
||||
url: process.env.FLAKINESS_DASHBOARD_BUILD_URL,
|
||||
},
|
||||
dashboardRepo: {
|
||||
url: 'https://github.com/aslushnikov/playwright-flakiness-dashboard.git',
|
||||
username: 'playwright-flakiness',
|
||||
email: 'aslushnikov+playwrightflakiness@gmail.com',
|
||||
password: process.env.FLAKINESS_DASHBOARD_PASSWORD,
|
||||
branch: process.env.FLAKINESS_DASHBOARD_NAME,
|
||||
},
|
||||
});
|
||||
|
||||
testRunner.on('testfinished', (test, parameters) => {
|
||||
// Do not report tests from COVERAGE testsuite.
|
||||
// They don't bring much value to us.
|
||||
if (test.fullName.includes('**API COVERAGE**'))
|
||||
return;
|
||||
const testpath = test.location.filePath.substring(utils.projectRoot().length);
|
||||
const url = `https://github.com/Microsoft/playwright/blob/${sha}/${testpath}#L${test.location.lineNumber}`;
|
||||
dashboard.reportTestResult({
|
||||
testId: test.testId,
|
||||
name: test.location().toString(),
|
||||
description: test.fullName(),
|
||||
url,
|
||||
result: test.result,
|
||||
});
|
||||
});
|
||||
testRunner.on('finished', async({result}) => {
|
||||
dashboard.setBuildResult(result);
|
||||
await dashboard.uploadAndCleanup();
|
||||
});
|
||||
|
||||
function generateTestIDs(testRunner) {
|
||||
const testIds = new Map();
|
||||
for (const test of testRunner.tests()) {
|
||||
const testIdComponents = [test.name];
|
||||
for (let suite = test.suite; !!suite.parentSuite; suite = suite.parentSuite)
|
||||
testIdComponents.push(suite.name);
|
||||
testIdComponents.reverse();
|
||||
const testId = testIdComponents.join('>');
|
||||
const clashingTest = testIds.get(testId);
|
||||
if (clashingTest)
|
||||
throw new Error(`Two tests with clashing IDs: ${test.location()} and ${clashingTest.location()}`);
|
||||
testIds.set(testId, test);
|
||||
test.testId = testId;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
makeUserDataDir: async function() {
|
||||
return await utils.mkdtempAsync(path.join(os.tmpdir(), 'playwright_dev_profile-'));
|
||||
},
|
||||
|
||||
removeUserDataDir: async function(dir) {
|
||||
await utils.removeFolderAsync(dir).catch(e => {});
|
||||
},
|
||||
|
||||
setPlatform(p) {
|
||||
// To support isplaywrightready.
|
||||
platform = p;
|
||||
},
|
||||
|
||||
createTestLogger(dumpLogOnFailure = true, testRun = null, prefix = '') {
|
||||
const colors = [31, 32, 33, 34, 35, 36, 37];
|
||||
let colorIndex = 0;
|
||||
for (let i = 0; i < prefix.length; i++)
|
||||
colorIndex += prefix.charCodeAt(i);
|
||||
const color = colors[colorIndex % colors.length];
|
||||
prefix = prefix ? `\x1b[${color}m[${prefix}]\x1b[0m ` : '';
|
||||
|
||||
const logger = {
|
||||
isEnabled: (name, severity) => {
|
||||
return name.startsWith('browser') || dumpLogOnFailure;
|
||||
},
|
||||
log: (name, severity, message, args) => {
|
||||
if (!testRun)
|
||||
return;
|
||||
if (name.startsWith('browser')) {
|
||||
if (severity === 'warning')
|
||||
testRun.log(`${prefix}\x1b[31m[browser]\x1b[0m ${message}`)
|
||||
else
|
||||
testRun.log(`${prefix}\x1b[33m[browser]\x1b[0m ${message}`)
|
||||
} else if (dumpLogOnFailure) {
|
||||
testRun.log(`${prefix}\x1b[32m[${name}]\x1b[0m ${message}`)
|
||||
}
|
||||
},
|
||||
setTestRun(tr) {
|
||||
if (testRun && testRun.ok())
|
||||
testRun.output().splice(0);
|
||||
testRun = tr;
|
||||
},
|
||||
};
|
||||
return logger;
|
||||
},
|
||||
|
||||
expectSSLError(browserName, errorMessage) {
|
||||
if (browserName === 'chromium') {
|
||||
expect(errorMessage).toContain('net::ERR_CERT_AUTHORITY_INVALID');
|
||||
} else if (browserName === 'webkit') {
|
||||
if (platform === 'darwin')
|
||||
expect(errorMessage).toContain('The certificate for this server is invalid');
|
||||
else if (platform === 'win32')
|
||||
expect(errorMessage).toContain('SSL peer certificate or SSH remote key was not OK');
|
||||
else
|
||||
expect(errorMessage).toContain('Unacceptable TLS certificate');
|
||||
} else {
|
||||
expect(errorMessage).toContain('SSL_ERROR_UNKNOWN');
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -15,8 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
import utils from './utils';
|
||||
import { it, expect, attachFrame, detachFrame } from './playwright.fixtures';
|
||||
|
||||
async function giveItTimeToLog(frame) {
|
||||
await frame.evaluate(() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f))));
|
||||
|
|
@ -197,7 +196,7 @@ it('should work when node is added through innerHTML', async ({page, server}) =>
|
|||
|
||||
it('page.waitForSelector is shortcut for main frame', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const otherFrame = page.frames()[1];
|
||||
const watchdog = page.waitForSelector('div', { state: 'attached' });
|
||||
await otherFrame.evaluate(addElement, 'div');
|
||||
|
|
@ -207,8 +206,8 @@ it('page.waitForSelector is shortcut for main frame', async ({page, server}) =>
|
|||
});
|
||||
|
||||
it('should run in specified frame', async ({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
const frame1 = page.frames()[1];
|
||||
const frame2 = page.frames()[2];
|
||||
const waitForSelectorPromise = frame2.waitForSelector('div', { state: 'attached' });
|
||||
|
|
@ -219,11 +218,11 @@ it('should run in specified frame', async ({page, server}) => {
|
|||
});
|
||||
|
||||
it('should throw when frame is detached', async ({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
let waitError = null;
|
||||
const waitPromise = frame.waitForSelector('.box').catch(e => waitError = e);
|
||||
await utils.detachFrame(page, 'frame1');
|
||||
await detachFrame(page, 'frame1');
|
||||
await waitPromise;
|
||||
expect(waitError).toBeTruthy();
|
||||
expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
import utils from './utils';
|
||||
import { it, expect, attachFrame, detachFrame } from './playwright.fixtures';
|
||||
|
||||
const addElement = tag => document.body.appendChild(document.createElement(tag));
|
||||
|
||||
|
|
@ -217,8 +216,8 @@ it('should respect timeout xpath', async ({page, playwright}) => {
|
|||
});
|
||||
|
||||
it('should run in specified frame xpath', async ({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
const frame1 = page.frames()[1];
|
||||
const frame2 = page.frames()[2];
|
||||
const waitForXPathPromise = frame2.waitForSelector('//div', { state: 'attached' });
|
||||
|
|
@ -229,11 +228,11 @@ it('should run in specified frame xpath', async ({page, server}) => {
|
|||
});
|
||||
|
||||
it('should throw when frame is detached xpath', async ({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
let waitError = null;
|
||||
const waitPromise = frame.waitForSelector('//*[@class="box"]').catch(e => waitError = e);
|
||||
await utils.detachFrame(page, 'frame1');
|
||||
await detachFrame(page, 'frame1');
|
||||
await waitPromise;
|
||||
expect(waitError).toBeTruthy();
|
||||
expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
|
||||
|
|
|
|||
Loading…
Reference in a new issue