chore: roll test runner to 0.3.9 (#3847)
This commit is contained in:
parent
38ed8de23d
commit
f94df318d5
12
package-lock.json
generated
12
package-lock.json
generated
|
|
@ -1168,9 +1168,9 @@
|
|||
}
|
||||
},
|
||||
"@playwright/test-runner": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test-runner/-/test-runner-0.3.5.tgz",
|
||||
"integrity": "sha512-G/X9fjqr4ZOyZojQpKAlid7KC8Bw+S834KxSD9nQSqLaN8QFpebFr9xqVhEs/G82RP8DRzxlp8vi5CqLhGrU0w==",
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test-runner/-/test-runner-0.3.9.tgz",
|
||||
"integrity": "sha512-+kFADjH/g7GQ4uXzCJ5/1ke79n2WCnwRsccbrRgCYcUW96/r97FQnywfYZjJnPcTbvJf4DqYBZ2/0wS7pAEmvQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
|
|
@ -3050,9 +3050,9 @@
|
|||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.564",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.564.tgz",
|
||||
"integrity": "sha512-fNaYN3EtKQWLQsrKXui8mzcryJXuA0LbCLoizeX6oayG2emBaS5MauKjCPAvc29NEY4FpLHIUWiP+Y0Bfrs5dg==",
|
||||
"version": "1.3.565",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.565.tgz",
|
||||
"integrity": "sha512-me5dGlHFd8Q7mKhqbWRLIYnKjw4i0fO6hmW0JBxa7tM87fBfNEjWokRnDF7V+Qme/9IYpwhfMn+soWs40tXWqg==",
|
||||
"dev": true
|
||||
},
|
||||
"elliptic": {
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
"ws": "^7.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test-runner": "^0.3.5",
|
||||
"@playwright/test-runner": "^0.3.9",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/extract-zip": "^1.6.2",
|
||||
"@types/mime": "^2.0.3",
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import { it, expect, describe, options } from './playwright.fixtures';
|
||||
|
||||
it('should work', async function({page}) {
|
||||
it('should work', async ({ page, isFirefox, isChromium }) => {
|
||||
await page.setContent(`
|
||||
<head>
|
||||
<title>Accessibility Test</title>
|
||||
|
|
@ -36,7 +36,7 @@ it('should work', async function({page}) {
|
|||
// autofocus happens after a delay in chrome these days
|
||||
await page.waitForFunction(() => document.activeElement.hasAttribute('autofocus'));
|
||||
|
||||
const golden = options.FIREFOX ? {
|
||||
const golden = isFirefox ? {
|
||||
role: 'document',
|
||||
name: 'Accessibility Test',
|
||||
children: [
|
||||
|
|
@ -49,7 +49,7 @@ it('should work', async function({page}) {
|
|||
{role: 'textbox', name: '', value: 'and a value'}, // firefox doesn't use aria-placeholder for the name
|
||||
{role: 'textbox', name: '', value: 'and a value', description: 'This is a description!'}, // and here
|
||||
]
|
||||
} : options.CHROMIUM ? {
|
||||
} : isChromium ? {
|
||||
role: 'WebArea',
|
||||
name: 'Accessibility Test',
|
||||
children: [
|
||||
|
|
@ -79,11 +79,11 @@ it('should work', async function({page}) {
|
|||
expect(await page.accessibility.snapshot()).toEqual(golden);
|
||||
});
|
||||
|
||||
it('should work with regular text', async ({page}) => {
|
||||
it('should work with regular text', async ({page, isFirefox}) => {
|
||||
await page.setContent(`<div>Hello World</div>`);
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual({
|
||||
role: options.FIREFOX ? 'text leaf' : 'text',
|
||||
role: isFirefox ? 'text leaf' : 'text',
|
||||
name: 'Hello World',
|
||||
});
|
||||
});
|
||||
|
|
@ -118,14 +118,14 @@ it('keyshortcuts', async ({page}) => {
|
|||
expect(snapshot.children[0].keyshortcuts).toEqual('foo');
|
||||
});
|
||||
|
||||
it('should not report text nodes inside controls', async function({page}) {
|
||||
it('should not report text nodes inside controls', async function({page, isFirefox}) {
|
||||
await page.setContent(`
|
||||
<div role="tablist">
|
||||
<div role="tab" aria-selected="true"><b>Tab1</b></div>
|
||||
<div role="tab">Tab2</div>
|
||||
</div>`);
|
||||
const golden = {
|
||||
role: options.FIREFOX ? 'document' : 'WebArea',
|
||||
role: isFirefox ? 'document' : 'WebArea',
|
||||
name: '',
|
||||
children: [{
|
||||
role: 'tab',
|
||||
|
|
@ -139,14 +139,14 @@ it('should not report text nodes inside controls', async function({page}) {
|
|||
expect(await page.accessibility.snapshot()).toEqual(golden);
|
||||
});
|
||||
|
||||
it('rich text editable fields should have children', test => {
|
||||
test.skip(options.WEBKIT, 'WebKit rich text accessibility is iffy');
|
||||
}, async function({page}) {
|
||||
it('rich text editable fields should have children', (test, parameters) => {
|
||||
test.skip(options.WEBKIT(parameters), 'WebKit rich text accessibility is iffy');
|
||||
}, async function({page, isFirefox}) {
|
||||
await page.setContent(`
|
||||
<div contenteditable="true">
|
||||
Edit this image: <img src="fakeimage.png" alt="my fake image">
|
||||
</div>`);
|
||||
const golden = options.FIREFOX ? {
|
||||
const golden = isFirefox ? {
|
||||
role: 'section',
|
||||
name: '',
|
||||
children: [{
|
||||
|
|
@ -172,14 +172,14 @@ it('rich text editable fields should have children', test => {
|
|||
expect(snapshot.children[0]).toEqual(golden);
|
||||
});
|
||||
|
||||
it('rich text editable fields with role should have children', test => {
|
||||
test.skip(options.WEBKIT, 'WebKit rich text accessibility is iffy');
|
||||
}, async function({page}) {
|
||||
it('rich text editable fields with role should have children', (test, parameters) => {
|
||||
test.skip(options.WEBKIT(parameters), 'WebKit rich text accessibility is iffy');
|
||||
}, async function({page, isFirefox}) {
|
||||
await page.setContent(`
|
||||
<div contenteditable="true" role='textbox'>
|
||||
Edit this image: <img src="fakeimage.png" alt="my fake image">
|
||||
</div>`);
|
||||
const golden = options.FIREFOX ? {
|
||||
const golden = isFirefox ? {
|
||||
role: 'textbox',
|
||||
name: '',
|
||||
value: 'Edit this image: my fake image',
|
||||
|
|
@ -203,9 +203,9 @@ it('rich text editable fields with role should have children', test => {
|
|||
expect(snapshot.children[0]).toEqual(golden);
|
||||
});
|
||||
|
||||
describe('contenteditable', suite => {
|
||||
suite.skip(options.FIREFOX, 'Firefox does not support contenteditable="plaintext-only"');
|
||||
suite.skip(options.WEBKIT, 'WebKit rich text accessibility is iffy');
|
||||
describe('contenteditable', (suite, parameters) => {
|
||||
suite.skip(options.FIREFOX(parameters), 'Firefox does not support contenteditable="plaintext-only"');
|
||||
suite.skip(options.WEBKIT(parameters), 'WebKit rich text accessibility is iffy');
|
||||
}, () => {
|
||||
it('plain text field with role should not have children', async function({page}) {
|
||||
await page.setContent(`
|
||||
|
|
@ -239,17 +239,17 @@ describe('contenteditable', suite => {
|
|||
});
|
||||
});
|
||||
|
||||
it('non editable textbox with role and tabIndex and label should not have children', async function({page}) {
|
||||
it('non editable textbox with role and tabIndex and label should not have children', async function({page, isChromium, isFirefox}) {
|
||||
await page.setContent(`
|
||||
<div role="textbox" tabIndex=0 aria-checked="true" aria-label="my favorite textbox">
|
||||
this is the inner content
|
||||
<img alt="yo" src="fakeimg.png">
|
||||
</div>`);
|
||||
const golden = options.FIREFOX ? {
|
||||
const golden = isFirefox ? {
|
||||
role: 'textbox',
|
||||
name: 'my favorite textbox',
|
||||
value: 'this is the inner content yo'
|
||||
} : options.CHROMIUM ? {
|
||||
} : isChromium ? {
|
||||
role: 'textbox',
|
||||
name: 'my favorite textbox',
|
||||
value: 'this is the inner content '
|
||||
|
|
@ -277,13 +277,13 @@ it('checkbox with and tabIndex and label should not have children', async functi
|
|||
expect(snapshot.children[0]).toEqual(golden);
|
||||
});
|
||||
|
||||
it('checkbox without label should not have children', async function({page}) {
|
||||
it('checkbox without label should not have children', async ({page, isFirefox}) => {
|
||||
await page.setContent(`
|
||||
<div role="checkbox" aria-checked="true">
|
||||
this is the inner content
|
||||
<img alt="yo" src="fakeimg.png">
|
||||
</div>`);
|
||||
const golden = options.FIREFOX ? {
|
||||
const golden = isFirefox ? {
|
||||
role: 'checkbox',
|
||||
name: 'this is the inner content yo',
|
||||
checked: true
|
||||
|
|
@ -317,7 +317,7 @@ it('should work an input', async ({page}) => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should work on a menu', async ({page}) => {
|
||||
it('should work on a menu', async ({page, isWebKit}) => {
|
||||
await page.setContent(`
|
||||
<div role="menu" title="My Menu">
|
||||
<div role="menuitem">First Item</div>
|
||||
|
|
@ -334,7 +334,7 @@ it('should work on a menu', async ({page}) => {
|
|||
[ { role: 'menuitem', name: 'First Item' },
|
||||
{ role: 'menuitem', name: 'Second Item' },
|
||||
{ role: 'menuitem', name: 'Third Item' } ],
|
||||
orientation: options.WEBKIT ? 'vertical' : undefined
|
||||
orientation: isWebKit ? 'vertical' : undefined
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ it('should work with goto following click', async ({page, server}) => {
|
|||
await page.goto(server.EMPTY_PAGE);
|
||||
});
|
||||
|
||||
it('should report navigation in the log when clicking anchor', test => {
|
||||
it('should report navigation in the log when clicking anchor', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({page, server}) => {
|
||||
await page.setContent(`<a href="${server.PREFIX + '/frames/one-frame.html'}">click me</a>`);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
it('should create new page', async function({browser}) {
|
||||
const page1 = await browser.newPage();
|
||||
|
|
@ -38,9 +38,9 @@ it('should throw upon second create new page', async function({browser}) {
|
|||
expect(error.message).toContain('Please use browser.newContext()');
|
||||
});
|
||||
|
||||
it('version should work', async function({browser}) {
|
||||
it('version should work', async function({browser, isChromium}) {
|
||||
const version = browser.version();
|
||||
if (options.CHROMIUM)
|
||||
if (isChromium)
|
||||
expect(version.match(/^\d+\.\d+\.\d+\.\d+$/)).toBeTruthy();
|
||||
else
|
||||
expect(version.match(/^\d+\.\d+/)).toBeTruthy();
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
it('should work', async ({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
|
@ -139,7 +139,7 @@ it('should isolate send cookie header', async ({server, context, browser}) => {
|
|||
}
|
||||
});
|
||||
|
||||
it('should isolate cookies between launches', test => {
|
||||
it('should isolate cookies between launches', (test, parameters) => {
|
||||
test.slow();
|
||||
}, async ({browserType, server, defaultBrowserOptions}) => {
|
||||
const browser1 = await browserType.launch(defaultBrowserOptions);
|
||||
|
|
@ -315,7 +315,7 @@ it('should set cookies for a frame', async ({context, page, server}) => {
|
|||
expect(await page.frames()[1].evaluate('document.cookie')).toBe('frame-cookie=value');
|
||||
});
|
||||
|
||||
it('should(not) block third party cookies', async ({context, page, server}) => {
|
||||
it('should(not) block third party cookies', async ({context, page, server, isChromium, isFirefox}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(src => {
|
||||
let fulfill;
|
||||
|
|
@ -328,7 +328,7 @@ it('should(not) block third party cookies', async ({context, page, server}) => {
|
|||
}, server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
await page.frames()[1].evaluate(`document.cookie = 'username=John Doe'`);
|
||||
await page.waitForTimeout(2000);
|
||||
const allowsThirdParty = options.CHROMIUM || options.FIREFOX;
|
||||
const allowsThirdParty = isChromium || isFirefox;
|
||||
const cookies = await context.cookies(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
if (allowsThirdParty) {
|
||||
expect(cookies).toEqual([
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
import utils from './utils';
|
||||
|
||||
|
|
@ -178,14 +178,14 @@ it('should close all belonging pages once closing context', async function({brow
|
|||
expect(context.pages().length).toBe(0);
|
||||
});
|
||||
|
||||
it('should disable javascript', async ({browser}) => {
|
||||
it('should disable javascript', async ({browser, isWebKit}) => {
|
||||
{
|
||||
const context = await browser.newContext({ javaScriptEnabled: false });
|
||||
const page = await context.newPage();
|
||||
await page.goto('data:text/html, <script>var something = "forbidden"</script>');
|
||||
let error = null;
|
||||
await page.evaluate('something').catch(e => error = e);
|
||||
if (options.WEBKIT)
|
||||
if (isWebKit)
|
||||
expect(error.message).toContain('Can\'t find variable: something');
|
||||
else
|
||||
expect(error.message).toContain('something is not defined');
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ it('should properly report httpOnly cookie', async ({context, page, server}) =>
|
|||
expect(cookies[0].httpOnly).toBe(true);
|
||||
});
|
||||
|
||||
it('should properly report "Strict" sameSite cookie', test => {
|
||||
test.fail(options.WEBKIT && WIN);
|
||||
it('should properly report "Strict" sameSite cookie', (test, parameters) => {
|
||||
test.fail(options.WEBKIT(parameters) && WIN);
|
||||
}, async ({context, page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'name=value;SameSite=Strict');
|
||||
|
|
@ -86,8 +86,8 @@ it('should properly report "Strict" sameSite cookie', test => {
|
|||
expect(cookies[0].sameSite).toBe('Strict');
|
||||
});
|
||||
|
||||
it('should properly report "Lax" sameSite cookie', test => {
|
||||
test.fail(options.WEBKIT && WIN);
|
||||
it('should properly report "Lax" sameSite cookie', (test, parameters) => {
|
||||
test.fail(options.WEBKIT(parameters) && WIN);
|
||||
}, async ({context, page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'name=value;SameSite=Lax');
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
|
||||
it('should fail without credentials', test => {
|
||||
test.fail(options.CHROMIUM && !options.HEADLESS);
|
||||
it('should fail without credentials', (test, parameters) => {
|
||||
test.fail(options.CHROMIUM(parameters) && !options.HEADLESS);
|
||||
}, async ({browser, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const context = await browser.newContext();
|
||||
|
|
@ -28,8 +28,8 @@ it('should fail without credentials', test => {
|
|||
await context.close();
|
||||
});
|
||||
|
||||
it('should work with setHTTPCredentials', test => {
|
||||
test.fail(options.CHROMIUM && !options.HEADLESS);
|
||||
it('should work with setHTTPCredentials', (test, parameters) => {
|
||||
test.fail(options.CHROMIUM(parameters) && !options.HEADLESS);
|
||||
}, async ({browser, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const context = await browser.newContext();
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
|
||||
import { it, expect, describe, options } from './playwright.fixtures';
|
||||
|
||||
describe('device', suite => {
|
||||
suite.skip(options.FIREFOX);
|
||||
describe('device', (suite, parameters) => {
|
||||
suite.skip(options.FIREFOX(parameters));
|
||||
}, () => {
|
||||
it('should work', async ({playwright, browser, server}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
|
|
|
|||
|
|
@ -157,8 +157,8 @@ it('should fire page lifecycle events', async function({browser, server}) {
|
|||
await context.close();
|
||||
});
|
||||
|
||||
it('should work with Shift-clicking', test => {
|
||||
test.fixme(options.WEBKIT, 'WebKit: Shift+Click does not open a new window.');
|
||||
it('should work with Shift-clicking', (test, parameters) => {
|
||||
test.fixme(options.WEBKIT(parameters), 'WebKit: Shift+Click does not open a new window.');
|
||||
}, async ({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
|
|
@ -172,9 +172,9 @@ it('should work with Shift-clicking', test => {
|
|||
await context.close();
|
||||
});
|
||||
|
||||
it('should work with Ctrl-clicking', test => {
|
||||
test.fixme(options.WEBKIT, 'Ctrl+Click does not open a new tab.');
|
||||
test.fixme(options.FIREFOX, 'Reports an opener in this case.');
|
||||
it('should work with Ctrl-clicking', (test, parameters) => {
|
||||
test.fixme(options.WEBKIT(parameters), 'Ctrl+Click does not open a new tab.');
|
||||
test.fixme(options.FIREFOX(parameters), 'Reports an opener in this case.');
|
||||
}, async ({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
|
||||
import { it, expect, describe, options } from './playwright.fixtures';
|
||||
|
||||
describe('mobile viewport', suite => {
|
||||
suite.skip(options.FIREFOX);
|
||||
describe('mobile viewport', (suite, parameters) => {
|
||||
suite.skip(options.FIREFOX(parameters));
|
||||
}, () => {
|
||||
it('should support mobile emulation', async ({playwright, browser, server}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
it('browserType.executablePath should work', test => {
|
||||
test.skip(Boolean(process.env.CRPATH || process.env.FFPATH || process.env.WKPATH));
|
||||
|
|
@ -26,12 +26,12 @@ it('browserType.executablePath should work', test => {
|
|||
expect(fs.realpathSync(executablePath)).toBe(executablePath);
|
||||
});
|
||||
|
||||
it('browserType.name should work', async ({browserType}) => {
|
||||
if (options.WEBKIT)
|
||||
it('browserType.name should work', async ({browserType, isChromium, isFirefox, isWebKit}) => {
|
||||
if (isWebKit)
|
||||
expect(browserType.name()).toBe('webkit');
|
||||
else if (options.FIREFOX)
|
||||
else if (isFirefox)
|
||||
expect(browserType.name()).toBe('firefox');
|
||||
else if (options.CHROMIUM)
|
||||
else if (isChromium)
|
||||
expect(browserType.name()).toBe('chromium');
|
||||
else
|
||||
throw new Error('Unknown browser');
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ it('should throw if userDataDir option is passed', async ({browserType, defaultB
|
|||
expect(waitError.message).toContain('launchPersistentContext');
|
||||
});
|
||||
|
||||
it('should throw if page argument is passed', test => {
|
||||
test.skip(options.FIREFOX);
|
||||
it('should throw if page argument is passed', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters));
|
||||
}, async ({browserType, defaultBrowserOptions}) => {
|
||||
let waitError = null;
|
||||
const options = Object.assign({}, defaultBrowserOptions, { args: ['http://example.com'] });
|
||||
|
|
@ -45,7 +45,7 @@ it('should throw if page argument is passed', test => {
|
|||
expect(waitError.message).toContain('can not specify page');
|
||||
});
|
||||
|
||||
it('should reject if launched browser fails immediately', test => {
|
||||
it('should reject if launched browser fails immediately', (test, parameters) => {
|
||||
test.fixme(`I'm getting ENCONRESET on this one.`);
|
||||
}, async ({browserType, defaultBrowserOptions}) => {
|
||||
const options = Object.assign({}, defaultBrowserOptions, {executablePath: path.join(__dirname, 'assets', 'dummy_bad_browser_executable.js')});
|
||||
|
|
@ -61,7 +61,7 @@ it('should reject if executable path is invalid', async ({browserType, defaultBr
|
|||
expect(waitError.message).toContain('Failed to launch');
|
||||
});
|
||||
|
||||
it('should handle timeout', test => {
|
||||
it('should handle timeout', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({browserType, defaultBrowserOptions}) => {
|
||||
const options = { ...defaultBrowserOptions, timeout: 5000, __testHookBeforeCreateBrowser: () => new Promise(f => setTimeout(f, 6000)) };
|
||||
|
|
@ -71,7 +71,7 @@ it('should handle timeout', test => {
|
|||
expect(error.message).toContain(`<launched> pid=`);
|
||||
});
|
||||
|
||||
it('should handle exception', test => {
|
||||
it('should handle exception', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({browserType, defaultBrowserOptions}) => {
|
||||
const e = new Error('Dummy');
|
||||
|
|
@ -80,7 +80,7 @@ it('should handle exception', test => {
|
|||
expect(error.message).toContain('Dummy');
|
||||
});
|
||||
|
||||
it('should report launch log', test => {
|
||||
it('should report launch log', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({browserType, defaultBrowserOptions}) => {
|
||||
const e = new Error('Dummy');
|
||||
|
|
@ -89,7 +89,7 @@ it('should report launch log', test => {
|
|||
expect(error.message).toContain('<launching>');
|
||||
});
|
||||
|
||||
it('should accept objects as options', test => {
|
||||
it('should accept objects as options', (test, parameters) => {
|
||||
test.slow();
|
||||
}, async ({browserType, defaultBrowserOptions}) => {
|
||||
// @ts-expect-error process is not a real option.
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
import url from 'url';
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
|
||||
it('Web Assembly should work', test => {
|
||||
test.fail(options.WEBKIT && WIN);
|
||||
it('Web Assembly should work', (test, parameters) => {
|
||||
test.fail(options.WEBKIT(parameters) && WIN);
|
||||
}, async function({page, server}) {
|
||||
await page.goto(server.PREFIX + '/wasm/table2.html');
|
||||
expect(await page.evaluate('loadTable()')).toBe('42, 83');
|
||||
|
|
@ -50,15 +50,15 @@ it('should respect CSP', async ({page, server}) => {
|
|||
expect(await page.evaluate(() => window['testStatus'])).toBe('SUCCESS');
|
||||
});
|
||||
|
||||
it('should play video', test => {
|
||||
test.fixme(options.WEBKIT && (WIN || LINUX));
|
||||
}, async ({page, asset}) => {
|
||||
it('should play video', (test, parameters) => {
|
||||
test.fixme(options.WEBKIT(parameters) && (WIN || LINUX));
|
||||
}, async ({page, asset, isWebKit}) => {
|
||||
// TODO: the test passes on Windows locally but fails on GitHub Action bot,
|
||||
// apparently due to a Media Pack issue in the Windows Server.
|
||||
// Also the test is very flaky on Linux WebKit.
|
||||
//
|
||||
// Safari only plays mp4 so we test WebKit with an .mp4 clip.
|
||||
const fileName = options.WEBKIT ? 'video_mp4.html' : 'video.html';
|
||||
const fileName = isWebKit ? 'video_mp4.html' : 'video.html';
|
||||
const absolutePath = asset(fileName);
|
||||
// Our test server doesn't support range requests required to play on Mac,
|
||||
// so we load the page using a file url.
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@ it('should scope context handles', async ({browserType, browser, server}) => {
|
|||
await expectScopeState(browser, GOLDEN_PRECONDITION);
|
||||
});
|
||||
|
||||
it('should scope CDPSession handles', test => {
|
||||
test.skip(!options.CHROMIUM);
|
||||
it('should scope CDPSession handles', (test, parameters) => {
|
||||
test.skip(!options.CHROMIUM(parameters));
|
||||
}, async ({browserType, browser}) => {
|
||||
const GOLDEN_PRECONDITION = {
|
||||
_guid: '',
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
import { it, expect, describe, options } from './playwright.fixtures';
|
||||
|
||||
describe('oopif', suite => {
|
||||
suite.skip(!options.CHROMIUM);
|
||||
describe('oopif', (suite, parameters) => {
|
||||
suite.skip(!options.CHROMIUM(parameters));
|
||||
}, () => {
|
||||
it('should work', async function({browserType, page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
|
|
|
|||
|
|
@ -16,15 +16,15 @@
|
|||
|
||||
import { it, expect, describe, options } from './playwright.fixtures';
|
||||
|
||||
it('should be missing', test => {
|
||||
test.skip(options.CHROMIUM);
|
||||
it('should be missing', (test, parameters) => {
|
||||
test.skip(options.CHROMIUM(parameters));
|
||||
},
|
||||
async function({page}) {
|
||||
expect(page.coverage).toBe(null);
|
||||
});
|
||||
|
||||
describe('oopif', suite => {
|
||||
suite.skip(!options.CHROMIUM);
|
||||
describe('oopif', (suite, parameters) => {
|
||||
suite.skip(!options.CHROMIUM(parameters));
|
||||
}, () => {
|
||||
it('should work', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
import { it, expect, describe, options } from '../playwright.fixtures';
|
||||
import type { ChromiumBrowserContext } from '../..';
|
||||
|
||||
describe('chromium', suite => {
|
||||
suite.skip(!options.CHROMIUM);
|
||||
describe('chromium', (suite, parameters) => {
|
||||
suite.skip(!options.CHROMIUM(parameters));
|
||||
}, () => {
|
||||
it('should create a worker from a service worker', async ({page, server, context}) => {
|
||||
const [worker] = await Promise.all([
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import utils from '../utils';
|
|||
import type { ChromiumBrowser, ChromiumBrowserContext } from '../..';
|
||||
const { makeUserDataDir, removeUserDataDir } = utils;
|
||||
|
||||
it('should throw with remote-debugging-pipe argument', test => {
|
||||
test.skip(options.WIRE || !options.CHROMIUM);
|
||||
it('should throw with remote-debugging-pipe argument', (test, parameters) => {
|
||||
test.skip(options.WIRE || !options.CHROMIUM(parameters));
|
||||
}, async ({browserType, defaultBrowserOptions}) => {
|
||||
const options = Object.assign({}, defaultBrowserOptions);
|
||||
options.args = ['--remote-debugging-pipe'].concat(options.args || []);
|
||||
|
|
@ -29,8 +29,8 @@ it('should throw with remote-debugging-pipe argument', test => {
|
|||
expect(error.message).toContain('Playwright manages remote debugging connection itself');
|
||||
});
|
||||
|
||||
it('should not throw with remote-debugging-port argument', test => {
|
||||
test.skip(options.WIRE || !options.CHROMIUM);
|
||||
it('should not throw with remote-debugging-port argument', (test, parameters) => {
|
||||
test.skip(options.WIRE || !options.CHROMIUM(parameters));
|
||||
}, async ({browserType, defaultBrowserOptions}) => {
|
||||
const options = Object.assign({}, defaultBrowserOptions);
|
||||
options.args = ['--remote-debugging-port=0'].concat(options.args || []);
|
||||
|
|
@ -38,8 +38,8 @@ it('should not throw with remote-debugging-port argument', test => {
|
|||
await browser.close();
|
||||
});
|
||||
|
||||
it('should open devtools when "devtools: true" option is given', test => {
|
||||
test.skip(!options.CHROMIUM || options.WIRE || WIN);
|
||||
it('should open devtools when "devtools: true" option is given', (test, parameters) => {
|
||||
test.skip(!options.CHROMIUM(parameters) || options.WIRE || WIN);
|
||||
}, async ({browserType, defaultBrowserOptions}) => {
|
||||
let devtoolsCallback;
|
||||
const devtoolsPromise = new Promise(f => devtoolsCallback = f);
|
||||
|
|
@ -56,8 +56,8 @@ it('should open devtools when "devtools: true" option is given', test => {
|
|||
await browser.close();
|
||||
});
|
||||
|
||||
it('should return background pages', test => {
|
||||
test.skip(!options.CHROMIUM);
|
||||
it('should return background pages', (test, parameters) => {
|
||||
test.skip(!options.CHROMIUM(parameters));
|
||||
}, async ({browserType, defaultBrowserOptions}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const extensionPath = path.join(__dirname, '..', 'assets', 'simple-extension');
|
||||
|
|
@ -80,8 +80,8 @@ it('should return background pages', test => {
|
|||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
|
||||
it('should not create pages automatically', test => {
|
||||
test.skip(!options.CHROMIUM);
|
||||
it('should not create pages automatically', (test, parameters) => {
|
||||
test.skip(!options.CHROMIUM(parameters));
|
||||
}, async ({browserType, defaultBrowserOptions}) => {
|
||||
const browser = await browserType.launch(defaultBrowserOptions);
|
||||
const browserSession = await (browser as ChromiumBrowser).newBrowserCDPSession();
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@
|
|||
*/
|
||||
|
||||
import { options, playwrightFixtures } from '../playwright.fixtures';
|
||||
const { it, expect, describe, registerWorkerFixture } = playwrightFixtures;
|
||||
const { it, expect, describe, overrideWorkerFixture } = playwrightFixtures;
|
||||
|
||||
registerWorkerFixture('browser', async ({browserType, defaultBrowserOptions}, test) => {
|
||||
overrideWorkerFixture('browser', async ({browserType, defaultBrowserOptions}, test) => {
|
||||
const browser = await browserType.launch({
|
||||
...defaultBrowserOptions,
|
||||
args: (defaultBrowserOptions.args || []).concat(['--site-per-process'])
|
||||
|
|
@ -26,8 +26,8 @@ registerWorkerFixture('browser', async ({browserType, defaultBrowserOptions}, te
|
|||
await browser.close();
|
||||
});
|
||||
|
||||
describe('oopif', suite => {
|
||||
suite.skip(!options.CHROMIUM);
|
||||
describe('oopif', (suite, parameters) => {
|
||||
suite.skip(!options.CHROMIUM(parameters));
|
||||
}, () => {
|
||||
it('should report oopif frames', async function({browser, page, server}) {
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
|
|
@ -68,9 +68,9 @@ describe('oopif', suite => {
|
|||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
});
|
||||
|
||||
it('should get the proper viewport', test => {
|
||||
test.fixme(options.CHROMIUM);
|
||||
test.skip(!options.CHROMIUM);
|
||||
it('should get the proper viewport', (test, parameters) => {
|
||||
test.fixme(options.CHROMIUM(parameters));
|
||||
test.skip(!options.CHROMIUM(parameters));
|
||||
}, async ({browser, page, server}) => {
|
||||
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
import { it, expect, describe, options } from '../playwright.fixtures';
|
||||
import type { ChromiumBrowserContext, ChromiumBrowser } from '../../types/types';
|
||||
|
||||
describe('session', suite => {
|
||||
suite.skip(!options.CHROMIUM);
|
||||
describe('session', (suite, parameters) => {
|
||||
suite.skip(!options.CHROMIUM(parameters));
|
||||
}, () => {
|
||||
it('should work', async function({page}) {
|
||||
const client = await (page.context() as ChromiumBrowserContext).newCDPSession(page);
|
||||
|
|
|
|||
|
|
@ -22,19 +22,19 @@ import type { ChromiumBrowser } from '../..';
|
|||
type TestState = {
|
||||
outputTraceFile: string;
|
||||
};
|
||||
const fixtures = playwrightFixtures.extend<{}, TestState>();
|
||||
const { it, expect, describe, registerFixture } = fixtures;
|
||||
const fixtures = playwrightFixtures.declareTestFixtures<TestState>();
|
||||
const { it, expect, describe, defineTestFixture } = fixtures;
|
||||
|
||||
|
||||
registerFixture('outputTraceFile', async ({tmpDir}, test) => {
|
||||
defineTestFixture('outputTraceFile', async ({tmpDir}, test) => {
|
||||
const outputTraceFile = path.join(tmpDir, `trace.json`);
|
||||
await test(outputTraceFile);
|
||||
if (fs.existsSync(outputTraceFile))
|
||||
fs.unlinkSync(outputTraceFile);
|
||||
});
|
||||
|
||||
describe('oopif', suite => {
|
||||
suite.skip(!options.CHROMIUM);
|
||||
describe('oopif', (suite, parameters) => {
|
||||
suite.skip(!options.CHROMIUM(parameters));
|
||||
}, () => {
|
||||
it('should output a trace', async ({browser, page, server, outputTraceFile}) => {
|
||||
await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputTraceFile});
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ declare const renderComponent;
|
|||
declare const e;
|
||||
declare const MyButton;
|
||||
|
||||
it('should report that selector does not match anymore', test => {
|
||||
it('should report that selector does not match anymore', (test, parameters) => {
|
||||
test.fail(true);
|
||||
}, async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
|
|
@ -41,7 +41,7 @@ it('should report that selector does not match anymore', test => {
|
|||
expect(error.message).toContain('element does not match the selector anymore');
|
||||
});
|
||||
|
||||
it('should not retarget the handle when element is recycled', test => {
|
||||
it('should not retarget the handle when element is recycled', (test, parameters) => {
|
||||
test.fixme(true);
|
||||
}, async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
|
|
@ -70,7 +70,7 @@ it('should timeout when click opens alert', async ({page, server}) => {
|
|||
await dialog.dismiss();
|
||||
});
|
||||
|
||||
it('should retarget when element is recycled during hit testing', test => {
|
||||
it('should retarget when element is recycled during hit testing', (test, parameters) => {
|
||||
test.fixme(true);
|
||||
}, async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
|
|
@ -87,7 +87,7 @@ it('should retarget when element is recycled during hit testing', test => {
|
|||
expect(await page.evaluate('window.button2')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should retarget when element is recycled before enabled check', test => {
|
||||
it('should retarget when element is recycled before enabled check', (test, parameters) => {
|
||||
test.fixme(true);
|
||||
}, async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
|
||||
it('should avoid side effects after timeout', test => {
|
||||
it('should avoid side effects after timeout', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
|
||||
it('should fail when element jumps during hit testing', test => {
|
||||
it('should fail when element jumps during hit testing', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({page}) => {
|
||||
await page.setContent('<button>Click me</button>');
|
||||
|
|
|
|||
|
|
@ -322,8 +322,8 @@ it('should click the button inside an iframe', async ({page, server}) => {
|
|||
expect(await frame.evaluate(() => window['result'])).toBe('Clicked');
|
||||
});
|
||||
|
||||
it('should click the button with fixed position inside an iframe', test => {
|
||||
test.fixme(options.CHROMIUM || options.WEBKIT);
|
||||
it('should click the button with fixed position inside an iframe', (test, parameters) => {
|
||||
test.fixme(options.CHROMIUM(parameters) || options.WEBKIT(parameters));
|
||||
}, async ({page, server}) => {
|
||||
// @see https://github.com/GoogleChrome/puppeteer/issues/4110
|
||||
// @see https://bugs.chromium.org/p/chromium/issues/detail?id=986390
|
||||
|
|
@ -351,39 +351,39 @@ it('should click the button with deviceScaleFactor set', async ({browser, server
|
|||
await context.close();
|
||||
});
|
||||
|
||||
it('should click the button with px border with offset', async ({page, server}) => {
|
||||
it('should click the button with px border with offset', async ({page, server, isWebKit}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => button.style.borderWidth = '8px');
|
||||
await page.click('button', { position: { x: 20, y: 10 } });
|
||||
expect(await page.evaluate('result')).toBe('Clicked');
|
||||
// Safari reports border-relative offsetX/offsetY.
|
||||
expect(await page.evaluate('offsetX')).toBe(options.WEBKIT ? 20 + 8 : 20);
|
||||
expect(await page.evaluate('offsetY')).toBe(options.WEBKIT ? 10 + 8 : 10);
|
||||
expect(await page.evaluate('offsetX')).toBe(isWebKit ? 20 + 8 : 20);
|
||||
expect(await page.evaluate('offsetY')).toBe(isWebKit ? 10 + 8 : 10);
|
||||
});
|
||||
|
||||
it('should click the button with em border with offset', async ({page, server}) => {
|
||||
it('should click the button with em border with offset', async ({page, server, isWebKit}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => button.style.borderWidth = '2em');
|
||||
await page.$eval('button', button => button.style.fontSize = '12px');
|
||||
await page.click('button', { position: { x: 20, y: 10 } });
|
||||
expect(await page.evaluate('result')).toBe('Clicked');
|
||||
// Safari reports border-relative offsetX/offsetY.
|
||||
expect(await page.evaluate('offsetX')).toBe(options.WEBKIT ? 12 * 2 + 20 : 20);
|
||||
expect(await page.evaluate('offsetY')).toBe(options.WEBKIT ? 12 * 2 + 10 : 10);
|
||||
expect(await page.evaluate('offsetX')).toBe(isWebKit ? 12 * 2 + 20 : 20);
|
||||
expect(await page.evaluate('offsetY')).toBe(isWebKit ? 12 * 2 + 10 : 10);
|
||||
});
|
||||
|
||||
it('should click a very large button with offset', async ({page, server}) => {
|
||||
it('should click a very large button with offset', async ({page, server, isWebKit}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => button.style.borderWidth = '8px');
|
||||
await page.$eval('button', button => button.style.height = button.style.width = '2000px');
|
||||
await page.click('button', { position: { x: 1900, y: 1910 } });
|
||||
expect(await page.evaluate(() => window['result'])).toBe('Clicked');
|
||||
// Safari reports border-relative offsetX/offsetY.
|
||||
expect(await page.evaluate('offsetX')).toBe(options.WEBKIT ? 1900 + 8 : 1900);
|
||||
expect(await page.evaluate('offsetY')).toBe(options.WEBKIT ? 1910 + 8 : 1910);
|
||||
expect(await page.evaluate('offsetX')).toBe(isWebKit ? 1900 + 8 : 1900);
|
||||
expect(await page.evaluate('offsetY')).toBe(isWebKit ? 1910 + 8 : 1910);
|
||||
});
|
||||
|
||||
it('should click a button in scrolling container with offset', async ({page, server}) => {
|
||||
it('should click a button in scrolling container with offset', async ({page, server, isWebKit}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => {
|
||||
const container = document.createElement('div');
|
||||
|
|
@ -399,13 +399,13 @@ it('should click a button in scrolling container with offset', async ({page, ser
|
|||
await page.click('button', { position: { x: 1900, y: 1910 } });
|
||||
expect(await page.evaluate(() => window['result'])).toBe('Clicked');
|
||||
// Safari reports border-relative offsetX/offsetY.
|
||||
expect(await page.evaluate('offsetX')).toBe(options.WEBKIT ? 1900 + 8 : 1900);
|
||||
expect(await page.evaluate('offsetY')).toBe(options.WEBKIT ? 1910 + 8 : 1910);
|
||||
expect(await page.evaluate('offsetX')).toBe(isWebKit ? 1900 + 8 : 1900);
|
||||
expect(await page.evaluate('offsetY')).toBe(isWebKit ? 1910 + 8 : 1910);
|
||||
});
|
||||
|
||||
it('should click the button with offset with page scale', test => {
|
||||
test.skip(options.FIREFOX);
|
||||
}, async ({browser, server}) => {
|
||||
it('should click the button with offset with page scale', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters));
|
||||
}, async ({browser, server, isWebKit, isChromium}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 400, height: 400 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
|
|
@ -417,10 +417,10 @@ it('should click the button with offset with page scale', test => {
|
|||
expect(await page.evaluate('result')).toBe('Clicked');
|
||||
const round = x => Math.round(x + 0.01);
|
||||
let expected = { x: 28, y: 18 }; // 20;10 + 8px of border in each direction
|
||||
if (options.WEBKIT) {
|
||||
if (isWebKit) {
|
||||
// WebKit rounds up during css -> dip -> css conversion.
|
||||
expected = { x: 29, y: 19 };
|
||||
} else if (options.CHROMIUM && options.HEADLESS) {
|
||||
} else if (isChromium && options.HEADLESS) {
|
||||
// Headless Chromium rounds down during css -> dip -> css conversion.
|
||||
expected = { x: 27, y: 18 };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
import fs from 'fs';
|
||||
import utils from './utils';
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ it('context.clearCookies() should work', async ({server, launchPersistent}) => {
|
|||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
});
|
||||
|
||||
it('should(not) block third party cookies', async ({server, launchPersistent}) => {
|
||||
it('should(not) block third party cookies', async ({server, launchPersistent, isChromium, isFirefox}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(src => {
|
||||
|
|
@ -96,7 +96,7 @@ it('should(not) block third party cookies', async ({server, launchPersistent}) =
|
|||
return document.cookie;
|
||||
});
|
||||
await page.waitForTimeout(2000);
|
||||
const allowsThirdParty = options.CHROMIUM || options.FIREFOX;
|
||||
const allowsThirdParty = isChromium || isFirefox;
|
||||
expect(documentCookie).toBe(allowsThirdParty ? 'username=John Doe' : '');
|
||||
const cookies = await context.cookies(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
if (allowsThirdParty) {
|
||||
|
|
@ -146,12 +146,12 @@ it('should support bypassCSP option', async ({server, launchPersistent}) => {
|
|||
expect(await page.evaluate('__injected')).toBe(42);
|
||||
});
|
||||
|
||||
it('should support javascriptEnabled option', async ({launchPersistent}) => {
|
||||
it('should support javascriptEnabled option', async ({launchPersistent, isWebKit}) => {
|
||||
const {page} = await launchPersistent({javaScriptEnabled: false});
|
||||
await page.goto('data:text/html, <script>var something = "forbidden"</script>');
|
||||
let error = null;
|
||||
await page.evaluate('something').catch(e => error = e);
|
||||
if (options.WEBKIT)
|
||||
if (isWebKit)
|
||||
expect(error.message).toContain('Can\'t find variable: something');
|
||||
else
|
||||
expect(error.message).toContain('something is not defined');
|
||||
|
|
@ -170,7 +170,7 @@ it('should support offline option', async ({server, launchPersistent}) => {
|
|||
expect(error).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should support acceptDownloads option', test => {
|
||||
it('should support acceptDownloads option', (test, parameters) => {
|
||||
test.skip('Unskip once we support downloads in persistent context.');
|
||||
}, async ({server, launchPersistent}) => {
|
||||
const {page} = await launchPersistent({acceptDownloads: true});
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ it('should support hasTouch option', async ({server, launchPersistent}) => {
|
|||
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true);
|
||||
});
|
||||
|
||||
it('should work in persistent context', test => {
|
||||
test.skip(options.FIREFOX);
|
||||
it('should work in persistent context', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters));
|
||||
}, async ({server, launchPersistent}) => {
|
||||
// Firefox does not support mobile.
|
||||
const {page} = await launchPersistent({viewport: {width: 320, height: 480}, isMobile: true});
|
||||
|
|
@ -68,8 +68,8 @@ it('should support ignoreHTTPSErrors option', async ({httpsServer, launchPersist
|
|||
expect(response.ok()).toBe(true);
|
||||
});
|
||||
|
||||
it('should support extraHTTPHeaders option', test => {
|
||||
test.flaky(options.FIREFOX && !options.HEADLESS && LINUX, 'Intermittent timeout on bots');
|
||||
it('should support extraHTTPHeaders option', (test, parameters) => {
|
||||
test.flaky(options.FIREFOX(parameters) && !options.HEADLESS && LINUX, 'Intermittent timeout on bots');
|
||||
}, async ({server, launchPersistent}) => {
|
||||
const {page} = await launchPersistent({extraHTTPHeaders: { foo: 'bar' }});
|
||||
const [request] = await Promise.all([
|
||||
|
|
@ -79,8 +79,8 @@ it('should support extraHTTPHeaders option', test => {
|
|||
expect(request.headers['foo']).toBe('bar');
|
||||
});
|
||||
|
||||
it('should accept userDataDir', test => {
|
||||
test.flaky(options.CHROMIUM);
|
||||
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.
|
||||
|
|
@ -91,7 +91,7 @@ it('should accept userDataDir', test => {
|
|||
await removeUserDataDir(tmpDir);
|
||||
});
|
||||
|
||||
it('should restore state from userDataDir', test => {
|
||||
it('should restore state from userDataDir', (test, parameters) => {
|
||||
test.slow();
|
||||
}, async ({browserType, defaultBrowserOptions, server, launchPersistent}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
|
|
@ -119,9 +119,9 @@ it('should restore state from userDataDir', test => {
|
|||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
|
||||
it('should restore cookies from userDataDir', test => {
|
||||
it('should restore cookies from userDataDir', (test, parameters) => {
|
||||
test.slow();
|
||||
test.flaky(options.CHROMIUM);
|
||||
test.flaky(options.CHROMIUM(parameters));
|
||||
}, async ({browserType, defaultBrowserOptions, server, launchPersistent}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
|
|
@ -158,15 +158,15 @@ it('should have default URL when launching browser', async ({launchPersistent})
|
|||
expect(urls).toEqual(['about:blank']);
|
||||
});
|
||||
|
||||
it('should throw if page argument is passed', test => {
|
||||
test.skip(options.FIREFOX);
|
||||
it('should throw if page argument is passed', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters));
|
||||
}, async ({browserType, defaultBrowserOptions, server, tmpDir}) => {
|
||||
const options = {...defaultBrowserOptions, args: [server.EMPTY_PAGE] };
|
||||
const error = await browserType.launchPersistentContext(tmpDir, options).catch(e => e);
|
||||
expect(error.message).toContain('can not specify page');
|
||||
});
|
||||
|
||||
it('should have passed URL when launching with ignoreDefaultArgs: true', test => {
|
||||
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');
|
||||
|
|
@ -184,7 +184,7 @@ it('should have passed URL when launching with ignoreDefaultArgs: true', test =>
|
|||
await browserContext.close();
|
||||
});
|
||||
|
||||
it('should handle timeout', test => {
|
||||
it('should handle timeout', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({browserType, defaultBrowserOptions, tmpDir}) => {
|
||||
const options = { ...defaultBrowserOptions, timeout: 5000, __testHookBeforeCreateBrowser: () => new Promise(f => setTimeout(f, 6000)) };
|
||||
|
|
@ -192,7 +192,7 @@ it('should handle timeout', test => {
|
|||
expect(error.message).toContain(`browserType.launchPersistentContext: Timeout 5000ms exceeded.`);
|
||||
});
|
||||
|
||||
it('should handle exception', test => {
|
||||
it('should handle exception', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({browserType, defaultBrowserOptions, tmpDir}) => {
|
||||
const e = new Error('Dummy');
|
||||
|
|
@ -209,8 +209,8 @@ it('should fire close event for a persistent context', async ({launchPersistent}
|
|||
expect(closed).toBe(true);
|
||||
});
|
||||
|
||||
it('coverage should work', test => {
|
||||
test.skip(!options.CHROMIUM);
|
||||
it('coverage should work', (test, parameters) => {
|
||||
test.skip(!options.CHROMIUM(parameters));
|
||||
}, async ({server, launchPersistent}) => {
|
||||
const {page} = await launchPersistent();
|
||||
await page.coverage.startJSCoverage();
|
||||
|
|
@ -221,8 +221,8 @@ it('coverage should work', test => {
|
|||
expect(coverage[0].functions.find(f => f.functionName === 'foo').ranges[0].count).toEqual(1);
|
||||
});
|
||||
|
||||
it('coverage should be missing', test => {
|
||||
test.skip(options.CHROMIUM);
|
||||
it('coverage should be missing', (test, parameters) => {
|
||||
test.skip(options.CHROMIUM(parameters));
|
||||
}, async ({launchPersistent}) => {
|
||||
const {page} = await launchPersistent();
|
||||
expect(page.coverage).toBe(null);
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ it('should dismiss the confirm prompt', async ({page}) => {
|
|||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should be able to close context with open alert', test => {
|
||||
test.fixme(options.WEBKIT && MAC);
|
||||
it('should be able to close context with open alert', (test, parameters) => {
|
||||
test.fixme(options.WEBKIT(parameters) && MAC);
|
||||
}, async ({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
|
|
|
|||
|
|
@ -127,8 +127,8 @@ it('should be atomic', async ({playwright, page}) => {
|
|||
expect(await page.evaluate(() => window['_clicked'])).toBe(true);
|
||||
});
|
||||
|
||||
it('should dispatch drag drop events', test => {
|
||||
test.fail(options.WEBKIT);
|
||||
it('should dispatch drag drop events', (test, parameters) => {
|
||||
test.fail(options.WEBKIT(parameters));
|
||||
}, async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/drag-n-drop.html');
|
||||
const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
|
||||
|
|
@ -141,8 +141,8 @@ it('should dispatch drag drop events', test => {
|
|||
}, {source, target})).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should dispatch drag drop events', test => {
|
||||
test.fail(options.WEBKIT);
|
||||
it('should dispatch drag drop events', (test, parameters) => {
|
||||
test.fail(options.WEBKIT(parameters));
|
||||
}, async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/drag-n-drop.html');
|
||||
const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ it('should create subdirectories when saving to non-existent user-specified path
|
|||
await page.close();
|
||||
});
|
||||
|
||||
it('should save when connected remotely', test => {
|
||||
it('should save when connected remotely', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({tmpDir, server, browserType, remoteServer}) => {
|
||||
const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() });
|
||||
|
|
@ -190,7 +190,7 @@ it('should error when saving after deletion', async ({tmpDir, browser, server})
|
|||
await page.close();
|
||||
});
|
||||
|
||||
it('should error when saving after deletion when connected remotely', test => {
|
||||
it('should error when saving after deletion when connected remotely', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({tmpDir, server, browserType, remoteServer}) => {
|
||||
const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() });
|
||||
|
|
@ -254,8 +254,8 @@ it(`should report download path within page.on('download', …) handler for Blob
|
|||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
it('should report alt-click downloads', test => {
|
||||
test.fixme(options.FIREFOX || options.WEBKIT);
|
||||
it('should report alt-click downloads', (test, parameters) => {
|
||||
test.fixme(options.FIREFOX(parameters) || options.WEBKIT(parameters));
|
||||
}, async ({browser, server}) => {
|
||||
// Firefox does not download on alt-click by default.
|
||||
// Our WebKit embedder does not download on alt-click, although Safari does.
|
||||
|
|
@ -277,8 +277,8 @@ it('should report alt-click downloads', test => {
|
|||
await page.close();
|
||||
});
|
||||
|
||||
it('should report new window downloads', test => {
|
||||
test.fixme(options.CHROMIUM && !options.HEADLESS);
|
||||
it('should report new window downloads', (test, parameters) => {
|
||||
test.fixme(options.CHROMIUM(parameters) && !options.HEADLESS);
|
||||
}, async ({browser, server}) => {
|
||||
// TODO: - the test fails in headful Chromium as the popup page gets closed along
|
||||
// with the session before download completed event arrives.
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ type TestState = {
|
|||
downloadsBrowser: Browser;
|
||||
persistentDownloadsContext: BrowserContext;
|
||||
};
|
||||
const fixtures = playwrightFixtures.extend<{}, TestState>();
|
||||
const { it, expect, registerFixture } = fixtures;
|
||||
const fixtures = playwrightFixtures.declareTestFixtures<TestState>();
|
||||
const { it, expect, defineTestFixture } = fixtures;
|
||||
|
||||
registerFixture('downloadsBrowser', async ({server, browserType, defaultBrowserOptions, tmpDir}, test) => {
|
||||
defineTestFixture('downloadsBrowser', async ({server, browserType, defaultBrowserOptions, tmpDir}, test) => {
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
|
||||
|
|
@ -43,7 +43,7 @@ registerFixture('downloadsBrowser', async ({server, browserType, defaultBrowserO
|
|||
await browser.close();
|
||||
});
|
||||
|
||||
registerFixture('persistentDownloadsContext', async ({server, browserType, defaultBrowserOptions, tmpDir}, test) => {
|
||||
defineTestFixture('persistentDownloadsContext', async ({server, browserType, defaultBrowserOptions, tmpDir}, test) => {
|
||||
const userDataDir = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-'));
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ const { it, expect, describe } = electronFixtures;
|
|||
import path from 'path';
|
||||
const electronName = process.platform === 'win32' ? 'electron.cmd' : 'electron';
|
||||
|
||||
describe('electron app', suite => {
|
||||
suite.skip(!options.CHROMIUM);
|
||||
describe('electron app', (suite, parameters) => {
|
||||
suite.skip(!options.CHROMIUM(parameters));
|
||||
}, () => {
|
||||
it('should fire close event', async ({ playwright }) => {
|
||||
const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName);
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ import { options } from '../playwright.fixtures';
|
|||
import { electronFixtures } from './electron.fixture';
|
||||
const { it, expect, describe } = electronFixtures;
|
||||
|
||||
describe('electron window', suite => {
|
||||
suite.skip(!options.CHROMIUM);
|
||||
describe('electron window', (suite, parameters) => {
|
||||
suite.skip(!options.CHROMIUM(parameters));
|
||||
}, () => {
|
||||
it('should click the button', async ({window, server}) => {
|
||||
await window.goto(server.PREFIX + '/input/button.html');
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@ type TestState = {
|
|||
window: ElectronPage;
|
||||
};
|
||||
|
||||
export const electronFixtures = playwrightFixtures.extend<{}, TestState>();
|
||||
const { registerFixture } = electronFixtures;
|
||||
export const electronFixtures = playwrightFixtures.declareTestFixtures<TestState>();
|
||||
const { defineTestFixture } = electronFixtures;
|
||||
|
||||
declare module '../../index' {
|
||||
const electron: ElectronLauncher;
|
||||
}
|
||||
|
||||
registerFixture('application', async ({playwright}, test) => {
|
||||
defineTestFixture('application', async ({playwright}, test) => {
|
||||
const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName);
|
||||
const application = await playwright.electron.launch(electronPath, {
|
||||
args: [path.join(__dirname, 'testApp.js')],
|
||||
|
|
@ -41,7 +41,7 @@ registerFixture('application', async ({playwright}, test) => {
|
|||
await application.close();
|
||||
});
|
||||
|
||||
registerFixture('window', async ({application}, test) => {
|
||||
defineTestFixture('window', async ({application}, test) => {
|
||||
const page = await application.newBrowserWindow({ width: 800, height: 600 });
|
||||
await test(page);
|
||||
await page.close();
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@
|
|||
import { it, expect, options } from './playwright.fixtures';
|
||||
|
||||
|
||||
it('should work', test => {
|
||||
test.fail(options.FIREFOX && !options.HEADLESS);
|
||||
it('should work', (test, parameters) => {
|
||||
test.fail(options.FIREFOX(parameters) && !options.HEADLESS);
|
||||
}, async ({ page, server }) => {
|
||||
await page.setViewportSize({ width: 500, height: 500 });
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
|
|
@ -67,8 +67,8 @@ it('should work with SVG nodes', async ({ page, server }) => {
|
|||
expect(pwBoundingBox).toEqual(webBoundingBox);
|
||||
});
|
||||
|
||||
it('should work with page scale', test => {
|
||||
test.skip(options.FIREFOX);
|
||||
it('should work with page scale', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters));
|
||||
}, async ({ browser, server }) => {
|
||||
const context = await browser.newContext({ viewport: { width: 400, height: 400 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ it('should work for cross-process iframes', async ({ page, server }) => {
|
|||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
});
|
||||
|
||||
it('should work for document', test => {
|
||||
test.flaky(WIN && options.WEBKIT);
|
||||
it('should work for document', (test, parameters) => {
|
||||
test.flaky(WIN && options.WEBKIT(parameters));
|
||||
}, async ({ page, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
|
|
|
|||
|
|
@ -22,11 +22,8 @@ import {PNG} from 'pngjs';
|
|||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
// Firefox headful produces a different image.
|
||||
const ffheadful = options.FIREFOX && !options.HEADLESS;
|
||||
|
||||
describe('element screenshot', suite => {
|
||||
suite.skip(ffheadful);
|
||||
describe('element screenshot', (suite, parameters) => {
|
||||
suite.skip(parameters.browserName === 'firefox' && !options.HEADLESS);
|
||||
}, () => {
|
||||
it('should work', async ({page, server, golden}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
|
|
@ -214,8 +211,8 @@ describe('element screenshot', suite => {
|
|||
expect(screenshot).toMatchImage(golden('screenshot-element-fractional.png'));
|
||||
});
|
||||
|
||||
it('should work with a mobile viewport', test => {
|
||||
test.skip(options.FIREFOX);
|
||||
it('should work with a mobile viewport', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters));
|
||||
}, async ({browser, server, golden}) => {
|
||||
const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true});
|
||||
const page = await context.newPage();
|
||||
|
|
@ -227,8 +224,8 @@ describe('element screenshot', suite => {
|
|||
await context.close();
|
||||
});
|
||||
|
||||
it('should work with device scale factor', test => {
|
||||
test.skip(options.FIREFOX);
|
||||
it('should work with device scale factor', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters));
|
||||
}, async ({browser, server, golden}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 320, height: 480 }, deviceScaleFactor: 2 });
|
||||
const page = await context.newPage();
|
||||
|
|
@ -292,7 +289,7 @@ describe('element screenshot', suite => {
|
|||
await context.close();
|
||||
});
|
||||
|
||||
it('should restore viewport after page screenshot and exception', test => {
|
||||
it('should restore viewport after page screenshot and exception', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({ browser, server }) => {
|
||||
const context = await browser.newContext({ viewport: { width: 350, height: 360 } });
|
||||
|
|
@ -305,7 +302,7 @@ describe('element screenshot', suite => {
|
|||
await context.close();
|
||||
});
|
||||
|
||||
it('should restore viewport after page screenshot and timeout', test => {
|
||||
it('should restore viewport after page screenshot and timeout', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({ browser, server }) => {
|
||||
const context = await browser.newContext({ viewport: { width: 350, height: 360 } });
|
||||
|
|
@ -351,9 +348,9 @@ describe('element screenshot', suite => {
|
|||
await context.close();
|
||||
});
|
||||
|
||||
it('should restore viewport after element screenshot and exception', test => {
|
||||
it('should restore viewport after element screenshot and exception', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({server, browser}) => {
|
||||
}, async ({browser}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 350, height: 360 } });
|
||||
const page = await context.newPage();
|
||||
await page.setContent(`<div style="width:600px;height:600px;"></div>`);
|
||||
|
|
@ -365,8 +362,8 @@ describe('element screenshot', suite => {
|
|||
await context.close();
|
||||
});
|
||||
|
||||
it('should wait for element to stop moving', test => {
|
||||
test.flaky(options.WEBKIT && !options.HEADLESS && LINUX);
|
||||
it('should wait for element to stop moving', (test, parameters) => {
|
||||
test.flaky(options.WEBKIT(parameters) && !options.HEADLESS && LINUX);
|
||||
}, async ({ page, server, golden }) => {
|
||||
await page.setViewportSize({ width: 500, height: 500 });
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
it('should select textarea', async ({ page, server }) => {
|
||||
it('should select textarea', async ({ page, server, isFirefox }) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
const textarea = await page.$('textarea');
|
||||
await textarea.evaluate(textarea => textarea.value = 'some value');
|
||||
await textarea.selectText();
|
||||
if (options.FIREFOX) {
|
||||
if (isFirefox) {
|
||||
expect(await textarea.evaluate(el => el.selectionStart)).toBe(0);
|
||||
expect(await textarea.evaluate(el => el.selectionEnd)).toBe(10);
|
||||
} else {
|
||||
|
|
@ -30,12 +30,12 @@ it('should select textarea', async ({ page, server }) => {
|
|||
}
|
||||
});
|
||||
|
||||
it('should select input', async ({ page, server }) => {
|
||||
it('should select input', async ({ page, server, isFirefox }) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
const input = await page.$('input');
|
||||
await input.evaluate(input => input.value = 'some value');
|
||||
await input.selectText();
|
||||
if (options.FIREFOX) {
|
||||
if (isFirefox) {
|
||||
expect(await input.evaluate(el => el.selectionStart)).toBe(0);
|
||||
expect(await input.evaluate(el => el.selectionEnd)).toBe(10);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -114,8 +114,8 @@ it('should wait for disabled button', async ({page}) => {
|
|||
await promise;
|
||||
});
|
||||
|
||||
it('should wait for stable position', test => {
|
||||
test.fixme(options.FIREFOX && LINUX);
|
||||
it('should wait for stable position', (test, parameters) => {
|
||||
test.fixme(options.FIREFOX(parameters) && LINUX);
|
||||
}, async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
|
|
|
|||
|
|
@ -101,8 +101,8 @@ it('should change document.activeElement', async ({page, server}) => {
|
|||
expect(active).toEqual(['INPUT', 'TEXTAREA']);
|
||||
});
|
||||
|
||||
it('should not affect screenshots', test => {
|
||||
test.skip(options.FIREFOX && !options.HEADLESS);
|
||||
it('should not affect screenshots', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters) && !options.HEADLESS);
|
||||
}, async ({page, server, golden}) => {
|
||||
// Firefox headful produces a different image.
|
||||
const page2 = await page.context().newPage();
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@
|
|||
*/
|
||||
import { it, expect, options } from '../playwright.fixtures';
|
||||
|
||||
it('should pass firefox user preferences', test => {
|
||||
test.skip(!options.FIREFOX);
|
||||
it('should pass firefox user preferences', (test, parameters) => {
|
||||
test.skip(!options.FIREFOX(parameters));
|
||||
}, async ({browserType, defaultBrowserOptions}) => {
|
||||
const browser = await browserType.launch({
|
||||
...defaultBrowserOptions,
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ export type FixturesFixtures = {
|
|||
stallingConnectedRemoteServer: RemoteServer;
|
||||
};
|
||||
|
||||
const fixturesFixtures = serverFixtures.extend<{}, FixturesFixtures>();
|
||||
const { it, describe, expect, registerFixture } = fixturesFixtures;
|
||||
const fixturesFixtures = serverFixtures.declareTestFixtures<FixturesFixtures>();
|
||||
const { it, describe, expect, defineTestFixture } = fixturesFixtures;
|
||||
|
||||
registerFixture('connectedRemoteServer', async ({browserType, remoteServer, server}, test) => {
|
||||
defineTestFixture('connectedRemoteServer', async ({browserType, remoteServer, server}, test) => {
|
||||
const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() });
|
||||
const page = await browser.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
|
@ -37,7 +37,7 @@ registerFixture('connectedRemoteServer', async ({browserType, remoteServer, serv
|
|||
await browser.close();
|
||||
});
|
||||
|
||||
registerFixture('stallingConnectedRemoteServer', async ({browserType, stallingRemoteServer, server}, test) => {
|
||||
defineTestFixture('stallingConnectedRemoteServer', async ({browserType, stallingRemoteServer, server}, test) => {
|
||||
const browser = await browserType.connect({ wsEndpoint: stallingRemoteServer.wsEndpoint() });
|
||||
const page = await browser.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
|
@ -45,7 +45,7 @@ registerFixture('stallingConnectedRemoteServer', async ({browserType, stallingRe
|
|||
await browser.close();
|
||||
});
|
||||
|
||||
it('should close the browser when the node process closes', test => {
|
||||
it('should close the browser when the node process closes', (test, parameters) => {
|
||||
test.slow();
|
||||
}, async ({connectedRemoteServer}) => {
|
||||
if (WIN)
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
|
||||
it('should work', test => {
|
||||
test.skip(options.FIREFOX);
|
||||
it('should work', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters));
|
||||
}, async function({page}) {
|
||||
await page.setContent(`<div id=d1 tabIndex=0></div>`);
|
||||
expect(await page.evaluate(() => document.activeElement.nodeName)).toBe('BODY');
|
||||
|
|
@ -78,8 +78,8 @@ it('should traverse focus in all directions', async function({page}) {
|
|||
expect(await page.evaluate(() => (document.activeElement as HTMLInputElement).value)).toBe('1');
|
||||
});
|
||||
|
||||
it('should traverse only form elements', test => {
|
||||
test.skip(!MAC || !options.WEBKIT,
|
||||
it('should traverse only form elements', (test, parameters) => {
|
||||
test.skip(!MAC || !options.WEBKIT(parameters),
|
||||
'Chromium and WebKit both have settings for tab traversing all links, but it is only on by default in WebKit.');
|
||||
}, async function({page}) {
|
||||
await page.setContent(`
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ it('should have correct execution contexts', async ({ page, server }) => {
|
|||
expect(await page.frames()[1].evaluate(() => document.body.textContent.trim())).toBe(`Hi, I'm frame`);
|
||||
});
|
||||
|
||||
function expectContexts(pageImpl, count) {
|
||||
if (options.CHROMIUM)
|
||||
function expectContexts(pageImpl, count, isChromium) {
|
||||
if (isChromium)
|
||||
expect(pageImpl._delegate._mainFrameSession._contextIdToContext.size).toBe(count);
|
||||
else
|
||||
expect(pageImpl._delegate._contextIdToContext.size).toBe(count);
|
||||
|
|
@ -45,22 +45,22 @@ function expectContexts(pageImpl, count) {
|
|||
|
||||
it('should dispose context on navigation', test => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({ page, server, toImpl }) => {
|
||||
}, async ({ page, server, toImpl, isChromium }) => {
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expectContexts(toImpl(page), 4);
|
||||
expectContexts(toImpl(page), 4, isChromium);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expectContexts(toImpl(page), 2);
|
||||
expectContexts(toImpl(page), 2, isChromium);
|
||||
});
|
||||
|
||||
it('should dispose context on cross-origin navigation', test => {
|
||||
it('should dispose context on cross-origin navigation', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({ page, server, toImpl }) => {
|
||||
}, async ({ page, server, toImpl, isChromium }) => {
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expectContexts(toImpl(page), 4);
|
||||
expectContexts(toImpl(page), 4, isChromium);
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
expectContexts(toImpl(page), 2);
|
||||
expectContexts(toImpl(page), 2, isChromium);
|
||||
});
|
||||
|
||||
it('should execute after cross-site navigation', async ({ page, server }) => {
|
||||
|
|
@ -131,9 +131,9 @@ it('should be isolated between frames', async ({page, server}) => {
|
|||
expect(a2).toBe(2);
|
||||
});
|
||||
|
||||
it('should work in iframes that failed initial navigation', test => {
|
||||
test.fail(options.CHROMIUM);
|
||||
test.fixme(options.FIREFOX);
|
||||
it('should work in iframes that failed initial navigation', (test, parameters) => {
|
||||
test.fail(options.CHROMIUM(parameters));
|
||||
test.fixme(options.FIREFOX(parameters));
|
||||
}, async ({page}) => {
|
||||
// - Firefox does not report domcontentloaded for the iframe.
|
||||
// - Chromium and Firefox report empty url.
|
||||
|
|
@ -155,8 +155,8 @@ it('should work in iframes that failed initial navigation', test => {
|
|||
expect(await page.frames()[1].$('div')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should work in iframes that interrupted initial javascript url navigation', test => {
|
||||
test.fixme(options.CHROMIUM);
|
||||
it('should work in iframes that interrupted initial javascript url navigation', (test, parameters) => {
|
||||
test.fixme(options.CHROMIUM(parameters));
|
||||
}, async ({page, server}) => {
|
||||
// Chromium does not report isolated world for the iframe.
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
|
|
|||
|
|
@ -172,8 +172,8 @@ it('should report different frame instance when frame re-attaches', async ({page
|
|||
expect(frame1).not.toBe(frame2);
|
||||
});
|
||||
|
||||
it('should refuse to display x-frame-options:deny iframe', test => {
|
||||
test.fixme(options.FIREFOX);
|
||||
it('should refuse to display x-frame-options:deny iframe', (test, parameters) => {
|
||||
test.fixme(options.FIREFOX(parameters));
|
||||
}, async ({page, server}) => {
|
||||
server.setRoute('/x-frame-options-deny.html', async (req, res) => {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ it('should have default url when launching browser', async ({browserType, defaul
|
|||
await browserContext.close();
|
||||
});
|
||||
|
||||
it('headless should be able to read cookies written by headful', test => {
|
||||
test.fail(WIN && options.CHROMIUM);
|
||||
test.flaky(options.FIREFOX);
|
||||
it('headless should be able to read cookies written by headful', (test, parameters) => {
|
||||
test.fail(WIN && options.CHROMIUM(parameters));
|
||||
test.flaky(options.FIREFOX(parameters));
|
||||
test.slow();
|
||||
}, async ({browserType, defaultBrowserOptions, server}) => {
|
||||
// see https://github.com/microsoft/playwright/issues/717
|
||||
|
|
@ -50,7 +50,7 @@ it('headless should be able to read cookies written by headful', test => {
|
|||
expect(cookie).toBe('foo=true');
|
||||
});
|
||||
|
||||
it('should close browser with beforeunload page', test => {
|
||||
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});
|
||||
|
|
@ -94,7 +94,7 @@ it('should close browser after context menu was triggered', async ({browserType,
|
|||
await browser.close();
|
||||
});
|
||||
|
||||
it('should(not) block third party cookies', async ({browserType, defaultBrowserOptions, server}) => {
|
||||
it('should(not) block third party cookies', async ({browserType, defaultBrowserOptions, server, isChromium, isFirefox}) => {
|
||||
const browser = await browserType.launch({...defaultBrowserOptions, headless: false });
|
||||
const page = await browser.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
|
@ -112,7 +112,7 @@ it('should(not) block third party cookies', async ({browserType, defaultBrowserO
|
|||
return document.cookie;
|
||||
});
|
||||
await page.waitForTimeout(2000);
|
||||
const allowsThirdParty = options.CHROMIUM || options.FIREFOX;
|
||||
const allowsThirdParty = isChromium || isFirefox;
|
||||
expect(documentCookie).toBe(allowsThirdParty ? 'username=John Doe' : '');
|
||||
const cookies = await page.context().cookies(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
if (allowsThirdParty) {
|
||||
|
|
@ -134,8 +134,8 @@ it('should(not) block third party cookies', async ({browserType, defaultBrowserO
|
|||
await browser.close();
|
||||
});
|
||||
|
||||
it('should not override viewport size when passed null', test => {
|
||||
test.fixme(options.WEBKIT);
|
||||
it('should not override viewport size when passed null', (test, parameters) => {
|
||||
test.fixme(options.WEBKIT(parameters));
|
||||
}, async function({browserType, defaultBrowserOptions, server}) {
|
||||
// Our WebKit embedder does not respect window features.
|
||||
const browser = await browserType.launch({...defaultBrowserOptions, headless: false });
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
it('should work for primitives', async ({page}) => {
|
||||
const numberHandle = await page.evaluateHandle(() => 2);
|
||||
|
|
@ -36,7 +36,7 @@ it('should work for promises', async ({page}) => {
|
|||
expect(bHandle.toString()).toBe('JSHandle@promise');
|
||||
});
|
||||
|
||||
it('should work with different subtypes', async ({page}) => {
|
||||
it('should work with different subtypes', async ({page, isWebKit}) => {
|
||||
expect((await page.evaluateHandle('(function(){})')).toString()).toBe('JSHandle@function');
|
||||
expect((await page.evaluateHandle('12')).toString()).toBe('JSHandle@12');
|
||||
expect((await page.evaluateHandle('true')).toString()).toBe('JSHandle@true');
|
||||
|
|
@ -54,6 +54,6 @@ it('should work with different subtypes', async ({page}) => {
|
|||
expect((await page.evaluateHandle('new WeakSet()')).toString()).toBe('JSHandle@weakset');
|
||||
expect((await page.evaluateHandle('new Error()')).toString()).toBe('JSHandle@error');
|
||||
// TODO(yurys): change subtype from array to typedarray in WebKit.
|
||||
expect((await page.evaluateHandle('new Int32Array()')).toString()).toBe(options.WEBKIT ? 'JSHandle@array' : 'JSHandle@typedarray');
|
||||
expect((await page.evaluateHandle('new Int32Array()')).toString()).toBe(isWebKit ? 'JSHandle@array' : 'JSHandle@typedarray');
|
||||
expect((await page.evaluateHandle('new Proxy({}, {})')).toString()).toBe('JSHandle@proxy');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -83,8 +83,8 @@ it('insertText should only emit input event', async ({page, server}) => {
|
|||
expect(await events.jsonValue()).toEqual(['input']);
|
||||
});
|
||||
|
||||
it('should report shiftKey', test => {
|
||||
test.fail(options.FIREFOX && MAC);
|
||||
it('should report shiftKey', (test, parameters) => {
|
||||
test.fail(options.FIREFOX(parameters) && MAC);
|
||||
}, async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/keyboard.html');
|
||||
const keyboard = page.keyboard;
|
||||
|
|
@ -342,7 +342,7 @@ it('should be able to prevent selectAll', async ({page, server}) => {
|
|||
expect(await page.$eval('textarea', textarea => textarea.value)).toBe('some tex');
|
||||
});
|
||||
|
||||
it('should support MacOS shortcuts', test => {
|
||||
it('should support MacOS shortcuts', (test, parameters) => {
|
||||
test.skip(!MAC);
|
||||
}, async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
|
|
@ -354,21 +354,21 @@ it('should support MacOS shortcuts', test => {
|
|||
expect(await page.$eval('textarea', textarea => textarea.value)).toBe('some ');
|
||||
});
|
||||
|
||||
it('should press the meta key', async ({page}) => {
|
||||
it('should press the meta key', async ({page, isFirefox}) => {
|
||||
const lastEvent = await captureLastKeydown(page);
|
||||
await page.keyboard.press('Meta');
|
||||
const {key, code, metaKey} = await lastEvent.jsonValue();
|
||||
if (options.FIREFOX && !MAC)
|
||||
if (isFirefox && !MAC)
|
||||
expect(key).toBe('OS');
|
||||
else
|
||||
expect(key).toBe('Meta');
|
||||
|
||||
if (options.FIREFOX)
|
||||
if (isFirefox)
|
||||
expect(code).toBe('OSLeft');
|
||||
else
|
||||
expect(code).toBe('MetaLeft');
|
||||
|
||||
if (options.FIREFOX && !MAC)
|
||||
if (isFirefox && !MAC)
|
||||
expect(metaKey).toBe(false);
|
||||
else
|
||||
expect(metaKey).toBe(true);
|
||||
|
|
@ -384,9 +384,9 @@ it('should work after a cross origin navigation', async ({page, server}) => {
|
|||
});
|
||||
|
||||
// event.keyIdentifier has been removed from all browsers except WebKit
|
||||
it('should expose keyIdentifier in webkit', test => {
|
||||
test.skip(!options.WEBKIT);
|
||||
}, async ({page, server}) => {
|
||||
it('should expose keyIdentifier in webkit', (test, parameters) => {
|
||||
test.skip(!options.WEBKIT(parameters));
|
||||
}, async ({page}) => {
|
||||
const lastEvent = await captureLastKeydown(page);
|
||||
const keyMap = {
|
||||
'ArrowUp': 'Up',
|
||||
|
|
|
|||
|
|
@ -27,10 +27,9 @@ function dimensions() {
|
|||
};
|
||||
}
|
||||
|
||||
it('should click the document', test => {
|
||||
test.flaky(options.FIREFOX && WIN);
|
||||
it('should click the document', (test, parameters) => {
|
||||
test.flaky(options.FIREFOX(parameters) && WIN, 'Occasionally times out on options.FIREFOX on Windows: https://github.com/microsoft/playwright/pull/1911/checks?check_run_id=607149016');
|
||||
}, async ({page, server}) => {
|
||||
// Occasionally times out on options.FIREFOX on Windows: https://github.com/microsoft/playwright/pull/1911/checks?check_run_id=607149016
|
||||
await page.evaluate(() => {
|
||||
window['clickPromise'] = new Promise(resolve => {
|
||||
document.addEventListener('click', event => {
|
||||
|
|
@ -124,12 +123,12 @@ it('should trigger hover state with removed window.Node', async ({page, server})
|
|||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
||||
});
|
||||
|
||||
it('should set modifier keys on click', async ({page, server}) => {
|
||||
it('should set modifier keys on click', async ({page, server, isFirefox}) => {
|
||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||
await page.evaluate(() => document.querySelector('#button-3').addEventListener('mousedown', e => window['lastEvent'] = e, true));
|
||||
const modifiers = {'Shift': 'shiftKey', 'Control': 'ctrlKey', 'Alt': 'altKey', 'Meta': 'metaKey'};
|
||||
// In Firefox, the Meta modifier only exists on Mac
|
||||
if (options.FIREFOX && !MAC)
|
||||
if (isFirefox && !MAC)
|
||||
delete modifiers['Meta'];
|
||||
for (const modifier in modifiers) {
|
||||
await page.keyboard.down(modifier);
|
||||
|
|
@ -145,9 +144,9 @@ it('should set modifier keys on click', async ({page, server}) => {
|
|||
}
|
||||
});
|
||||
|
||||
it('should tween mouse movement', async ({page}) => {
|
||||
it('should tween mouse movement', async ({page, isWebKit}) => {
|
||||
// The test becomes flaky on WebKit without next line.
|
||||
if (options.WEBKIT)
|
||||
if (isWebKit)
|
||||
await page.evaluate(() => new Promise(requestAnimationFrame));
|
||||
await page.mouse.move(100, 100);
|
||||
await page.evaluate(() => {
|
||||
|
|
@ -166,8 +165,8 @@ it('should tween mouse movement', async ({page}) => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('should work with mobile viewports and cross process navigations', test => {
|
||||
test.skip(options.FIREFOX);
|
||||
it('should work with mobile viewports and cross process navigations', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters));
|
||||
}, async ({browser, server}) => {
|
||||
// @see https://crbug.com/929806
|
||||
const context = await browser.newContext({ viewport: {width: 360, height: 640}, isMobile: true });
|
||||
|
|
|
|||
|
|
@ -45,18 +45,18 @@ it('should work for fetch requests', async ({page, server}) => {
|
|||
expect(requests[0].frame()).toBe(page.mainFrame());
|
||||
});
|
||||
|
||||
it('should return headers', async ({page, server}) => {
|
||||
it('should return headers', async ({page, server, isChromium, isFirefox, isWebKit}) => {
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
if (options.CHROMIUM)
|
||||
if (isChromium)
|
||||
expect(response.request().headers()['user-agent']).toContain('Chrome');
|
||||
else if (options.FIREFOX)
|
||||
else if (isFirefox)
|
||||
expect(response.request().headers()['user-agent']).toContain('Firefox');
|
||||
else if (options.WEBKIT)
|
||||
else if (isWebKit)
|
||||
expect(response.request().headers()['user-agent']).toContain('WebKit');
|
||||
});
|
||||
|
||||
it('should get the same headers as the server', test => {
|
||||
test.fail(options.CHROMIUM || options.WEBKIT);
|
||||
it('should get the same headers as the server', (test, parameters) => {
|
||||
test.fail(options.CHROMIUM(parameters) || options.WEBKIT(parameters));
|
||||
}, async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
let serverRequest;
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ it('should work with a path', async ({page, server}) => {
|
|||
expect(await page.evaluate(() => window['__injected'])).toBe(42);
|
||||
});
|
||||
|
||||
it('should include sourceURL when path is provided', test => {
|
||||
test.skip(options.WEBKIT);
|
||||
it('should include sourceURL when path is provided', (test, parameters) => {
|
||||
test.skip(options.WEBKIT(parameters));
|
||||
}, async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.addScriptTag({ path: path.join(__dirname, 'assets/injectedfile.js') });
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ it('should not be visible in context.pages', async ({context}) => {
|
|||
expect(context.pages()).not.toContain(newPage);
|
||||
});
|
||||
|
||||
it('should run beforeunload if asked for', async ({context, server}) => {
|
||||
it('should run beforeunload if asked for', async ({context, server, isChromium, isWebKit}) => {
|
||||
const newPage = await context.newPage();
|
||||
await newPage.goto(server.PREFIX + '/beforeunload.html');
|
||||
// We have to interact with a page so that 'beforeunload' handlers
|
||||
|
|
@ -44,9 +44,9 @@ it('should run beforeunload if asked for', async ({context, server}) => {
|
|||
const dialog = await newPage.waitForEvent('dialog');
|
||||
expect(dialog.type()).toBe('beforeunload');
|
||||
expect(dialog.defaultValue()).toBe('');
|
||||
if (options.CHROMIUM)
|
||||
if (isChromium)
|
||||
expect(dialog.message()).toBe('');
|
||||
else if (options.WEBKIT)
|
||||
else if (isWebKit)
|
||||
expect(dialog.message()).toBe('Leave?');
|
||||
else
|
||||
expect(dialog.message()).toBe('This page is asking you to confirm that you want to leave - data you have entered may not be saved.');
|
||||
|
|
@ -197,7 +197,7 @@ it('page.frame should respect url', async function({page, server}) {
|
|||
expect(page.frame({ url: /empty/ }).url()).toBe(server.EMPTY_PAGE);
|
||||
});
|
||||
|
||||
it('should have sane user agent', async ({page}) => {
|
||||
it('should have sane user agent', async ({page, isChromium, isFirefox}) => {
|
||||
const userAgent = await page.evaluate(() => navigator.userAgent);
|
||||
const [
|
||||
part1,
|
||||
|
|
@ -211,7 +211,7 @@ it('should have sane user agent', async ({page}) => {
|
|||
// Second part in parenthesis is platform - ignore it.
|
||||
|
||||
// Third part for Firefox is the last one and encodes engine and browser versions.
|
||||
if (options.FIREFOX) {
|
||||
if (isFirefox) {
|
||||
const [engine, browser] = part3.split(' ');
|
||||
expect(engine.startsWith('Gecko')).toBe(true);
|
||||
expect(browser.startsWith('Firefox')).toBe(true);
|
||||
|
|
@ -225,7 +225,7 @@ it('should have sane user agent', async ({page}) => {
|
|||
// 5th part encodes real browser name and engine version.
|
||||
const [engine, browser] = part5.split(' ');
|
||||
expect(browser.startsWith('Safari/')).toBe(true);
|
||||
if (options.CHROMIUM)
|
||||
if (isChromium)
|
||||
expect(engine.includes('Chrome/')).toBe(true);
|
||||
else
|
||||
expect(engine.startsWith('Version/')).toBe(true);
|
||||
|
|
@ -252,8 +252,8 @@ it('frame.press should work', async ({page, server}) => {
|
|||
expect(await frame.evaluate(() => document.querySelector('textarea').value)).toBe('a');
|
||||
});
|
||||
|
||||
it('frame.focus should work multiple times', test => {
|
||||
test.fail(options.FIREFOX);
|
||||
it('frame.focus should work multiple times', (test, parameters) => {
|
||||
test.fail(options.FIREFOX(parameters));
|
||||
}, async ({ context, server }) => {
|
||||
const page1 = await context.newPage();
|
||||
const page2 = await context.newPage();
|
||||
|
|
|
|||
|
|
@ -415,8 +415,8 @@ it('should not throw an error when evaluation does a navigation', async ({ page,
|
|||
expect(result).toEqual([42]);
|
||||
});
|
||||
|
||||
it('should not throw an error when evaluation does a synchronous navigation and returns an object', test => {
|
||||
test.fixme(options.WEBKIT);
|
||||
it('should not throw an error when evaluation does a synchronous navigation and returns an object', (test, parameters) => {
|
||||
test.fixme(options.WEBKIT(parameters));
|
||||
}, async ({ page, server }) => {
|
||||
// It is imporant to be on about:blank for sync reload.
|
||||
const result = await page.evaluate(() => {
|
||||
|
|
@ -435,7 +435,7 @@ it('should not throw an error when evaluation does a synchronous navigation and
|
|||
expect(result).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should transfer 100Mb of data from page to node.js', test => {
|
||||
it('should transfer 100Mb of data from page to node.js', (test, parameters) => {
|
||||
test.skip(options.WIRE);
|
||||
}, async ({ page }) => {
|
||||
// This is too slow with wire.
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ function crash(pageImpl, browserName) {
|
|||
pageImpl._delegate._session.send('Page.crash', {}).catch(e => {});
|
||||
}
|
||||
|
||||
describe('', suite => {
|
||||
describe('', (suite, parameters) => {
|
||||
suite.fixme(options.WIRE);
|
||||
suite.flaky(options.FIREFOX && WIN);
|
||||
suite.flaky(options.FIREFOX(parameters) && WIN);
|
||||
}, () => {
|
||||
it('should emit crash event when page crashes', async ({page, browserName, toImpl}) => {
|
||||
await page.setContent(`<div>This page should crash</div>`);
|
||||
|
|
@ -63,9 +63,9 @@ describe('', suite => {
|
|||
expect(error.message).toContain('Navigation failed because page crashed');
|
||||
});
|
||||
|
||||
it('should be able to close context when page crashes', test => {
|
||||
it('should be able to close context when page crashes', (test, parameters) => {
|
||||
test.fixme(options.WIRE);
|
||||
test.flaky(options.FIREFOX && WIN);
|
||||
test.flaky(options.FIREFOX(parameters) && WIN);
|
||||
}, async ({page, browserName, toImpl}) => {
|
||||
await page.setContent(`<div>This page should crash</div>`);
|
||||
crash(toImpl(page), browserName);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
it('Page.Events.Request', async ({page, server}) => {
|
||||
const requests = [];
|
||||
|
|
@ -41,7 +41,7 @@ it('Page.Events.Response', async ({page, server}) => {
|
|||
expect(responses[0].request()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Page.Events.RequestFailed', async ({page, server}) => {
|
||||
it('Page.Events.RequestFailed', async ({page, server, isChromium, isWebKit}) => {
|
||||
server.setRoute('/one-style.css', (req, res) => {
|
||||
res.setHeader('Content-Type', 'text/css');
|
||||
res.connection.destroy();
|
||||
|
|
@ -53,9 +53,9 @@ it('Page.Events.RequestFailed', async ({page, server}) => {
|
|||
expect(failedRequests[0].url()).toContain('one-style.css');
|
||||
expect(await failedRequests[0].response()).toBe(null);
|
||||
expect(failedRequests[0].resourceType()).toBe('stylesheet');
|
||||
if (options.CHROMIUM) {
|
||||
if (isChromium) {
|
||||
expect(failedRequests[0].failure().errorText).toBe('net::ERR_EMPTY_RESPONSE');
|
||||
} else if (options.WEBKIT) {
|
||||
} else if (isWebKit) {
|
||||
if (MAC)
|
||||
expect(failedRequests[0].failure().errorText).toBe('The network connection was lost.');
|
||||
else if (WIN)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
|
||||
it('should fire', async ({page, server}) => {
|
||||
it('should fire', async ({page, server, isWebKit}) => {
|
||||
const [error] = await Promise.all([
|
||||
page.waitForEvent('pageerror'),
|
||||
page.goto(server.PREFIX + '/error.html'),
|
||||
|
|
@ -26,13 +26,13 @@ it('should fire', async ({page, server}) => {
|
|||
expect(error.message).toBe('Fancy error!');
|
||||
let stack = await page.evaluate(() => window['e'].stack);
|
||||
// Note that WebKit reports the stack of the 'throw' statement instead of the Error constructor call.
|
||||
if (options.WEBKIT)
|
||||
if (isWebKit)
|
||||
stack = stack.replace('14:25', '15:19');
|
||||
expect(error.stack).toBe(stack);
|
||||
});
|
||||
|
||||
it('should contain sourceURL', test => {
|
||||
test.fail(options.WEBKIT);
|
||||
it('should contain sourceURL', (test, parameters) => {
|
||||
test.fail(options.WEBKIT(parameters));
|
||||
}, async ({page, server}) => {
|
||||
const [error] = await Promise.all([
|
||||
page.waitForEvent('pageerror'),
|
||||
|
|
@ -41,7 +41,7 @@ it('should contain sourceURL', test => {
|
|||
expect(error.stack).toContain('myscript.js');
|
||||
});
|
||||
|
||||
it('should handle odd values', async ({page}) => {
|
||||
it('should handle odd values', async ({page, isFirefox}) => {
|
||||
const cases = [
|
||||
[null, 'null'],
|
||||
[undefined, 'undefined'],
|
||||
|
|
@ -53,28 +53,28 @@ it('should handle odd values', async ({page}) => {
|
|||
page.waitForEvent('pageerror'),
|
||||
page.evaluate(value => setTimeout(() => { throw value; }, 0), value),
|
||||
]);
|
||||
expect(error.message).toBe(options.FIREFOX ? 'uncaught exception: ' + message : message);
|
||||
expect(error.message).toBe(isFirefox ? 'uncaught exception: ' + message : message);
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle object', test => {
|
||||
test.fixme(options.FIREFOX);
|
||||
}, async ({page}) => {
|
||||
it('should handle object', (test, parameters) => {
|
||||
test.fixme(options.FIREFOX(parameters));
|
||||
}, async ({page, isChromium}) => {
|
||||
// Firefox just does not report this error.
|
||||
const [error] = await Promise.all([
|
||||
page.waitForEvent('pageerror'),
|
||||
page.evaluate(() => setTimeout(() => { throw {}; }, 0)),
|
||||
]);
|
||||
expect(error.message).toBe(options.CHROMIUM ? 'Object' : '[object Object]');
|
||||
expect(error.message).toBe(isChromium ? 'Object' : '[object Object]');
|
||||
});
|
||||
|
||||
it('should handle window', test => {
|
||||
test.fixme(options.FIREFOX);
|
||||
}, async ({page}) => {
|
||||
it('should handle window', (test, parameters) => {
|
||||
test.fixme(options.FIREFOX(parameters));
|
||||
}, async ({page, isChromium}) => {
|
||||
// Firefox just does not report this error.
|
||||
const [error] = await Promise.all([
|
||||
page.waitForEvent('pageerror'),
|
||||
page.evaluate(() => setTimeout(() => { throw window; }, 0)),
|
||||
]);
|
||||
expect(error.message).toBe(options.CHROMIUM ? 'Window' : '[object Window]');
|
||||
expect(error.message).toBe(isChromium ? 'Window' : '[object Window]');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ it('should fill date input after clicking', async ({page, server}) => {
|
|||
expect(await page.$eval('input', input => input.value)).toBe('2020-03-02');
|
||||
});
|
||||
|
||||
it('should throw on incorrect date', test => {
|
||||
test.skip(options.WEBKIT);
|
||||
it('should throw on incorrect date', (test, parameters) => {
|
||||
test.skip(options.WEBKIT(parameters));
|
||||
}, async ({page}) => {
|
||||
await page.setContent('<input type=date>');
|
||||
const error = await page.fill('input', '2020-13-05').catch(e => e);
|
||||
|
|
@ -74,8 +74,8 @@ it('should fill time input', async ({page, server}) => {
|
|||
expect(await page.$eval('input', input => input.value)).toBe('13:15');
|
||||
});
|
||||
|
||||
it('should throw on incorrect time', test => {
|
||||
test.skip(options.WEBKIT);
|
||||
it('should throw on incorrect time', (test, parameters) => {
|
||||
test.skip(options.WEBKIT(parameters));
|
||||
}, async ({page}) => {
|
||||
await page.setContent('<input type=time>');
|
||||
const error = await page.fill('input', '25:05').catch(e => e);
|
||||
|
|
@ -88,8 +88,8 @@ it('should fill datetime-local input', async ({page, server}) => {
|
|||
expect(await page.$eval('input', input => input.value)).toBe('2020-03-02T05:15');
|
||||
});
|
||||
|
||||
it('should throw on incorrect datetime-local', test => {
|
||||
test.skip(options.WEBKIT || options.FIREFOX);
|
||||
it('should throw on incorrect datetime-local', (test, parameters) => {
|
||||
test.skip(options.WEBKIT(parameters) || options.FIREFOX(parameters));
|
||||
}, async ({page, server}) => {
|
||||
await page.setContent('<input type=datetime-local>');
|
||||
const error = await page.fill('input', 'abc').catch(e => e);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
import utils from './utils';
|
||||
import path from 'path';
|
||||
|
|
@ -134,7 +134,7 @@ it('should work with subframes return 204 with domcontentloaded', async ({page,
|
|||
await page.goto(server.PREFIX + '/frames/one-frame.html', { waitUntil: 'domcontentloaded' });
|
||||
});
|
||||
|
||||
it('should fail when server returns 204', async ({page, server}) => {
|
||||
it('should fail when server returns 204', async ({page, server, isChromium, isWebKit}) => {
|
||||
// Webkit just loads an empty page.
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.statusCode = 204;
|
||||
|
|
@ -143,9 +143,9 @@ it('should fail when server returns 204', async ({page, server}) => {
|
|||
let error = null;
|
||||
await page.goto(server.EMPTY_PAGE).catch(e => error = e);
|
||||
expect(error).not.toBe(null);
|
||||
if (options.CHROMIUM)
|
||||
if (isChromium)
|
||||
expect(error.message).toContain('net::ERR_ABORTED');
|
||||
else if (options.WEBKIT)
|
||||
else if (isWebKit)
|
||||
expect(error.message).toContain('Aborted: 204 No Content');
|
||||
else
|
||||
expect(error.message).toContain('NS_BINDING_ABORTED');
|
||||
|
|
@ -165,10 +165,10 @@ it('should work when page calls history API in beforeunload', async ({page, serv
|
|||
expect(response.status()).toBe(200);
|
||||
});
|
||||
|
||||
it('should fail when navigating to bad url', async ({page}) => {
|
||||
it('should fail when navigating to bad url', async ({page, isChromium, isWebKit}) => {
|
||||
let error = null;
|
||||
await page.goto('asdfasdf').catch(e => error = e);
|
||||
if (options.CHROMIUM || options.WEBKIT)
|
||||
if (isChromium || isWebKit)
|
||||
expect(error.message).toContain('Cannot navigate to invalid URL');
|
||||
else
|
||||
expect(error.message).toContain('Invalid url');
|
||||
|
|
@ -210,14 +210,14 @@ it('should throw if networkidle2 is passed as an option', async ({page, server})
|
|||
expect(error.message).toContain(`waitUntil: expected one of (load|domcontentloaded|networkidle)`);
|
||||
});
|
||||
|
||||
it('should fail when main resources failed to load', async ({page}) => {
|
||||
it('should fail when main resources failed to load', async ({page, isChromium, isWebKit}) => {
|
||||
let error = null;
|
||||
await page.goto('http://localhost:44123/non-existing-url').catch(e => error = e);
|
||||
if (options.CHROMIUM)
|
||||
if (isChromium)
|
||||
expect(error.message).toContain('net::ERR_CONNECTION_REFUSED');
|
||||
else if (options.WEBKIT && WIN)
|
||||
else if (isWebKit && WIN)
|
||||
expect(error.message).toContain(`Couldn\'t connect to server`);
|
||||
else if (options.WEBKIT)
|
||||
else if (isWebKit)
|
||||
expect(error.message).toContain('Could not connect');
|
||||
else
|
||||
expect(error.message).toContain('NS_ERROR_CONNECTION_REFUSED');
|
||||
|
|
@ -300,7 +300,7 @@ it('should disable timeout when its set to 0', async ({page, server}) => {
|
|||
expect(loaded).toBe(true);
|
||||
});
|
||||
|
||||
it('should fail when replaced by another navigation', async ({page, server}) => {
|
||||
it('should fail when replaced by another navigation', async ({page, server, isChromium, isWebKit}) => {
|
||||
let anotherPromise;
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
anotherPromise = page.goto(server.PREFIX + '/one-style.html');
|
||||
|
|
@ -308,9 +308,9 @@ it('should fail when replaced by another navigation', async ({page, server}) =>
|
|||
});
|
||||
const error = await page.goto(server.PREFIX + '/empty.html').catch(e => e);
|
||||
await anotherPromise;
|
||||
if (options.CHROMIUM)
|
||||
if (isChromium)
|
||||
expect(error.message).toContain('net::ERR_ABORTED');
|
||||
else if (options.WEBKIT)
|
||||
else if (isWebKit)
|
||||
expect(error.message).toContain('cancelled');
|
||||
else
|
||||
expect(error.message).toContain('NS_BINDING_ABORTED');
|
||||
|
|
@ -476,7 +476,7 @@ it('should fail when canceled by another navigation', async ({page, server}) =>
|
|||
expect(error.message).toBeTruthy();
|
||||
});
|
||||
|
||||
it('extraHttpHeaders should be pushed to provisional page', test => {
|
||||
it('extraHttpHeaders should be pushed to provisional page', (test, parameters) => {
|
||||
test.flaky('This test is flaky, because we cannot await page.setExtraHTTPHeaders.');
|
||||
// We need a way to test our implementation by more than just public api.
|
||||
}, async ({page, server}) => {
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ it('page.goBack should work with HistoryAPI', async ({page, server}) => {
|
|||
expect(page.url()).toBe(server.PREFIX + '/first.html');
|
||||
});
|
||||
|
||||
it('page.goBack should work for file urls', test => {
|
||||
test.fail(options.WEBKIT && MAC);
|
||||
it('page.goBack should work for file urls', (test, parameters) => {
|
||||
test.fail(options.WEBKIT(parameters) && MAC);
|
||||
}, async ({page, server, asset}) => {
|
||||
// WebKit embedder fails to go back/forward to the file url.
|
||||
const url1 = url.pathToFileURL(asset('empty.html')).href;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
it('should intercept', async ({page, server}) => {
|
||||
let intercepted = false;
|
||||
|
|
@ -183,15 +183,15 @@ it('should be abortable', async ({page, server}) => {
|
|||
expect(failed).toBe(true);
|
||||
});
|
||||
|
||||
it('should be abortable with custom error codes', async ({page, server}) => {
|
||||
it('should be abortable with custom error codes', async ({page, server, isWebKit, isFirefox}) => {
|
||||
await page.route('**/*', route => route.abort('internetdisconnected'));
|
||||
let failedRequest = null;
|
||||
page.on('requestfailed', request => failedRequest = request);
|
||||
await page.goto(server.EMPTY_PAGE).catch(e => {});
|
||||
expect(failedRequest).toBeTruthy();
|
||||
if (options.WEBKIT)
|
||||
if (isWebKit)
|
||||
expect(failedRequest.failure().errorText).toBe('Request intercepted');
|
||||
else if (options.FIREFOX)
|
||||
else if (isFirefox)
|
||||
expect(failedRequest.failure().errorText).toBe('NS_ERROR_OFFLINE');
|
||||
else
|
||||
expect(failedRequest.failure().errorText).toBe('net::ERR_INTERNET_DISCONNECTED');
|
||||
|
|
@ -209,14 +209,14 @@ it('should send referer', async ({page, server}) => {
|
|||
expect(request.headers['referer']).toBe('http://google.com/');
|
||||
});
|
||||
|
||||
it('should fail navigation when aborting main resource', async ({page, server}) => {
|
||||
it('should fail navigation when aborting main resource', async ({page, server, isWebKit, isFirefox}) => {
|
||||
await page.route('**/*', route => route.abort());
|
||||
let error = null;
|
||||
await page.goto(server.EMPTY_PAGE).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
if (options.WEBKIT)
|
||||
if (isWebKit)
|
||||
expect(error.message).toContain('Request intercepted');
|
||||
else if (options.FIREFOX)
|
||||
else if (isFirefox)
|
||||
expect(error.message).toContain('NS_ERROR_FAILURE');
|
||||
else
|
||||
expect(error.message).toContain('net::ERR_FAILED');
|
||||
|
|
|
|||
|
|
@ -21,10 +21,9 @@ import path from 'path';
|
|||
import fs from 'fs';
|
||||
|
||||
// Firefox headful produces a different image.
|
||||
const ffheadful = options.FIREFOX && !options.HEADLESS;
|
||||
|
||||
describe('page screenshot', suite => {
|
||||
suite.skip(ffheadful);
|
||||
describe('page screenshot', (suite, parameters) => {
|
||||
suite.skip(options.FIREFOX(parameters) && !options.HEADLESS);
|
||||
}, () => {
|
||||
it('should work', async ({page, server, golden}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
|
|
@ -142,8 +141,8 @@ describe('page screenshot', suite => {
|
|||
await Promise.all(pages.map(page => page.close()));
|
||||
});
|
||||
|
||||
it('should allow transparency', test => {
|
||||
test.fail(options.FIREFOX);
|
||||
it('should allow transparency', (test, parameters) => {
|
||||
test.fail(options.FIREFOX(parameters));
|
||||
}, async ({page, golden}) => {
|
||||
await page.setViewportSize({ width: 50, height: 150 });
|
||||
await page.setContent(`
|
||||
|
|
@ -178,8 +177,8 @@ describe('page screenshot', suite => {
|
|||
expect(screenshot).toMatchImage(golden('screenshot-clip-odd-size.png'));
|
||||
});
|
||||
|
||||
it('should work with a mobile viewport', test => {
|
||||
test.skip(options.FIREFOX);
|
||||
it('should work with a mobile viewport', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters));
|
||||
}, async ({browser, server, golden}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 320, height: 480 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
|
|
@ -189,8 +188,8 @@ describe('page screenshot', suite => {
|
|||
await context.close();
|
||||
});
|
||||
|
||||
it('should work with a mobile viewport and clip', test => {
|
||||
test.skip(options.FIREFOX);
|
||||
it('should work with a mobile viewport and clip', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters));
|
||||
}, async ({browser, server, golden}) => {
|
||||
const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true});
|
||||
const page = await context.newPage();
|
||||
|
|
@ -200,8 +199,8 @@ describe('page screenshot', suite => {
|
|||
await context.close();
|
||||
});
|
||||
|
||||
it('should work with a mobile viewport and fullPage', test => {
|
||||
test.skip(options.FIREFOX);
|
||||
it('should work with a mobile viewport and fullPage', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters));
|
||||
}, async ({browser, server, golden}) => {
|
||||
const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true});
|
||||
const page = await context.newPage();
|
||||
|
|
@ -218,9 +217,9 @@ describe('page screenshot', suite => {
|
|||
expect(screenshot).toMatchImage(golden('screenshot-canvas.png'), { threshold: 0.3 });
|
||||
});
|
||||
|
||||
it('should work for webgl', test => {
|
||||
test.fixme(options.FIREFOX);
|
||||
test.fixme(options.WEBKIT && LINUX);
|
||||
it('should work for webgl', (test, parameters) => {
|
||||
test.fixme(options.FIREFOX(parameters));
|
||||
test.fixme(options.WEBKIT(parameters) && LINUX);
|
||||
}, async ({page, server, golden}) => {
|
||||
await page.setViewportSize({width: 640, height: 480});
|
||||
await page.goto(server.PREFIX + '/screenshots/webgl.html');
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
import type { Route } from '..';
|
||||
|
||||
it('should pick up ongoing navigation', async ({page, server}) => {
|
||||
|
|
@ -69,7 +69,7 @@ it('should work with pages that have loaded before being connected to', async ({
|
|||
expect(popup.url()).toBe(server.EMPTY_PAGE);
|
||||
});
|
||||
|
||||
it('should wait for load state of empty url popup', async ({browser, page}) => {
|
||||
it('should wait for load state of empty url popup', async ({page, isFirefox}) => {
|
||||
const [popup, readyState] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => {
|
||||
|
|
@ -78,8 +78,8 @@ it('should wait for load state of empty url popup', async ({browser, page}) => {
|
|||
}),
|
||||
]);
|
||||
await popup.waitForLoadState();
|
||||
expect(readyState).toBe(options.FIREFOX ? 'uninitialized' : 'complete');
|
||||
expect(await popup.evaluate(() => document.readyState)).toBe(options.FIREFOX ? 'uninitialized' : 'complete');
|
||||
expect(readyState).toBe(isFirefox ? 'uninitialized' : 'complete');
|
||||
expect(await popup.evaluate(() => document.readyState)).toBe(isFirefox ? 'uninitialized' : 'complete');
|
||||
});
|
||||
|
||||
it('should wait for load state of about:blank popup ', async ({browser, page}) => {
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import fs from 'fs';
|
|||
import path from 'path';
|
||||
|
||||
|
||||
it('should be able to save file', test => {
|
||||
test.skip(!(options.HEADLESS && options.CHROMIUM), 'Printing to pdf is currently only supported in headless chromium.');
|
||||
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');
|
||||
await page.pdf({path: outputFile});
|
||||
|
|
@ -29,8 +29,8 @@ it('should be able to save file', test => {
|
|||
fs.unlinkSync(outputFile);
|
||||
});
|
||||
|
||||
it('should only have pdf in chromium', test => {
|
||||
test.skip(options.CHROMIUM);
|
||||
it('should only have pdf in chromium', (test, parameters) => {
|
||||
test.skip(options.CHROMIUM(parameters));
|
||||
}, async ({page}) => {
|
||||
expect(page.pdf).toBe(undefined);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ function getPermission(page, name) {
|
|||
return page.evaluate(name => navigator.permissions.query({name}).then(result => result.state), name);
|
||||
}
|
||||
|
||||
describe('permissions', suite => {
|
||||
suite.skip(options.WEBKIT);
|
||||
describe('permissions', (suite, parameters) => {
|
||||
suite.skip(options.WEBKIT(parameters));
|
||||
}, () => {
|
||||
it('should be prompt by default', async ({page, server, context}) => {
|
||||
// Permissions API is not implemented in WebKit (see https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API)
|
||||
|
|
@ -101,10 +101,10 @@ describe('permissions', suite => {
|
|||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
});
|
||||
|
||||
it('should trigger permission onchange', test => {
|
||||
test.fail(options.WEBKIT);
|
||||
test.fail(options.CHROMIUM && !options.HEADLESS);
|
||||
test.flaky(options.FIREFOX && LINUX);
|
||||
it('should trigger permission onchange', (test, parameters) => {
|
||||
test.fail(options.WEBKIT(parameters));
|
||||
test.fail(options.CHROMIUM(parameters) && !options.HEADLESS);
|
||||
test.flaky(options.FIREFOX(parameters) && LINUX);
|
||||
}, async ({page, server, context}) => {
|
||||
// TODO: flaky
|
||||
// - Linux: https://github.com/microsoft/playwright/pull/1790/checks?check_run_id=587327883
|
||||
|
|
@ -147,10 +147,10 @@ describe('permissions', suite => {
|
|||
await otherContext.close();
|
||||
});
|
||||
|
||||
it('should support clipboard read', test => {
|
||||
test.fail(options.WEBKIT);
|
||||
test.fail(options.FIREFOX, 'No such permissions (requires flag) in Firefox');
|
||||
test.fixme(options.CHROMIUM && !options.HEADLESS);
|
||||
it('should support clipboard read', (test, parameters) => {
|
||||
test.fail(options.WEBKIT(parameters));
|
||||
test.fail(options.FIREFOX(parameters), 'No such permissions (requires flag) in Firefox');
|
||||
test.fixme(options.CHROMIUM(parameters) && !options.HEADLESS);
|
||||
}, async ({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await getPermission(page, 'clipboard-read')).toBe('prompt');
|
||||
|
|
|
|||
|
|
@ -26,19 +26,25 @@ import { installCoverageHooks } from './coverage';
|
|||
import { mkdtempAsync, removeFolderAsync } from './utils';
|
||||
import { fixtures as baseFixtures } from '@playwright/test-runner';
|
||||
|
||||
export type PlaywrightWorkerFixtures = {
|
||||
type PlaywrightParameters = {
|
||||
browserName: string;
|
||||
};
|
||||
|
||||
type PlaywrightWorkerFixtures = {
|
||||
asset: (path: string) => string;
|
||||
defaultBrowserOptions: LaunchOptions;
|
||||
golden: (path: string) => string;
|
||||
playwright: typeof import('../index');
|
||||
browserName: string;
|
||||
browserType: BrowserType<Browser>;
|
||||
browser: Browser;
|
||||
httpService: {server: TestServer, httpsServer: TestServer}
|
||||
toImpl: (rpcObject: any) => any;
|
||||
isChromium: boolean;
|
||||
isFirefox: boolean;
|
||||
isWebKit: boolean;
|
||||
};
|
||||
|
||||
export type PlaywrightFixtures = {
|
||||
type PlaywrightFixtures = {
|
||||
context: BrowserContext;
|
||||
server: TestServer;
|
||||
page: Page;
|
||||
|
|
@ -47,8 +53,12 @@ export type PlaywrightFixtures = {
|
|||
launchPersistent: (options?: Parameters<BrowserType<Browser>['launchPersistentContext']>[1]) => Promise<{context: BrowserContext, page: Page}>;
|
||||
};
|
||||
|
||||
const fixtures = baseFixtures.extend<PlaywrightWorkerFixtures, PlaywrightFixtures>();
|
||||
const { registerFixture, registerWorkerFixture } = fixtures;
|
||||
const fixtures = baseFixtures
|
||||
.declareParameters<PlaywrightParameters>()
|
||||
.declareWorkerFixtures<PlaywrightWorkerFixtures>()
|
||||
.declareTestFixtures<PlaywrightFixtures>();
|
||||
const { defineTestFixture, defineWorkerFixture, defineParameter } = fixtures;
|
||||
|
||||
export const playwrightFixtures = fixtures;
|
||||
export const it = fixtures.it;
|
||||
export const fit = fixtures.fit;
|
||||
|
|
@ -63,9 +73,9 @@ export const afterAll = fixtures.afterAll;
|
|||
export const expect = fixtures.expect;
|
||||
|
||||
export const options = {
|
||||
CHROMIUM: fixtures.parameters.browserName === 'chromium',
|
||||
FIREFOX: fixtures.parameters.browserName === 'firefox',
|
||||
WEBKIT: fixtures.parameters.browserName === 'webkit',
|
||||
CHROMIUM: parameters => parameters.browserName === 'chromium',
|
||||
FIREFOX: parameters => parameters.browserName === 'firefox',
|
||||
WEBKIT: parameters => parameters.browserName === 'webkit',
|
||||
HEADLESS: !!valueFromEnv('HEADLESS', true),
|
||||
WIRE: !!process.env.PWWIRE,
|
||||
SLOW_MO: valueFromEnv('SLOW_MO', 0),
|
||||
|
|
@ -83,7 +93,7 @@ global['MAC'] = platform === 'darwin';
|
|||
global['LINUX'] = platform === 'linux';
|
||||
global['WIN'] = platform === 'win32';
|
||||
|
||||
registerWorkerFixture('httpService', async ({parallelIndex}, test) => {
|
||||
defineWorkerFixture('httpService', async ({parallelIndex}, test) => {
|
||||
const assetsPath = path.join(__dirname, 'assets');
|
||||
const cachedPath = path.join(__dirname, 'assets', 'cached');
|
||||
|
||||
|
|
@ -112,7 +122,7 @@ const getExecutablePath = browserName => {
|
|||
return process.env.WKPATH;
|
||||
};
|
||||
|
||||
registerWorkerFixture('defaultBrowserOptions', async ({browserName}, test) => {
|
||||
defineWorkerFixture('defaultBrowserOptions', async ({browserName}, test) => {
|
||||
const executablePath = getExecutablePath(browserName);
|
||||
|
||||
if (executablePath)
|
||||
|
|
@ -125,7 +135,7 @@ registerWorkerFixture('defaultBrowserOptions', async ({browserName}, test) => {
|
|||
});
|
||||
});
|
||||
|
||||
registerWorkerFixture('playwright', async ({browserName}, test) => {
|
||||
defineWorkerFixture('playwright', async ({browserName, parallelIndex}, test) => {
|
||||
const {coverage, uninstall} = installCoverageHooks(browserName);
|
||||
if (options.WIRE) {
|
||||
require('../lib/utils/utils').setUnderTest();
|
||||
|
|
@ -163,27 +173,37 @@ registerWorkerFixture('playwright', async ({browserName}, test) => {
|
|||
|
||||
async function teardownCoverage() {
|
||||
uninstall();
|
||||
const coveragePath = path.join(__dirname, 'coverage-report', fixtures.parameters.parallelIndex + '.json');
|
||||
const coveragePath = path.join(__dirname, 'coverage-report', parallelIndex + '.json');
|
||||
const coverageJSON = [...coverage.keys()].filter(key => coverage.get(key));
|
||||
await fs.promises.mkdir(path.dirname(coveragePath), { recursive: true });
|
||||
await fs.promises.writeFile(coveragePath, JSON.stringify(coverageJSON, undefined, 2), 'utf8');
|
||||
}
|
||||
});
|
||||
|
||||
registerWorkerFixture('toImpl', async ({playwright}, test) => {
|
||||
defineWorkerFixture('toImpl', async ({playwright}, test) => {
|
||||
await test((playwright as any)._toImpl);
|
||||
});
|
||||
|
||||
registerWorkerFixture('browserType', async ({playwright, browserName}, test) => {
|
||||
defineWorkerFixture('browserType', async ({playwright, browserName}, test) => {
|
||||
const browserType = playwright[browserName];
|
||||
await test(browserType);
|
||||
});
|
||||
|
||||
registerWorkerFixture('browserName', async ({}, test) => {
|
||||
throw new Error(`Parameter 'browserName' is not specified`);
|
||||
defineParameter('browserName', 'Browser type name', '');
|
||||
|
||||
defineWorkerFixture('isChromium', async ({browserName}, test) => {
|
||||
await test(browserName === 'chromium');
|
||||
});
|
||||
|
||||
registerWorkerFixture('browser', async ({browserType, defaultBrowserOptions}, test) => {
|
||||
defineWorkerFixture('isFirefox', async ({browserName}, test) => {
|
||||
await test(browserName === 'firefox');
|
||||
});
|
||||
|
||||
defineWorkerFixture('isWebKit', async ({browserName}, test) => {
|
||||
await test(browserName === 'webkit');
|
||||
});
|
||||
|
||||
defineWorkerFixture('browser', async ({browserType, defaultBrowserOptions}, test) => {
|
||||
const browser = await browserType.launch(defaultBrowserOptions);
|
||||
await test(browser);
|
||||
if (browser.contexts().length !== 0) {
|
||||
|
|
@ -193,15 +213,15 @@ registerWorkerFixture('browser', async ({browserType, defaultBrowserOptions}, te
|
|||
await browser.close();
|
||||
});
|
||||
|
||||
registerWorkerFixture('asset', async ({}, test) => {
|
||||
defineWorkerFixture('asset', async ({}, test) => {
|
||||
await test(p => path.join(__dirname, `assets`, p));
|
||||
});
|
||||
|
||||
registerWorkerFixture('golden', async ({browserName}, test) => {
|
||||
defineWorkerFixture('golden', async ({browserName}, test) => {
|
||||
await test(p => path.join(browserName, p));
|
||||
});
|
||||
|
||||
registerFixture('context', async ({browser, playwright, toImpl}, runTest, info) => {
|
||||
defineTestFixture('context', async ({browser, playwright, toImpl}, runTest, info) => {
|
||||
const context = await browser.newContext();
|
||||
const { test, config } = info;
|
||||
if ((playwright as any).__tracer) {
|
||||
|
|
@ -215,7 +235,7 @@ registerFixture('context', async ({browser, playwright, toImpl}, runTest, info)
|
|||
await context.close();
|
||||
});
|
||||
|
||||
registerFixture('page', async ({context, playwright, toImpl}, runTest, info) => {
|
||||
defineTestFixture('page', async ({context, playwright, toImpl}, runTest, info) => {
|
||||
const page = await context.newPage();
|
||||
await runTest(page);
|
||||
const { test, config, result } = info;
|
||||
|
|
@ -229,7 +249,7 @@ registerFixture('page', async ({context, playwright, toImpl}, runTest, info) =>
|
|||
}
|
||||
});
|
||||
|
||||
registerFixture('launchPersistent', async ({tmpDir, defaultBrowserOptions, browserType}, test) => {
|
||||
defineTestFixture('launchPersistent', async ({tmpDir, defaultBrowserOptions, browserType}, test) => {
|
||||
let context;
|
||||
async function launchPersistent(options) {
|
||||
if (context)
|
||||
|
|
@ -243,17 +263,17 @@ registerFixture('launchPersistent', async ({tmpDir, defaultBrowserOptions, brows
|
|||
await context.close();
|
||||
});
|
||||
|
||||
registerFixture('server', async ({httpService}, test) => {
|
||||
defineTestFixture('server', async ({httpService}, test) => {
|
||||
httpService.server.reset();
|
||||
await test(httpService.server);
|
||||
});
|
||||
|
||||
registerFixture('httpsServer', async ({httpService}, test) => {
|
||||
defineTestFixture('httpsServer', async ({httpService}, test) => {
|
||||
httpService.httpsServer.reset();
|
||||
await test(httpService.httpsServer);
|
||||
});
|
||||
|
||||
registerFixture('tmpDir', async ({}, test) => {
|
||||
defineTestFixture('tmpDir', async ({}, test) => {
|
||||
const tmpDir = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-'));
|
||||
await test(tmpDir);
|
||||
await removeFolderAsync(tmpDir).catch(e => {});
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
it('should inherit user agent from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext({
|
||||
|
|
@ -210,7 +210,7 @@ it('should expose function from browser context', async function({browser, serve
|
|||
expect(messages.join('|')).toBe('page|binding');
|
||||
});
|
||||
|
||||
it('should not dispatch binding on a closed page', async function({browser, server}) {
|
||||
it('should not dispatch binding on a closed page', async function({browser, server, isFirefox}) {
|
||||
const context = await browser.newContext();
|
||||
const messages = [];
|
||||
await context.exposeFunction('add', (a, b) => {
|
||||
|
|
@ -233,7 +233,7 @@ it('should not dispatch binding on a closed page', async function({browser, serv
|
|||
}),
|
||||
]);
|
||||
await context.close();
|
||||
if (options.FIREFOX)
|
||||
if (isFirefox)
|
||||
expect(messages.join('|')).toBe('close');
|
||||
else
|
||||
expect(messages.join('|')).toBe('binding|close');
|
||||
|
|
|
|||
|
|
@ -77,8 +77,8 @@ it('should authenticate', async ({browserType, defaultBrowserOptions, server}) =
|
|||
await browser.close();
|
||||
});
|
||||
|
||||
it('should exclude patterns', test => {
|
||||
test.flaky(options.CHROMIUM && !options.HEADLESS, 'Chromium headful crashes with CHECK(!in_frame_tree_) in RenderFrameImpl::OnDeleteFrame.');
|
||||
it('should exclude patterns', (test, parameters) => {
|
||||
test.flaky(options.CHROMIUM(parameters) && !options.HEADLESS, 'Chromium headful crashes with CHECK(!in_frame_tree_) in RenderFrameImpl::OnDeleteFrame.');
|
||||
}, async ({browserType, defaultBrowserOptions, server}) => {
|
||||
server.setRoute('/target.html', async (req, res) => {
|
||||
res.end('<html><title>Served by the proxy</title></html>');
|
||||
|
|
@ -119,8 +119,8 @@ it('should exclude patterns', test => {
|
|||
await browser.close();
|
||||
});
|
||||
|
||||
it('should use socks proxy', test => {
|
||||
test.flaky(MAC && options.WEBKIT, 'Intermittent page.goto: The network connection was lost error on bots');
|
||||
it('should use socks proxy', (test, parameters) => {
|
||||
test.flaky(MAC && options.WEBKIT(parameters), 'Intermittent page.goto: The network connection was lost error on bots');
|
||||
}, async ({ browserType, defaultBrowserOptions, parallelIndex }) => {
|
||||
const server = socks.createServer((info, accept, deny) => {
|
||||
let socket;
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ type ServerFixtures = {
|
|||
remoteServer: RemoteServer;
|
||||
stallingRemoteServer: RemoteServer;
|
||||
};
|
||||
export const serverFixtures = playwrightFixtures.extend<{}, ServerFixtures>();
|
||||
const { registerFixture } = serverFixtures;
|
||||
export const serverFixtures = playwrightFixtures.declareTestFixtures<ServerFixtures>();
|
||||
const { defineTestFixture } = serverFixtures;
|
||||
|
||||
const playwrightPath = path.join(__dirname, '..');
|
||||
|
||||
|
|
@ -116,14 +116,14 @@ export class RemoteServer {
|
|||
}
|
||||
}
|
||||
|
||||
registerFixture('remoteServer', async ({browserType, defaultBrowserOptions}, test) => {
|
||||
defineTestFixture('remoteServer', async ({browserType, defaultBrowserOptions}, test) => {
|
||||
const remoteServer = new RemoteServer();
|
||||
await remoteServer._start(browserType, defaultBrowserOptions);
|
||||
await test(remoteServer);
|
||||
await remoteServer.close();
|
||||
});
|
||||
|
||||
registerFixture('stallingRemoteServer', async ({browserType, defaultBrowserOptions}, test) => {
|
||||
defineTestFixture('stallingRemoteServer', async ({browserType, defaultBrowserOptions}, test) => {
|
||||
const remoteServer = new RemoteServer();
|
||||
await remoteServer._start(browserType, defaultBrowserOptions, { stallOnClose: true });
|
||||
await test(remoteServer);
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ it('should work with status code 422', async ({page, server}) => {
|
|||
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!');
|
||||
});
|
||||
|
||||
it('should allow mocking binary responses', test => {
|
||||
test.skip(options.FIREFOX && !options.HEADLESS, '// Firefox headful produces a different image.');
|
||||
it('should allow mocking binary responses', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters) && !options.HEADLESS, '// Firefox headful produces a different image.');
|
||||
}, async ({page, server, golden}) => {
|
||||
await page.route('**/*', route => {
|
||||
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
|
||||
|
|
@ -70,8 +70,8 @@ it('should allow mocking binary responses', test => {
|
|||
expect(await img.screenshot()).toMatchImage(golden('mock-binary-response.png'));
|
||||
});
|
||||
|
||||
it('should allow mocking svg with charset', test => {
|
||||
test.skip(options.FIREFOX && !options.HEADLESS, '// Firefox headful produces a different image.');
|
||||
it('should allow mocking svg with charset', (test, parameters) => {
|
||||
test.skip(options.FIREFOX(parameters) && !options.HEADLESS, '// Firefox headful produces a different image.');
|
||||
}, async ({page, server, golden}) => {
|
||||
// Firefox headful produces a different image.
|
||||
await page.route('**/*', route => {
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@ import { TestServer } from '../utils/testserver';
|
|||
type TestState = {
|
||||
videoPlayer: VideoPlayer;
|
||||
};
|
||||
const fixtures = playwrightFixtures.extend<{}, TestState>();
|
||||
const { it, expect, describe, registerFixture } = fixtures;
|
||||
const fixtures = playwrightFixtures.declareTestFixtures<TestState>();
|
||||
const { it, expect, describe, defineTestFixture } = fixtures;
|
||||
|
||||
registerFixture('videoPlayer', async ({playwright, context, server}, test) => {
|
||||
defineTestFixture('videoPlayer', async ({playwright, context, server}, test) => {
|
||||
// WebKit on Mac & Windows cannot replay webm/vp8 video, is unrelyable
|
||||
// on Linux (times out) and in Firefox, so we always launch chromium for
|
||||
// playback.
|
||||
|
|
@ -181,7 +181,7 @@ describe('screencast', suite => {
|
|||
expectAll(pixels, almostRed);
|
||||
});
|
||||
|
||||
it('should capture navigation', test => {
|
||||
it('should capture navigation', (test, parameters) => {
|
||||
test.flaky();
|
||||
}, async ({page, tmpDir, server, videoPlayer, toImpl}) => {
|
||||
const videoFile = path.join(tmpDir, 'v.webm');
|
||||
|
|
@ -210,8 +210,8 @@ describe('screencast', suite => {
|
|||
}
|
||||
});
|
||||
|
||||
it('should capture css transformation', test => {
|
||||
test.fail(options.WEBKIT && WIN, 'Does not work on WebKit Windows');
|
||||
it('should capture css transformation', (test, parameters) => {
|
||||
test.fail(options.WEBKIT(parameters) && WIN, 'Does not work on WebKit Windows');
|
||||
}, async ({page, tmpDir, server, videoPlayer, toImpl}) => {
|
||||
const videoFile = path.join(tmpDir, 'v.webm');
|
||||
// Set viewport equal to screencast frame size to avoid scaling.
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, options } from './playwright.fixtures';
|
||||
import { it, expect } from './playwright.fixtures';
|
||||
|
||||
it('query', async ({page}) => {
|
||||
it('query', async ({page, isWebKit}) => {
|
||||
await page.setContent(`<div>yo</div><div>ya</div><div>\nye </div>`);
|
||||
expect(await page.$eval(`text=ya`, e => e.outerHTML)).toBe('<div>ya</div>');
|
||||
expect(await page.$eval(`text="ya"`, e => e.outerHTML)).toBe('<div>ya</div>');
|
||||
|
|
@ -59,9 +59,9 @@ it('query', async ({page}) => {
|
|||
expect(await page.$eval(`"x"`, e => e.outerHTML)).toBe('<div>x</div>');
|
||||
expect(await page.$eval(`'x'`, e => e.outerHTML)).toBe('<div>x</div>');
|
||||
let error = await page.$(`"`).catch(e => e);
|
||||
expect(error.message).toContain(options.WEBKIT ? 'SyntaxError' : 'querySelector');
|
||||
expect(error.message).toContain(isWebKit ? 'SyntaxError' : 'querySelector');
|
||||
error = await page.$(`'`).catch(e => e);
|
||||
expect(error.message).toContain(options.WEBKIT ? 'SyntaxError' : 'querySelector');
|
||||
expect(error.message).toContain(isWebKit ? 'SyntaxError' : 'querySelector');
|
||||
|
||||
await page.setContent(`<div> ' </div><div> " </div>`);
|
||||
expect(await page.$eval(`text="`, e => e.outerHTML)).toBe('<div> " </div>');
|
||||
|
|
|
|||
|
|
@ -14,16 +14,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare const before: (f: () => Promise<any>) => void;
|
||||
declare const after: (f: () => Promise<any>) => void;
|
||||
declare const matrix: (m: any) => void;
|
||||
|
||||
matrix({
|
||||
'browserName': process.env.BROWSER ? [process.env.BROWSER] : ['chromium', 'webkit', 'firefox'],
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
});
|
||||
(global as any).setParameterValues(
|
||||
'browserName',
|
||||
process.env.BROWSER ? [process.env.BROWSER] : ['chromium', 'webkit', 'firefox']);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
import { it, options } from './playwright.fixtures';
|
||||
|
||||
it('should not throw', test => {
|
||||
it('should not throw', (test, parameters) => {
|
||||
test.skip(!options.TRACING);
|
||||
}, async ({page, server, playwright, toImpl}) => {
|
||||
await page.goto(server.PREFIX + '/snapshot/snapshot-with-css.html');
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ const utils = module.exports = {
|
|||
},
|
||||
});
|
||||
|
||||
testRunner.on('testfinished', test => {
|
||||
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**'))
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ const Source = require('../../Source');
|
|||
const mdBuilder = require('../MDBuilder');
|
||||
const jsBuilder = require('../JSBuilder');
|
||||
const { fixtures } = require('@playwright/test-runner');
|
||||
const { registerWorkerFixture, describe, it, expect } = fixtures;
|
||||
const { defineWorkerFixture, describe, it, expect } = fixtures;
|
||||
|
||||
registerWorkerFixture('page', async({}, test) => {
|
||||
defineWorkerFixture('page', async({}, test) => {
|
||||
const browser = await playwright.chromium.launch();
|
||||
const page = await browser.newPage();
|
||||
await test(page);
|
||||
|
|
|
|||
Loading…
Reference in a new issue