test: remove module.export.describe wrapper (#1716)
This commit is contained in:
parent
2ef8e26602
commit
ade9d23c28
|
|
@ -15,340 +15,336 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({FFOX, CHROMIUM, WEBKIT, MAC}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
|
||||
describe('Accessibility', function() {
|
||||
it('should work', async function({page}) {
|
||||
describe('Accessibility', function() {
|
||||
it('should work', async function({page}) {
|
||||
await page.setContent(`
|
||||
<head>
|
||||
<title>Accessibility Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Inputs</h1>
|
||||
<input placeholder="Empty input" autofocus />
|
||||
<input placeholder="readonly input" readonly />
|
||||
<input placeholder="disabled input" disabled />
|
||||
<input aria-label="Input with whitespace" value=" " />
|
||||
<input value="value only" />
|
||||
<input aria-placeholder="placeholder" value="and a value" />
|
||||
<div aria-hidden="true" id="desc">This is a description!</div>
|
||||
<input aria-placeholder="placeholder" value="and a value" aria-describedby="desc" />
|
||||
</body>`);
|
||||
// autofocus happens after a delay in chrome these days
|
||||
await page.waitForFunction(() => document.activeElement.hasAttribute('autofocus'));
|
||||
|
||||
const golden = FFOX ? {
|
||||
role: 'document',
|
||||
name: 'Accessibility Test',
|
||||
children: [
|
||||
{role: 'heading', name: 'Inputs', level: 1},
|
||||
{role: 'textbox', name: 'Empty input', focused: true},
|
||||
{role: 'textbox', name: 'readonly input', readonly: true},
|
||||
{role: 'textbox', name: 'disabled input', disabled: true},
|
||||
{role: 'textbox', name: 'Input with whitespace', value: ' '},
|
||||
{role: 'textbox', name: '', value: 'value only'},
|
||||
{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
|
||||
]
|
||||
} : CHROMIUM ? {
|
||||
role: 'WebArea',
|
||||
name: 'Accessibility Test',
|
||||
children: [
|
||||
{role: 'heading', name: 'Inputs', level: 1},
|
||||
{role: 'textbox', name: 'Empty input', focused: true},
|
||||
{role: 'textbox', name: 'readonly input', readonly: true},
|
||||
{role: 'textbox', name: 'disabled input', disabled: true},
|
||||
{role: 'textbox', name: 'Input with whitespace', value: ' '},
|
||||
{role: 'textbox', name: '', value: 'value only'},
|
||||
{role: 'textbox', name: 'placeholder', value: 'and a value'},
|
||||
{role: 'textbox', name: 'placeholder', value: 'and a value', description: 'This is a description!'},
|
||||
]
|
||||
} : {
|
||||
role: 'WebArea',
|
||||
name: 'Accessibility Test',
|
||||
children: [
|
||||
{role: 'heading', name: 'Inputs', level: 1},
|
||||
{role: 'textbox', name: 'Empty input', focused: true},
|
||||
{role: 'textbox', name: 'readonly input', readonly: true},
|
||||
{role: 'textbox', name: 'disabled input', disabled: true},
|
||||
{role: 'textbox', name: 'Input with whitespace', value: ' ' },
|
||||
{role: 'textbox', name: '', value: 'value only' },
|
||||
{role: 'textbox', name: 'placeholder', value: 'and a value'},
|
||||
{role: 'textbox', name: 'This is a description!',value: 'and a value'}, // webkit uses the description over placeholder for the name
|
||||
]
|
||||
};
|
||||
expect(await page.accessibility.snapshot()).toEqual(golden);
|
||||
});
|
||||
it('should work with regular text', async({page}) => {
|
||||
await page.setContent(`<div>Hello World</div>`);
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual({
|
||||
role: FFOX ? 'text leaf' : 'text',
|
||||
name: 'Hello World',
|
||||
});
|
||||
});
|
||||
it('roledescription', async({page}) => {
|
||||
await page.setContent('<div tabIndex=-1 aria-roledescription="foo">Hi</div>');
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0].roledescription).toEqual('foo');
|
||||
});
|
||||
it('orientation', async({page}) => {
|
||||
await page.setContent('<a href="" role="slider" aria-orientation="vertical">11</a>');
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0].orientation).toEqual('vertical');
|
||||
});
|
||||
it('autocomplete', async({page}) => {
|
||||
await page.setContent('<div role="textbox" aria-autocomplete="list">hi</div>');
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0].autocomplete).toEqual('list');
|
||||
});
|
||||
it('multiselectable', async({page}) => {
|
||||
await page.setContent('<div role="grid" tabIndex=-1 aria-multiselectable=true>hey</div>');
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0].multiselectable).toEqual(true);
|
||||
});
|
||||
it('keyshortcuts', async({page}) => {
|
||||
await page.setContent('<div role="grid" tabIndex=-1 aria-keyshortcuts="foo">hey</div>');
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0].keyshortcuts).toEqual('foo');
|
||||
});
|
||||
describe('filtering children of leaf nodes', function() {
|
||||
it('should not report text nodes inside controls', async function({page}) {
|
||||
await page.setContent(`
|
||||
<head>
|
||||
<title>Accessibility Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Inputs</h1>
|
||||
<input placeholder="Empty input" autofocus />
|
||||
<input placeholder="readonly input" readonly />
|
||||
<input placeholder="disabled input" disabled />
|
||||
<input aria-label="Input with whitespace" value=" " />
|
||||
<input value="value only" />
|
||||
<input aria-placeholder="placeholder" value="and a value" />
|
||||
<div aria-hidden="true" id="desc">This is a description!</div>
|
||||
<input aria-placeholder="placeholder" value="and a value" aria-describedby="desc" />
|
||||
</body>`);
|
||||
// autofocus happens after a delay in chrome these days
|
||||
await page.waitForFunction(() => document.activeElement.hasAttribute('autofocus'));
|
||||
|
||||
const golden = FFOX ? {
|
||||
role: 'document',
|
||||
name: 'Accessibility Test',
|
||||
children: [
|
||||
{role: 'heading', name: 'Inputs', level: 1},
|
||||
{role: 'textbox', name: 'Empty input', focused: true},
|
||||
{role: 'textbox', name: 'readonly input', readonly: true},
|
||||
{role: 'textbox', name: 'disabled input', disabled: true},
|
||||
{role: 'textbox', name: 'Input with whitespace', value: ' '},
|
||||
{role: 'textbox', name: '', value: 'value only'},
|
||||
{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
|
||||
]
|
||||
} : CHROMIUM ? {
|
||||
role: 'WebArea',
|
||||
name: 'Accessibility Test',
|
||||
children: [
|
||||
{role: 'heading', name: 'Inputs', level: 1},
|
||||
{role: 'textbox', name: 'Empty input', focused: true},
|
||||
{role: 'textbox', name: 'readonly input', readonly: true},
|
||||
{role: 'textbox', name: 'disabled input', disabled: true},
|
||||
{role: 'textbox', name: 'Input with whitespace', value: ' '},
|
||||
{role: 'textbox', name: '', value: 'value only'},
|
||||
{role: 'textbox', name: 'placeholder', value: 'and a value'},
|
||||
{role: 'textbox', name: 'placeholder', value: 'and a value', description: 'This is a description!'},
|
||||
]
|
||||
} : {
|
||||
role: 'WebArea',
|
||||
name: 'Accessibility Test',
|
||||
children: [
|
||||
{role: 'heading', name: 'Inputs', level: 1},
|
||||
{role: 'textbox', name: 'Empty input', focused: true},
|
||||
{role: 'textbox', name: 'readonly input', readonly: true},
|
||||
{role: 'textbox', name: 'disabled input', disabled: true},
|
||||
{role: 'textbox', name: 'Input with whitespace', value: ' ' },
|
||||
{role: 'textbox', name: '', value: 'value only' },
|
||||
{role: 'textbox', name: 'placeholder', value: 'and a value'},
|
||||
{role: 'textbox', name: 'This is a description!',value: 'and a value'}, // webkit uses the description over placeholder for the name
|
||||
]
|
||||
<div role="tablist">
|
||||
<div role="tab" aria-selected="true"><b>Tab1</b></div>
|
||||
<div role="tab">Tab2</div>
|
||||
</div>`);
|
||||
const golden = {
|
||||
role: FFOX ? 'document' : 'WebArea',
|
||||
name: '',
|
||||
children: [{
|
||||
role: 'tab',
|
||||
name: 'Tab1',
|
||||
selected: true
|
||||
}, {
|
||||
role: 'tab',
|
||||
name: 'Tab2'
|
||||
}]
|
||||
};
|
||||
expect(await page.accessibility.snapshot()).toEqual(golden);
|
||||
});
|
||||
it('should work with regular text', async({page}) => {
|
||||
await page.setContent(`<div>Hello World</div>`);
|
||||
// WebKit rich text accessibility is iffy
|
||||
it.skip(WEBKIT)('rich text editable fields should have children', async function({page}) {
|
||||
await page.setContent(`
|
||||
<div contenteditable="true">
|
||||
Edit this image: <img src="fakeimage.png" alt="my fake image">
|
||||
</div>`);
|
||||
const golden = FFOX ? {
|
||||
role: 'section',
|
||||
name: '',
|
||||
children: [{
|
||||
role: 'text leaf',
|
||||
name: 'Edit this image: '
|
||||
}, {
|
||||
role: 'text',
|
||||
name: 'my fake image'
|
||||
}]
|
||||
} : {
|
||||
role: 'generic',
|
||||
name: '',
|
||||
value: 'Edit this image: ',
|
||||
children: [{
|
||||
role: 'text',
|
||||
name: 'Edit this image:'
|
||||
}, {
|
||||
role: 'img',
|
||||
name: 'my fake image'
|
||||
}]
|
||||
};
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual({
|
||||
role: FFOX ? 'text leaf' : 'text',
|
||||
name: 'Hello World',
|
||||
});
|
||||
expect(snapshot.children[0]).toEqual(golden);
|
||||
});
|
||||
it('roledescription', async({page}) => {
|
||||
await page.setContent('<div tabIndex=-1 aria-roledescription="foo">Hi</div>');
|
||||
// WebKit rich text accessibility is iffy
|
||||
it.skip(WEBKIT)('rich text editable fields with role should have children', async function({page}) {
|
||||
await page.setContent(`
|
||||
<div contenteditable="true" role='textbox'>
|
||||
Edit this image: <img src="fakeimage.png" alt="my fake image">
|
||||
</div>`);
|
||||
const golden = FFOX ? {
|
||||
role: 'textbox',
|
||||
name: '',
|
||||
value: 'Edit this image: my fake image',
|
||||
children: [{
|
||||
role: 'text',
|
||||
name: 'my fake image'
|
||||
}]
|
||||
} : {
|
||||
role: 'textbox',
|
||||
name: '',
|
||||
value: 'Edit this image: ',
|
||||
children: [{
|
||||
role: 'text',
|
||||
name: 'Edit this image:'
|
||||
}, {
|
||||
role: 'img',
|
||||
name: 'my fake image'
|
||||
}]
|
||||
};
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0].roledescription).toEqual('foo');
|
||||
expect(snapshot.children[0]).toEqual(golden);
|
||||
});
|
||||
it('orientation', async({page}) => {
|
||||
await page.setContent('<a href="" role="slider" aria-orientation="vertical">11</a>');
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0].orientation).toEqual('vertical');
|
||||
});
|
||||
it('autocomplete', async({page}) => {
|
||||
await page.setContent('<div role="textbox" aria-autocomplete="list">hi</div>');
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0].autocomplete).toEqual('list');
|
||||
});
|
||||
it('multiselectable', async({page}) => {
|
||||
await page.setContent('<div role="grid" tabIndex=-1 aria-multiselectable=true>hey</div>');
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0].multiselectable).toEqual(true);
|
||||
});
|
||||
it('keyshortcuts', async({page}) => {
|
||||
await page.setContent('<div role="grid" tabIndex=-1 aria-keyshortcuts="foo">hey</div>');
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0].keyshortcuts).toEqual('foo');
|
||||
});
|
||||
describe('filtering children of leaf nodes', function() {
|
||||
it('should not report text nodes inside controls', async function({page}) {
|
||||
// Firefox does not support contenteditable="plaintext-only".
|
||||
// WebKit rich text accessibility is iffy
|
||||
describe.skip(FFOX || WEBKIT)('plaintext contenteditable', function() {
|
||||
it('plain text field with role should not have children', async function({page}) {
|
||||
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: FFOX ? 'document' : 'WebArea',
|
||||
<div contenteditable="plaintext-only" role='textbox'>Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual({
|
||||
role: 'textbox',
|
||||
name: '',
|
||||
children: [{
|
||||
role: 'tab',
|
||||
name: 'Tab1',
|
||||
selected: true
|
||||
}, {
|
||||
role: 'tab',
|
||||
name: 'Tab2'
|
||||
}]
|
||||
};
|
||||
expect(await page.accessibility.snapshot()).toEqual(golden);
|
||||
value: 'Edit this image:'
|
||||
});
|
||||
});
|
||||
// WebKit rich text accessibility is iffy
|
||||
it.skip(WEBKIT)('rich text editable fields should have children', async function({page}) {
|
||||
it('plain text field without role should not have content', async function({page}) {
|
||||
await page.setContent(`
|
||||
<div contenteditable="true">
|
||||
Edit this image: <img src="fakeimage.png" alt="my fake image">
|
||||
</div>`);
|
||||
const golden = FFOX ? {
|
||||
role: 'section',
|
||||
name: '',
|
||||
children: [{
|
||||
role: 'text leaf',
|
||||
name: 'Edit this image: '
|
||||
}, {
|
||||
role: 'text',
|
||||
name: 'my fake image'
|
||||
}]
|
||||
} : {
|
||||
<div contenteditable="plaintext-only">Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual({
|
||||
role: 'generic',
|
||||
name: '',
|
||||
value: 'Edit this image: ',
|
||||
children: [{
|
||||
role: 'text',
|
||||
name: 'Edit this image:'
|
||||
}, {
|
||||
role: 'img',
|
||||
name: 'my fake image'
|
||||
}]
|
||||
};
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual(golden);
|
||||
name: ''
|
||||
});
|
||||
});
|
||||
// WebKit rich text accessibility is iffy
|
||||
it.skip(WEBKIT)('rich text editable fields with role should have children', async function({page}) {
|
||||
it('plain text field with tabindex and without role should not have content', async function({page}) {
|
||||
await page.setContent(`
|
||||
<div contenteditable="true" role='textbox'>
|
||||
Edit this image: <img src="fakeimage.png" alt="my fake image">
|
||||
</div>`);
|
||||
const golden = FFOX ? {
|
||||
role: 'textbox',
|
||||
name: '',
|
||||
value: 'Edit this image: my fake image',
|
||||
children: [{
|
||||
role: 'text',
|
||||
name: 'my fake image'
|
||||
}]
|
||||
} : {
|
||||
role: 'textbox',
|
||||
name: '',
|
||||
value: 'Edit this image: ',
|
||||
children: [{
|
||||
role: 'text',
|
||||
name: 'Edit this image:'
|
||||
}, {
|
||||
role: 'img',
|
||||
name: 'my fake image'
|
||||
}]
|
||||
};
|
||||
<div contenteditable="plaintext-only" tabIndex=0>Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual(golden);
|
||||
});
|
||||
// Firefox does not support contenteditable="plaintext-only".
|
||||
// WebKit rich text accessibility is iffy
|
||||
describe.skip(FFOX || WEBKIT)('plaintext contenteditable', function() {
|
||||
it('plain text field with role should not have children', async function({page}) {
|
||||
await page.setContent(`
|
||||
<div contenteditable="plaintext-only" role='textbox'>Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual({
|
||||
role: 'textbox',
|
||||
name: '',
|
||||
value: 'Edit this image:'
|
||||
});
|
||||
});
|
||||
it('plain text field without role should not have content', async function({page}) {
|
||||
await page.setContent(`
|
||||
<div contenteditable="plaintext-only">Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual({
|
||||
role: 'generic',
|
||||
name: ''
|
||||
});
|
||||
});
|
||||
it('plain text field with tabindex and without role should not have content', async function({page}) {
|
||||
await page.setContent(`
|
||||
<div contenteditable="plaintext-only" tabIndex=0>Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual({
|
||||
role: 'generic',
|
||||
name: ''
|
||||
});
|
||||
expect(snapshot.children[0]).toEqual({
|
||||
role: 'generic',
|
||||
name: ''
|
||||
});
|
||||
});
|
||||
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}) {
|
||||
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 = FFOX ? {
|
||||
role: 'textbox',
|
||||
name: 'my favorite textbox',
|
||||
value: 'this is the inner content yo'
|
||||
} : CHROMIUM ? {
|
||||
role: 'textbox',
|
||||
name: 'my favorite textbox',
|
||||
value: 'this is the inner content '
|
||||
} : {
|
||||
role: 'textbox',
|
||||
name: 'my favorite textbox',
|
||||
value: 'this is the inner content ',
|
||||
};
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual(golden);
|
||||
});
|
||||
it('checkbox with and tabIndex and label should not have children', async function({page}) {
|
||||
await page.setContent(`
|
||||
<div role="checkbox" tabIndex=0 aria-checked="true" aria-label="my favorite checkbox">
|
||||
this is the inner content
|
||||
<img alt="yo" src="fakeimg.png">
|
||||
</div>`);
|
||||
const golden = {
|
||||
role: 'checkbox',
|
||||
name: 'my favorite checkbox',
|
||||
checked: true
|
||||
};
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual(golden);
|
||||
});
|
||||
it('checkbox without label should not have children', async function({page}) {
|
||||
await page.setContent(`
|
||||
<div role="checkbox" aria-checked="true">
|
||||
this is the inner content
|
||||
<img alt="yo" src="fakeimg.png">
|
||||
</div>`);
|
||||
const golden = FFOX ? {
|
||||
role: 'checkbox',
|
||||
name: 'this is the inner content yo',
|
||||
checked: true
|
||||
} : {
|
||||
role: 'checkbox',
|
||||
name: 'this is the inner content yo',
|
||||
checked: true
|
||||
};
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual(golden);
|
||||
});
|
||||
|
||||
describe('root option', function() {
|
||||
it('should work a button', async({page}) => {
|
||||
await page.setContent(`<button>My Button</button>`);
|
||||
|
||||
const button = await page.$('button');
|
||||
expect(await page.accessibility.snapshot({root: button})).toEqual({
|
||||
role: 'button',
|
||||
name: 'My Button'
|
||||
});
|
||||
});
|
||||
it('should work an input', async({page}) => {
|
||||
await page.setContent(`<input title="My Input" value="My Value">`);
|
||||
|
||||
const input = await page.$('input');
|
||||
expect(await page.accessibility.snapshot({root: input})).toEqual({
|
||||
role: 'textbox',
|
||||
name: 'My Input',
|
||||
value: 'My Value'
|
||||
});
|
||||
});
|
||||
it('should work on a menu', async({page}) => {
|
||||
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 = FFOX ? {
|
||||
role: 'textbox',
|
||||
name: 'my favorite textbox',
|
||||
value: 'this is the inner content yo'
|
||||
} : CHROMIUM ? {
|
||||
role: 'textbox',
|
||||
name: 'my favorite textbox',
|
||||
value: 'this is the inner content '
|
||||
} : {
|
||||
role: 'textbox',
|
||||
name: 'my favorite textbox',
|
||||
value: 'this is the inner content ',
|
||||
};
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual(golden);
|
||||
<div role="menu" title="My Menu">
|
||||
<div role="menuitem">First Item</div>
|
||||
<div role="menuitem">Second Item</div>
|
||||
<div role="menuitem">Third Item</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
const menu = await page.$('div[role="menu"]');
|
||||
expect(await page.accessibility.snapshot({root: menu})).toEqual({
|
||||
role: 'menu',
|
||||
name: 'My Menu',
|
||||
children:
|
||||
[ { role: 'menuitem', name: 'First Item' },
|
||||
{ role: 'menuitem', name: 'Second Item' },
|
||||
{ role: 'menuitem', name: 'Third Item' } ],
|
||||
orientation: WEBKIT ? 'vertical' : undefined
|
||||
});
|
||||
});
|
||||
it('checkbox with and tabIndex and label should not have children', async function({page}) {
|
||||
it('should return null when the element is no longer in DOM', async({page}) => {
|
||||
await page.setContent(`<button>My Button</button>`);
|
||||
const button = await page.$('button');
|
||||
await page.$eval('button', button => button.remove());
|
||||
expect(await page.accessibility.snapshot({root: button})).toEqual(null);
|
||||
});
|
||||
it('should show uninteresting nodes', async({page}) => {
|
||||
await page.setContent(`
|
||||
<div role="checkbox" tabIndex=0 aria-checked="true" aria-label="my favorite checkbox">
|
||||
this is the inner content
|
||||
<img alt="yo" src="fakeimg.png">
|
||||
</div>`);
|
||||
const golden = {
|
||||
role: 'checkbox',
|
||||
name: 'my favorite checkbox',
|
||||
checked: true
|
||||
};
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual(golden);
|
||||
});
|
||||
it('checkbox without label should not have children', async function({page}) {
|
||||
await page.setContent(`
|
||||
<div role="checkbox" aria-checked="true">
|
||||
this is the inner content
|
||||
<img alt="yo" src="fakeimg.png">
|
||||
</div>`);
|
||||
const golden = FFOX ? {
|
||||
role: 'checkbox',
|
||||
name: 'this is the inner content yo',
|
||||
checked: true
|
||||
} : {
|
||||
role: 'checkbox',
|
||||
name: 'this is the inner content yo',
|
||||
checked: true
|
||||
};
|
||||
const snapshot = await page.accessibility.snapshot();
|
||||
expect(snapshot.children[0]).toEqual(golden);
|
||||
});
|
||||
|
||||
describe('root option', function() {
|
||||
it('should work a button', async({page}) => {
|
||||
await page.setContent(`<button>My Button</button>`);
|
||||
|
||||
const button = await page.$('button');
|
||||
expect(await page.accessibility.snapshot({root: button})).toEqual({
|
||||
role: 'button',
|
||||
name: 'My Button'
|
||||
});
|
||||
});
|
||||
it('should work an input', async({page}) => {
|
||||
await page.setContent(`<input title="My Input" value="My Value">`);
|
||||
|
||||
const input = await page.$('input');
|
||||
expect(await page.accessibility.snapshot({root: input})).toEqual({
|
||||
role: 'textbox',
|
||||
name: 'My Input',
|
||||
value: 'My Value'
|
||||
});
|
||||
});
|
||||
it('should work on a menu', async({page}) => {
|
||||
await page.setContent(`
|
||||
<div role="menu" title="My Menu">
|
||||
<div role="menuitem">First Item</div>
|
||||
<div role="menuitem">Second Item</div>
|
||||
<div role="menuitem">Third Item</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
const menu = await page.$('div[role="menu"]');
|
||||
expect(await page.accessibility.snapshot({root: menu})).toEqual({
|
||||
role: 'menu',
|
||||
name: 'My Menu',
|
||||
children:
|
||||
[ { role: 'menuitem', name: 'First Item' },
|
||||
{ role: 'menuitem', name: 'Second Item' },
|
||||
{ role: 'menuitem', name: 'Third Item' } ],
|
||||
orientation: WEBKIT ? 'vertical' : undefined
|
||||
});
|
||||
});
|
||||
it('should return null when the element is no longer in DOM', async({page}) => {
|
||||
await page.setContent(`<button>My Button</button>`);
|
||||
const button = await page.$('button');
|
||||
await page.$eval('button', button => button.remove());
|
||||
expect(await page.accessibility.snapshot({root: button})).toEqual(null);
|
||||
});
|
||||
it('should show uninteresting nodes', async({page}) => {
|
||||
await page.setContent(`
|
||||
<div id="root" role="textbox">
|
||||
<div id="root" role="textbox">
|
||||
<div>
|
||||
hello
|
||||
<div>
|
||||
hello
|
||||
<div>
|
||||
world
|
||||
</div>
|
||||
world
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
</div>
|
||||
`);
|
||||
|
||||
const root = await page.$('#root');
|
||||
const snapshot = await page.accessibility.snapshot({root, interestingOnly: false});
|
||||
expect(snapshot.role).toBe('textbox');
|
||||
expect(snapshot.value).toContain('hello');
|
||||
expect(snapshot.value).toContain('world');
|
||||
expect(!!snapshot.children).toBe(true);
|
||||
});
|
||||
const root = await page.$('#root');
|
||||
const snapshot = await page.accessibility.snapshot({root, interestingOnly: false});
|
||||
expect(snapshot.role).toBe('textbox');
|
||||
expect(snapshot.value).toContain('hello');
|
||||
expect(snapshot.value).toContain('world');
|
||||
expect(!!snapshot.children).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -48,56 +48,54 @@ function traceAPICoverage(apiCoverage, events, className, classType) {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports.describe = function({browserType}) {
|
||||
describe('**API COVERAGE**', () => {
|
||||
const BROWSER_CONFIGS = [
|
||||
{
|
||||
name: 'Firefox',
|
||||
events: require('../lib/events').Events,
|
||||
missingCoverage: ['browserContext.setGeolocation', 'browserContext.setOffline', 'cDPSession.send', 'cDPSession.detach'],
|
||||
describe.skip(!process.env.COVERAGE)('**API COVERAGE**', () => {
|
||||
const BROWSER_CONFIGS = [
|
||||
{
|
||||
name: 'Firefox',
|
||||
events: require('../lib/events').Events,
|
||||
missingCoverage: ['browserContext.setGeolocation', 'browserContext.setOffline', 'cDPSession.send', 'cDPSession.detach'],
|
||||
},
|
||||
{
|
||||
name: 'WebKit',
|
||||
events: require('../lib/events').Events,
|
||||
missingCoverage: ['browserContext.clearPermissions', 'cDPSession.send', 'cDPSession.detach'],
|
||||
},
|
||||
{
|
||||
name: 'Chromium',
|
||||
events: {
|
||||
...require('../lib/events').Events,
|
||||
...require('../lib/chromium/events').Events,
|
||||
},
|
||||
{
|
||||
name: 'WebKit',
|
||||
events: require('../lib/events').Events,
|
||||
missingCoverage: ['browserContext.clearPermissions', 'cDPSession.send', 'cDPSession.detach'],
|
||||
},
|
||||
{
|
||||
name: 'Chromium',
|
||||
events: {
|
||||
...require('../lib/events').Events,
|
||||
...require('../lib/chromium/events').Events,
|
||||
},
|
||||
missingCoverage: [],
|
||||
},
|
||||
];
|
||||
const browserConfig = BROWSER_CONFIGS.find(config => config.name.toLowerCase() === browserType.name());
|
||||
const events = browserConfig.events;
|
||||
const api = require('../lib/api');
|
||||
missingCoverage: [],
|
||||
},
|
||||
];
|
||||
const browserConfig = BROWSER_CONFIGS.find(config => config.name.toLowerCase() === browserType.name());
|
||||
const events = browserConfig.events;
|
||||
const api = require('../lib/api');
|
||||
|
||||
const coverage = new Map();
|
||||
Object.keys(api).forEach(apiName => {
|
||||
if (BROWSER_CONFIGS.some(config => apiName.startsWith(config.name)) && !apiName.startsWith(browserConfig.name))
|
||||
return;
|
||||
traceAPICoverage(coverage, events, apiName, api[apiName]);
|
||||
});
|
||||
|
||||
it('should call all API methods', () => {
|
||||
const ignoredMethods = new Set(browserConfig.missingCoverage);
|
||||
const missingMethods = [];
|
||||
const extraIgnoredMethods = [];
|
||||
for (const method of coverage.keys()) {
|
||||
// Sometimes we already have a background page while launching, before adding a listener.
|
||||
if (method === 'chromiumBrowserContext.emit("backgroundpage")')
|
||||
continue;
|
||||
if (!coverage.get(method) && !ignoredMethods.has(method))
|
||||
missingMethods.push(method);
|
||||
else if (coverage.get(method) && ignoredMethods.has(method))
|
||||
extraIgnoredMethods.push(method);
|
||||
}
|
||||
if (extraIgnoredMethods.length)
|
||||
throw new Error('Certain API Methods are called and should not be ignored: ' + extraIgnoredMethods.join(', '));
|
||||
if (missingMethods.length)
|
||||
throw new Error('Certain API Methods are not called: ' + missingMethods.join(', '));
|
||||
});
|
||||
const coverage = new Map();
|
||||
Object.keys(api).forEach(apiName => {
|
||||
if (BROWSER_CONFIGS.some(config => apiName.startsWith(config.name)) && !apiName.startsWith(browserConfig.name))
|
||||
return;
|
||||
traceAPICoverage(coverage, events, apiName, api[apiName]);
|
||||
});
|
||||
};
|
||||
|
||||
it('should call all API methods', () => {
|
||||
const ignoredMethods = new Set(browserConfig.missingCoverage);
|
||||
const missingMethods = [];
|
||||
const extraIgnoredMethods = [];
|
||||
for (const method of coverage.keys()) {
|
||||
// Sometimes we already have a background page while launching, before adding a listener.
|
||||
if (method === 'chromiumBrowserContext.emit("backgroundpage")')
|
||||
continue;
|
||||
if (!coverage.get(method) && !ignoredMethods.has(method))
|
||||
missingMethods.push(method);
|
||||
else if (coverage.get(method) && ignoredMethods.has(method))
|
||||
extraIgnoredMethods.push(method);
|
||||
}
|
||||
if (extraIgnoredMethods.length)
|
||||
throw new Error('Certain API Methods are called and should not be ignored: ' + extraIgnoredMethods.join(', '));
|
||||
if (missingMethods.length)
|
||||
throw new Error('Certain API Methods are not called: ' + missingMethods.join(', '));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,216 +15,212 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({playwright, MAC, WIN, FFOX, CHROMIUM, WEBKIT}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
|
||||
describe('Auto waiting', () => {
|
||||
it('should await navigation when clicking anchor', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
|
||||
await Promise.all([
|
||||
page.click('a').then(() => messages.push('click')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|click');
|
||||
describe('Auto waiting', () => {
|
||||
it('should await navigation when clicking anchor', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
it('should await cross-process navigation when clicking anchor', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`<a href="${server.CROSS_PROCESS_PREFIX + '/empty.html'}">empty.html</a>`);
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
|
||||
await Promise.all([
|
||||
page.click('a').then(() => messages.push('click')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|click');
|
||||
await Promise.all([
|
||||
page.click('a').then(() => messages.push('click')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|click');
|
||||
});
|
||||
it('should await cross-process navigation when clicking anchor', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
it('should await form-get on click', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html?foo=bar', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`
|
||||
<form action="${server.EMPTY_PAGE}" method="get">
|
||||
<input name="foo" value="bar">
|
||||
<input type="submit" value="Submit">
|
||||
</form>`);
|
||||
await page.setContent(`<a href="${server.CROSS_PROCESS_PREFIX + '/empty.html'}">empty.html</a>`);
|
||||
|
||||
await Promise.all([
|
||||
page.click('input[type=submit]').then(() => messages.push('click')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|click');
|
||||
await Promise.all([
|
||||
page.click('a').then(() => messages.push('click')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|click');
|
||||
});
|
||||
it('should await form-get on click', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html?foo=bar', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
it('should await form-post on click', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`
|
||||
<form action="${server.EMPTY_PAGE}" method="post">
|
||||
<input name="foo" value="bar">
|
||||
<input type="submit" value="Submit">
|
||||
</form>`);
|
||||
await page.setContent(`
|
||||
<form action="${server.EMPTY_PAGE}" method="get">
|
||||
<input name="foo" value="bar">
|
||||
<input type="submit" value="Submit">
|
||||
</form>`);
|
||||
|
||||
await Promise.all([
|
||||
page.click('input[type=submit]').then(() => messages.push('click')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|click');
|
||||
await Promise.all([
|
||||
page.click('input[type=submit]').then(() => messages.push('click')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|click');
|
||||
});
|
||||
it('should await form-post on click', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
it('should await navigation when assigning location', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
await Promise.all([
|
||||
page.evaluate(`window.location.href = "${server.EMPTY_PAGE}"`).then(() => messages.push('evaluate')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|evaluate');
|
||||
});
|
||||
it.fail(CHROMIUM)('should await navigation when assigning location twice', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html?cancel', async (req, res) => { res.end('done'); });
|
||||
server.setRoute('/empty.html?override', async (req, res) => { messages.push('routeoverride'); res.end('done'); });
|
||||
await Promise.all([
|
||||
page.evaluate(`
|
||||
window.location.href = "${server.EMPTY_PAGE}?cancel";
|
||||
window.location.href = "${server.EMPTY_PAGE}?override";
|
||||
`).then(() => messages.push('evaluate')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('routeoverride|evaluate');
|
||||
});
|
||||
it('should await navigation when evaluating reload', async({page, server}) => {
|
||||
const messages = [];
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
page.evaluate(`window.location.reload()`).then(() => messages.push('evaluate')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|evaluate');
|
||||
});
|
||||
it('should await navigating specified target', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
await page.setContent(`
|
||||
<form action="${server.EMPTY_PAGE}" method="post">
|
||||
<input name="foo" value="bar">
|
||||
<input type="submit" value="Submit">
|
||||
</form>`);
|
||||
|
||||
await page.setContent(`
|
||||
<a href="${server.EMPTY_PAGE}" target=target>empty.html</a>
|
||||
<iframe name=target></iframe>
|
||||
`);
|
||||
const frame = page.frame({ name: 'target' });
|
||||
await Promise.all([
|
||||
page.click('a').then(() => messages.push('click')),
|
||||
frame.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(frame.url()).toBe(server.EMPTY_PAGE);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|click');
|
||||
await Promise.all([
|
||||
page.click('input[type=submit]').then(() => messages.push('click')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|click');
|
||||
});
|
||||
it('should await navigation when assigning location', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
it('should work with waitUntil: nowait', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
await Promise.all([
|
||||
page.click('a', { waitUntil: 'nowait' }).then(() => messages.push('click')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
page.waitForNavigation({ waitUntil: 'load' }).then(() => messages.push('load')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('click|domcontentloaded|load');
|
||||
await Promise.all([
|
||||
page.evaluate(`window.location.href = "${server.EMPTY_PAGE}"`).then(() => messages.push('evaluate')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|evaluate');
|
||||
});
|
||||
it.fail(CHROMIUM)('should await navigation when assigning location twice', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html?cancel', async (req, res) => { res.end('done'); });
|
||||
server.setRoute('/empty.html?override', async (req, res) => { messages.push('routeoverride'); res.end('done'); });
|
||||
await Promise.all([
|
||||
page.evaluate(`
|
||||
window.location.href = "${server.EMPTY_PAGE}?cancel";
|
||||
window.location.href = "${server.EMPTY_PAGE}?override";
|
||||
`).then(() => messages.push('evaluate')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('routeoverride|evaluate');
|
||||
});
|
||||
it('should await navigation when evaluating reload', async({page, server}) => {
|
||||
const messages = [];
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
it('should work with waitUntil: load', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
await Promise.all([
|
||||
page.click('a', { waitUntil: 'load' }).then(() => messages.push('click')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
page.waitForNavigation({ waitUntil: 'load' }).then(() => messages.push('load')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|load|click');
|
||||
await Promise.all([
|
||||
page.evaluate(`window.location.reload()`).then(() => messages.push('evaluate')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|evaluate');
|
||||
});
|
||||
it('should await navigating specified target', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`
|
||||
<a href="${server.EMPTY_PAGE}" target=target>empty.html</a>
|
||||
<iframe name=target></iframe>
|
||||
`);
|
||||
const frame = page.frame({ name: 'target' });
|
||||
await Promise.all([
|
||||
page.click('a').then(() => messages.push('click')),
|
||||
frame.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(frame.url()).toBe(server.EMPTY_PAGE);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|click');
|
||||
});
|
||||
it('should work with waitUntil: nowait', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
await Promise.all([
|
||||
page.click('a', { waitUntil: 'nowait' }).then(() => messages.push('click')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
page.waitForNavigation({ waitUntil: 'load' }).then(() => messages.push('load')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('click|domcontentloaded|load');
|
||||
});
|
||||
it('should work with waitUntil: load', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
await Promise.all([
|
||||
page.click('a', { waitUntil: 'load' }).then(() => messages.push('click')),
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }).then(() => messages.push('domcontentloaded')),
|
||||
page.waitForNavigation({ waitUntil: 'load' }).then(() => messages.push('load')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|load|click');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Auto waiting should not hang when', () => {
|
||||
it('clicking on links which do not commit navigation', async({page, server, httpsServer}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<a href='${httpsServer.EMPTY_PAGE}'>foobar</a>`);
|
||||
await page.click('a');
|
||||
});
|
||||
it('calling window.stop async', async({page, server, httpsServer}) => {
|
||||
server.setRoute('/empty.html', async (req, res) => {});
|
||||
await page.evaluate((url) => {
|
||||
window.location.href = url;
|
||||
setTimeout(() => window.stop(), 100);
|
||||
}, server.EMPTY_PAGE);
|
||||
});
|
||||
it.fail(CHROMIUM)('calling window.stop sync', async({page, server, httpsServer}) => {
|
||||
// Flaky, see https://github.com/microsoft/playwright/pull/1630/checks?check_run_id=553475173.
|
||||
// We only get Page.frameStoppedLoading, but do not know that navigation was aborted or
|
||||
// that navigation request was cancelled.
|
||||
await page.evaluate((url) => {
|
||||
window.location.href = url;
|
||||
window.stop();
|
||||
}, server.EMPTY_PAGE);
|
||||
});
|
||||
it('assigning location to about:blank', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(`window.location.href = "about:blank";`);
|
||||
});
|
||||
it('assigning location to about:blank after non-about:blank', async({page, server}) => {
|
||||
server.setRoute('/empty.html', async (req, res) => {});
|
||||
await page.evaluate(`
|
||||
window.location.href = "${server.EMPTY_PAGE}";
|
||||
window.location.href = "about:blank";`);
|
||||
});
|
||||
it('calling window.open and window.close', async function({page, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
const popup = window.open(window.location.href);
|
||||
popup.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Auto waiting should not hang when', () => {
|
||||
it('clicking on links which do not commit navigation', async({page, server, httpsServer}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<a href='${httpsServer.EMPTY_PAGE}'>foobar</a>`);
|
||||
await page.click('a');
|
||||
});
|
||||
it('calling window.stop async', async({page, server, httpsServer}) => {
|
||||
server.setRoute('/empty.html', async (req, res) => {});
|
||||
await page.evaluate((url) => {
|
||||
window.location.href = url;
|
||||
setTimeout(() => window.stop(), 100);
|
||||
}, server.EMPTY_PAGE);
|
||||
});
|
||||
it.fail(CHROMIUM)('calling window.stop sync', async({page, server, httpsServer}) => {
|
||||
// Flaky, see https://github.com/microsoft/playwright/pull/1630/checks?check_run_id=553475173.
|
||||
// We only get Page.frameStoppedLoading, but do not know that navigation was aborted or
|
||||
// that navigation request was cancelled.
|
||||
await page.evaluate((url) => {
|
||||
window.location.href = url;
|
||||
window.stop();
|
||||
}, server.EMPTY_PAGE);
|
||||
});
|
||||
it('assigning location to about:blank', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(`window.location.href = "about:blank";`);
|
||||
});
|
||||
it('assigning location to about:blank after non-about:blank', async({page, server}) => {
|
||||
server.setRoute('/empty.html', async (req, res) => {});
|
||||
await page.evaluate(`
|
||||
window.location.href = "${server.EMPTY_PAGE}";
|
||||
window.location.href = "about:blank";`);
|
||||
});
|
||||
it('calling window.open and window.close', async function({page, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
const popup = window.open(window.location.href);
|
||||
popup.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -14,31 +14,27 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {BrowserTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({playwright, CHROMIUM, WEBKIT}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
|
||||
describe('Browser.newPage', function() {
|
||||
it('should create new page', async function({browser}) {
|
||||
const page1 = await browser.newPage();
|
||||
expect(browser.contexts().length).toBe(1);
|
||||
describe('Browser.newPage', function() {
|
||||
it('should create new page', async function({browser}) {
|
||||
const page1 = await browser.newPage();
|
||||
expect(browser.contexts().length).toBe(1);
|
||||
|
||||
const page2 = await browser.newPage();
|
||||
expect(browser.contexts().length).toBe(2);
|
||||
const page2 = await browser.newPage();
|
||||
expect(browser.contexts().length).toBe(2);
|
||||
|
||||
await page1.close();
|
||||
expect(browser.contexts().length).toBe(1);
|
||||
await page1.close();
|
||||
expect(browser.contexts().length).toBe(1);
|
||||
|
||||
await page2.close();
|
||||
expect(browser.contexts().length).toBe(0);
|
||||
});
|
||||
it('should throw upon second create new page', async function({browser}) {
|
||||
const page = await browser.newPage();
|
||||
let error;
|
||||
await page.context().newPage().catch(e => error = e);
|
||||
await page.close();
|
||||
expect(error.message).toContain('Please use browser.newContext()');
|
||||
});
|
||||
await page2.close();
|
||||
expect(browser.contexts().length).toBe(0);
|
||||
});
|
||||
};
|
||||
it('should throw upon second create new page', async function({browser}) {
|
||||
const page = await browser.newPage();
|
||||
let error;
|
||||
await page.context().newPage().catch(e => error = e);
|
||||
await page.close();
|
||||
expect(error.message).toContain('Please use browser.newContext()');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -14,15 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({WIN, WEBKIT}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN} = require('./utils').testOptions(browserType);
|
||||
|
||||
describe('Capabilities', function() {
|
||||
it.fail(WEBKIT && WIN)('Web Assembly should work', async function({page, server}) {
|
||||
await page.goto(server.PREFIX + '/wasm/table2.html');
|
||||
expect(await page.evaluate(() => loadTable())).toBe('42, 83');
|
||||
});
|
||||
describe('Capabilities', function() {
|
||||
it.fail(WEBKIT && WIN)('Web Assembly should work', async function({page, server}) {
|
||||
await page.goto(server.PREFIX + '/wasm/table2.html');
|
||||
expect(await page.evaluate(() => loadTable())).toBe('42, 83');
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,67 +14,62 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {ChromiumTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({playwright, FFOX, CHROMIUM, WEBKIT}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('../utils').testOptions(browserType);
|
||||
|
||||
describe('ChromiumBrowserContext', function() {
|
||||
it('should create a worker from a service worker', async({browser, page, server, context}) => {
|
||||
const [worker] = await Promise.all([
|
||||
context.waitForEvent('serviceworker'),
|
||||
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
|
||||
]);
|
||||
expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]');
|
||||
});
|
||||
it('serviceWorkers() should return current workers', async({browser, page, server, context}) => {
|
||||
const [worker1] = await Promise.all([
|
||||
context.waitForEvent('serviceworker'),
|
||||
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
|
||||
]);
|
||||
let workers = context.serviceWorkers();
|
||||
expect(workers.length).toBe(1);
|
||||
|
||||
const [worker2] = await Promise.all([
|
||||
context.waitForEvent('serviceworker'),
|
||||
page.goto(server.CROSS_PROCESS_PREFIX + '/serviceworkers/empty/sw.html')
|
||||
]);
|
||||
workers = context.serviceWorkers();
|
||||
expect(workers.length).toBe(2);
|
||||
expect(workers).toContain(worker1);
|
||||
expect(workers).toContain(worker2);
|
||||
});
|
||||
it('should not create a worker from a shared worker', async({browser, page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let serviceWorkerCreated;
|
||||
context.once('serviceworker', () => serviceWorkerCreated = true);
|
||||
await page.evaluate(() => {
|
||||
new SharedWorker('data:text/javascript,console.log("hi")');
|
||||
});
|
||||
expect(serviceWorkerCreated).not.toBeTruthy();
|
||||
});
|
||||
describe('ChromiumBrowserContext', function() {
|
||||
it('should create a worker from a service worker', async({browser, page, server, context}) => {
|
||||
const [worker] = await Promise.all([
|
||||
context.waitForEvent('serviceworker'),
|
||||
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
|
||||
]);
|
||||
expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]');
|
||||
});
|
||||
it('serviceWorkers() should return current workers', async({browser, page, server, context}) => {
|
||||
const [worker1] = await Promise.all([
|
||||
context.waitForEvent('serviceworker'),
|
||||
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
|
||||
]);
|
||||
let workers = context.serviceWorkers();
|
||||
expect(workers.length).toBe(1);
|
||||
|
||||
describe('Chromium-Specific Page Tests', function() {
|
||||
it('Page.route should work with intervention headers', async({server, page}) => {
|
||||
server.setRoute('/intervention', (req, res) => res.end(`
|
||||
<script>
|
||||
document.write('<script src="${server.CROSS_PROCESS_PREFIX}/intervention.js">' + '</scr' + 'ipt>');
|
||||
</script>
|
||||
`));
|
||||
server.setRedirect('/intervention.js', '/redirect.js');
|
||||
let serverRequest = null;
|
||||
server.setRoute('/redirect.js', (req, res) => {
|
||||
serverRequest = req;
|
||||
res.end('console.log(1);');
|
||||
});
|
||||
|
||||
await page.route('*', route => route.continue());
|
||||
await page.goto(server.PREFIX + '/intervention');
|
||||
// Check for feature URL substring rather than https://www.chromestatus.com to
|
||||
// make it work with Edgium.
|
||||
expect(serverRequest.headers.intervention).toContain('feature/5718547946799104');
|
||||
});
|
||||
const [worker2] = await Promise.all([
|
||||
context.waitForEvent('serviceworker'),
|
||||
page.goto(server.CROSS_PROCESS_PREFIX + '/serviceworkers/empty/sw.html')
|
||||
]);
|
||||
workers = context.serviceWorkers();
|
||||
expect(workers.length).toBe(2);
|
||||
expect(workers).toContain(worker1);
|
||||
expect(workers).toContain(worker2);
|
||||
});
|
||||
it('should not create a worker from a shared worker', async({browser, page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let serviceWorkerCreated;
|
||||
context.once('serviceworker', () => serviceWorkerCreated = true);
|
||||
await page.evaluate(() => {
|
||||
new SharedWorker('data:text/javascript,console.log("hi")');
|
||||
});
|
||||
expect(serviceWorkerCreated).not.toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
describe('Chromium-Specific Page Tests', function() {
|
||||
it('Page.route should work with intervention headers', async({server, page}) => {
|
||||
server.setRoute('/intervention', (req, res) => res.end(`
|
||||
<script>
|
||||
document.write('<script src="${server.CROSS_PROCESS_PREFIX}/intervention.js">' + '</scr' + 'ipt>');
|
||||
</script>
|
||||
`));
|
||||
server.setRedirect('/intervention.js', '/redirect.js');
|
||||
let serverRequest = null;
|
||||
server.setRoute('/redirect.js', (req, res) => {
|
||||
serverRequest = req;
|
||||
res.end('console.log(1);');
|
||||
});
|
||||
|
||||
await page.route('*', route => route.continue());
|
||||
await page.goto(server.PREFIX + '/intervention');
|
||||
// Check for feature URL substring rather than https://www.chromestatus.com to
|
||||
// make it work with Edgium.
|
||||
expect(serverRequest.headers.intervention).toContain('feature/5718547946799104');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,175 +14,171 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {ChromiumTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({FFOX, CHROMIUM, WEBKIT}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('../utils').testOptions(browserType);
|
||||
|
||||
describe('JSCoverage', function() {
|
||||
it('should work', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' });
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/jscoverage/simple.html');
|
||||
expect(coverage[0].functions.find(f => f.functionName === 'foo').ranges[0].count).toEqual(1);
|
||||
});
|
||||
it('should report sourceURLs', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/sourceurl.html');
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toBe('nicename.js');
|
||||
});
|
||||
it('should ignore eval() scripts by default', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/eval.html');
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
});
|
||||
it('shouldn\'t ignore eval() scripts if reportAnonymousScripts is true', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage({reportAnonymousScripts: true});
|
||||
await page.goto(server.PREFIX + '/jscoverage/eval.html');
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.find(entry => entry.url.startsWith('debugger://'))).not.toBe(null);
|
||||
expect(coverage.length).toBe(2);
|
||||
});
|
||||
it('should ignore playwright internal scripts if reportAnonymousScripts is true', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage({reportAnonymousScripts: true});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate('console.log("foo")');
|
||||
await page.evaluate(() => console.log('bar'));
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
it('should report multiple scripts', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
describe('JSCoverage', function() {
|
||||
it('should work', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' });
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/jscoverage/simple.html');
|
||||
expect(coverage[0].functions.find(f => f.functionName === 'foo').ranges[0].count).toEqual(1);
|
||||
});
|
||||
it('should report sourceURLs', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/sourceurl.html');
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toBe('nicename.js');
|
||||
});
|
||||
it('should ignore eval() scripts by default', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/eval.html');
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
});
|
||||
it('shouldn\'t ignore eval() scripts if reportAnonymousScripts is true', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage({reportAnonymousScripts: true});
|
||||
await page.goto(server.PREFIX + '/jscoverage/eval.html');
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.find(entry => entry.url.startsWith('debugger://'))).not.toBe(null);
|
||||
expect(coverage.length).toBe(2);
|
||||
});
|
||||
it('should ignore playwright internal scripts if reportAnonymousScripts is true', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage({reportAnonymousScripts: true});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate('console.log("foo")');
|
||||
await page.evaluate(() => console.log('bar'));
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
it('should report multiple scripts', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/multiple.html');
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(2);
|
||||
coverage.sort((a, b) => a.url.localeCompare(b.url));
|
||||
expect(coverage[0].url).toContain('/jscoverage/script1.js');
|
||||
expect(coverage[1].url).toContain('/jscoverage/script2.js');
|
||||
});
|
||||
describe('resetOnNavigation', function() {
|
||||
it('should report scripts across navigations when disabled', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage({resetOnNavigation: false});
|
||||
await page.goto(server.PREFIX + '/jscoverage/multiple.html');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(2);
|
||||
coverage.sort((a, b) => a.url.localeCompare(b.url));
|
||||
expect(coverage[0].url).toContain('/jscoverage/script1.js');
|
||||
expect(coverage[1].url).toContain('/jscoverage/script2.js');
|
||||
});
|
||||
describe('resetOnNavigation', function() {
|
||||
it('should report scripts across navigations when disabled', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage({resetOnNavigation: false});
|
||||
await page.goto(server.PREFIX + '/jscoverage/multiple.html');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(2);
|
||||
});
|
||||
it('should NOT report scripts across navigations when enabled', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage(); // Enabled by default.
|
||||
await page.goto(server.PREFIX + '/jscoverage/multiple.html');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
});
|
||||
it('should not hang when there is a debugger statement', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
it('should NOT report scripts across navigations when enabled', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage(); // Enabled by default.
|
||||
await page.goto(server.PREFIX + '/jscoverage/multiple.html');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
debugger; // eslint-disable-line no-debugger
|
||||
});
|
||||
await page.coverage.stopJSCoverage();
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
});
|
||||
it('should not hang when there is a debugger statement', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
debugger; // eslint-disable-line no-debugger
|
||||
});
|
||||
await page.coverage.stopJSCoverage();
|
||||
});
|
||||
});
|
||||
|
||||
describe('CSSCoverage', function() {
|
||||
it('should work', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/simple.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/csscoverage/simple.html');
|
||||
expect(coverage[0].ranges).toEqual([
|
||||
{start: 1, end: 22}
|
||||
]);
|
||||
const range = coverage[0].ranges[0];
|
||||
expect(coverage[0].text.substring(range.start, range.end)).toBe('div { color: green; }');
|
||||
});
|
||||
it('should report sourceURLs', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/sourceurl.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toBe('nicename.css');
|
||||
});
|
||||
it('should report multiple stylesheets', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
describe('CSSCoverage', function() {
|
||||
it('should work', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/simple.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/csscoverage/simple.html');
|
||||
expect(coverage[0].ranges).toEqual([
|
||||
{start: 1, end: 22}
|
||||
]);
|
||||
const range = coverage[0].ranges[0];
|
||||
expect(coverage[0].text.substring(range.start, range.end)).toBe('div { color: green; }');
|
||||
});
|
||||
it('should report sourceURLs', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/sourceurl.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toBe('nicename.css');
|
||||
});
|
||||
it('should report multiple stylesheets', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/multiple.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(2);
|
||||
coverage.sort((a, b) => a.url.localeCompare(b.url));
|
||||
expect(coverage[0].url).toContain('/csscoverage/stylesheet1.css');
|
||||
expect(coverage[1].url).toContain('/csscoverage/stylesheet2.css');
|
||||
});
|
||||
it('should report stylesheets that have no coverage', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/unused.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toBe('unused.css');
|
||||
expect(coverage[0].ranges.length).toBe(0);
|
||||
});
|
||||
it('should work with media queries', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/media.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/csscoverage/media.html');
|
||||
expect(coverage[0].ranges).toEqual([
|
||||
{start: 17, end: 38}
|
||||
]);
|
||||
});
|
||||
it('should work with complicated usecases', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/involved.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(JSON.stringify(coverage, null, 2).replace(/:\d{4}\//g, ':<PORT>/')).toBeGolden('csscoverage-involved.txt');
|
||||
});
|
||||
it('should ignore injected stylesheets', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.addStyleTag({content: 'body { margin: 10px;}'});
|
||||
// trigger style recalc
|
||||
const margin = await page.evaluate(() => window.getComputedStyle(document.body).margin);
|
||||
expect(margin).toBe('10px');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
describe('resetOnNavigation', function() {
|
||||
it('should report stylesheets across navigations', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage({resetOnNavigation: false});
|
||||
await page.goto(server.PREFIX + '/csscoverage/multiple.html');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(2);
|
||||
coverage.sort((a, b) => a.url.localeCompare(b.url));
|
||||
expect(coverage[0].url).toContain('/csscoverage/stylesheet1.css');
|
||||
expect(coverage[1].url).toContain('/csscoverage/stylesheet2.css');
|
||||
});
|
||||
it('should report stylesheets that have no coverage', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/unused.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toBe('unused.css');
|
||||
expect(coverage[0].ranges.length).toBe(0);
|
||||
});
|
||||
it('should work with media queries', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/media.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/csscoverage/media.html');
|
||||
expect(coverage[0].ranges).toEqual([
|
||||
{start: 17, end: 38}
|
||||
]);
|
||||
});
|
||||
it('should work with complicated usecases', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/involved.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(JSON.stringify(coverage, null, 2).replace(/:\d{4}\//g, ':<PORT>/')).toBeGolden('csscoverage-involved.txt');
|
||||
});
|
||||
it('should ignore injected stylesheets', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.addStyleTag({content: 'body { margin: 10px;}'});
|
||||
// trigger style recalc
|
||||
const margin = await page.evaluate(() => window.getComputedStyle(document.body).margin);
|
||||
expect(margin).toBe('10px');
|
||||
it('should NOT report scripts across navigations', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage(); // Enabled by default.
|
||||
await page.goto(server.PREFIX + '/csscoverage/multiple.html');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
describe('resetOnNavigation', function() {
|
||||
it('should report stylesheets across navigations', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage({resetOnNavigation: false});
|
||||
await page.goto(server.PREFIX + '/csscoverage/multiple.html');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(2);
|
||||
});
|
||||
it('should NOT report scripts across navigations', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage(); // Enabled by default.
|
||||
await page.goto(server.PREFIX + '/csscoverage/multiple.html');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
});
|
||||
it('should work with a recently loaded stylesheet', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.evaluate(async url => {
|
||||
document.body.textContent = 'hello, world';
|
||||
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = url;
|
||||
document.head.appendChild(link);
|
||||
await new Promise(x => link.onload = x);
|
||||
await new Promise(f => requestAnimationFrame(f));
|
||||
}, server.PREFIX + '/csscoverage/stylesheet1.css');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
});
|
||||
});
|
||||
};
|
||||
it('should work with a recently loaded stylesheet', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.evaluate(async url => {
|
||||
document.body.textContent = 'hello, world';
|
||||
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = url;
|
||||
document.head.appendChild(link);
|
||||
await new Promise(x => link.onload = x);
|
||||
await new Promise(f => requestAnimationFrame(f));
|
||||
}, server.PREFIX + '/csscoverage/stylesheet1.css');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,87 +14,80 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const util = require('util');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const { makeUserDataDir, removeUserDataDir } = require('../utils');
|
||||
const utils = require('../utils');
|
||||
const {makeUserDataDir, removeUserDataDir} = utils;
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN, defaultBrowserOptions} = utils.testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {TestSuite}
|
||||
*/
|
||||
module.exports.describe = function({defaultBrowserOptions, browserType, WIN}) {
|
||||
const headfulOptions = Object.assign({}, defaultBrowserOptions, {
|
||||
headless: false
|
||||
});
|
||||
const extensionPath = path.join(__dirname, '..', 'assets', 'simple-extension');
|
||||
const extensionOptions = Object.assign({}, defaultBrowserOptions, {
|
||||
headless: false,
|
||||
args: [
|
||||
`--disable-extensions-except=${extensionPath}`,
|
||||
`--load-extension=${extensionPath}`,
|
||||
],
|
||||
});
|
||||
|
||||
const headfulOptions = Object.assign({}, defaultBrowserOptions, {
|
||||
headless: false
|
||||
describe('launcher', function() {
|
||||
it('should throw with remote-debugging-pipe argument', async({browserType}) => {
|
||||
const options = Object.assign({}, defaultBrowserOptions);
|
||||
options.args = ['--remote-debugging-pipe'].concat(options.args || []);
|
||||
const error = await browserType.launchServer(options).catch(e => e);
|
||||
expect(error.message).toContain('Playwright manages remote debugging connection itself');
|
||||
});
|
||||
const extensionPath = path.join(__dirname, '..', 'assets', 'simple-extension');
|
||||
const extensionOptions = Object.assign({}, defaultBrowserOptions, {
|
||||
headless: false,
|
||||
args: [
|
||||
`--disable-extensions-except=${extensionPath}`,
|
||||
`--load-extension=${extensionPath}`,
|
||||
],
|
||||
it('should throw with remote-debugging-port argument', async({browserType}) => {
|
||||
const options = Object.assign({}, defaultBrowserOptions);
|
||||
options.args = ['--remote-debugging-port=9222'].concat(options.args || []);
|
||||
const error = await browserType.launchServer(options).catch(e => e);
|
||||
expect(error.message).toContain('Playwright manages remote debugging connection itself');
|
||||
});
|
||||
it('should open devtools when "devtools: true" option is given', async({browserType}) => {
|
||||
const browser = await browserType.launch(Object.assign({devtools: true}, headfulOptions));
|
||||
const context = await browser.newContext();
|
||||
const browserSession = await browser.newBrowserCDPSession();
|
||||
await browserSession.send('Target.setDiscoverTargets', { discover: true });
|
||||
const devtoolsPagePromise = new Promise(fulfill => browserSession.on('Target.targetCreated', async ({targetInfo}) => {
|
||||
if (targetInfo.type === 'other' && targetInfo.url.includes('devtools://'))
|
||||
fulfill();
|
||||
}));
|
||||
await Promise.all([
|
||||
devtoolsPagePromise,
|
||||
context.newPage()
|
||||
]);
|
||||
await browser.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('launcher', function() {
|
||||
it('should throw with remote-debugging-pipe argument', async() => {
|
||||
const options = Object.assign({}, defaultBrowserOptions);
|
||||
options.args = ['--remote-debugging-pipe'].concat(options.args || []);
|
||||
const error = await browserType.launchServer(options).catch(e => e);
|
||||
expect(error.message).toContain('Playwright manages remote debugging connection itself');
|
||||
});
|
||||
it('should throw with remote-debugging-port argument', async() => {
|
||||
const options = Object.assign({}, defaultBrowserOptions);
|
||||
options.args = ['--remote-debugging-port=9222'].concat(options.args || []);
|
||||
const error = await browserType.launchServer(options).catch(e => e);
|
||||
expect(error.message).toContain('Playwright manages remote debugging connection itself');
|
||||
});
|
||||
it('should open devtools when "devtools: true" option is given', async({server}) => {
|
||||
const browser = await browserType.launch(Object.assign({devtools: true}, headfulOptions));
|
||||
const context = await browser.newContext();
|
||||
const browserSession = await browser.newBrowserCDPSession();
|
||||
await browserSession.send('Target.setDiscoverTargets', { discover: true });
|
||||
const devtoolsPagePromise = new Promise(fulfill => browserSession.on('Target.targetCreated', async ({targetInfo}) => {
|
||||
if (targetInfo.type === 'other' && targetInfo.url.includes('devtools://'))
|
||||
fulfill();
|
||||
}));
|
||||
await Promise.all([
|
||||
devtoolsPagePromise,
|
||||
context.newPage()
|
||||
]);
|
||||
await browser.close();
|
||||
});
|
||||
describe('extensions', () => {
|
||||
it('should return background pages', async({browserType}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const context = await browserType.launchPersistentContext(userDataDir, extensionOptions);
|
||||
const backgroundPages = context.backgroundPages();
|
||||
let backgroundPage = backgroundPages.length
|
||||
? backgroundPages[0]
|
||||
: await context.waitForEvent('backgroundpage');
|
||||
expect(backgroundPage).toBeTruthy();
|
||||
expect(context.backgroundPages()).toContain(backgroundPage);
|
||||
expect(context.pages()).not.toContain(backgroundPage);
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
});
|
||||
|
||||
describe('extensions', () => {
|
||||
it('should return background pages', async() => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const context = await browserType.launchPersistentContext(userDataDir, extensionOptions);
|
||||
const backgroundPages = context.backgroundPages();
|
||||
let backgroundPage = backgroundPages.length
|
||||
? backgroundPages[0]
|
||||
: await context.waitForEvent('backgroundpage');
|
||||
expect(backgroundPage).toBeTruthy();
|
||||
expect(context.backgroundPages()).toContain(backgroundPage);
|
||||
expect(context.pages()).not.toContain(backgroundPage);
|
||||
await removeUserDataDir(userDataDir);
|
||||
describe('BrowserContext', function() {
|
||||
it('should not create pages automatically', async ({browserType}) => {
|
||||
const browser = await browserType.launch();
|
||||
const browserSession = await browser.newBrowserCDPSession();
|
||||
const targets = [];
|
||||
browserSession.on('Target.targetCreated', async ({targetInfo}) => {
|
||||
if (targetInfo.type !== 'browser')
|
||||
targets.push(targetInfo);
|
||||
});
|
||||
await browserSession.send('Target.setDiscoverTargets', { discover: true });
|
||||
await browser.newContext();
|
||||
await browser.close();
|
||||
expect(targets.length).toBe(0);
|
||||
});
|
||||
|
||||
describe('BrowserContext', function() {
|
||||
it('should not create pages automatically', async function() {
|
||||
const browser = await browserType.launch();
|
||||
const browserSession = await browser.newBrowserCDPSession();
|
||||
const targets = [];
|
||||
browserSession.on('Target.targetCreated', async ({targetInfo}) => {
|
||||
if (targetInfo.type !== 'browser')
|
||||
targets.push(targetInfo);
|
||||
});
|
||||
await browserSession.send('Target.setDiscoverTargets', { discover: true });
|
||||
await browser.newContext();
|
||||
await browser.close();
|
||||
expect(targets.length).toBe(0);
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,92 +14,88 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {ChromiumTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({defaultBrowserOptions, browserType, FFOX, CHROMIUM, WEBKIT}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT, defaultBrowserOptions} = require('../utils').testOptions(browserType);
|
||||
|
||||
const headfulOptions = Object.assign({}, defaultBrowserOptions, {
|
||||
headless: false
|
||||
});
|
||||
const headfulOptions = Object.assign({}, defaultBrowserOptions, {
|
||||
headless: false
|
||||
});
|
||||
|
||||
describe('OOPIF', function() {
|
||||
beforeAll(async function(state) {
|
||||
state.browser = await browserType.launch(Object.assign({}, defaultBrowserOptions, {
|
||||
args: (defaultBrowserOptions.args || []).concat(['--site-per-process']),
|
||||
}));
|
||||
});
|
||||
beforeEach(async function(state) {
|
||||
state.context = await state.browser.newContext();
|
||||
state.page = await state.context.newPage();
|
||||
});
|
||||
afterEach(async function(state) {
|
||||
await state.context.close();
|
||||
state.page = null;
|
||||
state.context = null;
|
||||
});
|
||||
afterAll(async function(state) {
|
||||
await state.browser.close();
|
||||
state.browser = null;
|
||||
});
|
||||
it('should report oopif frames', async function({browser, page, server, context}) {
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
expect(page.frames().length).toBe(2);
|
||||
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
});
|
||||
it('should handle remote -> local -> remote transitions', async function({browser, page, server, context}) {
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
await Promise.all([
|
||||
page.frames()[1].waitForNavigation(),
|
||||
page.evaluate(() => goLocal()),
|
||||
]);
|
||||
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.PREFIX + '/grid.html');
|
||||
await Promise.all([
|
||||
page.frames()[1].waitForNavigation(),
|
||||
page.evaluate(() => goRemote()),
|
||||
]);
|
||||
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
});
|
||||
it('should load oopif iframes with subresources and request interception', async function({browser, page, server, context}) {
|
||||
await page.route('**/*', route => route.continue());
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
});
|
||||
// @see https://github.com/microsoft/playwright/issues/1240
|
||||
xit('should click a button when it overlays oopif', async function({browser, page, server, context}) {
|
||||
await page.goto(server.PREFIX + '/button-overlay-oopif.html');
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => window.BUTTON_CLICKED)).toBe(true);
|
||||
});
|
||||
it('should report google.com frame with headful', async({server}) => {
|
||||
// TODO: Support OOOPIF. @see https://github.com/GoogleChrome/puppeteer/issues/2548
|
||||
// https://google.com is isolated by default in Chromium embedder.
|
||||
const browser = await browserType.launch(headfulOptions);
|
||||
const page = await browser.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.route('**/*', route => {
|
||||
route.fulfill({body: 'YO, GOOGLE.COM'});
|
||||
});
|
||||
await page.evaluate(() => {
|
||||
const frame = document.createElement('iframe');
|
||||
frame.setAttribute('src', 'https://google.com/');
|
||||
document.body.appendChild(frame);
|
||||
return new Promise(x => frame.onload = x);
|
||||
});
|
||||
await page.waitForSelector('iframe[src="https://google.com/"]');
|
||||
const urls = page.frames().map(frame => frame.url());
|
||||
expect(urls).toEqual([
|
||||
server.EMPTY_PAGE,
|
||||
'https://google.com/'
|
||||
]);
|
||||
await browser.close();
|
||||
});
|
||||
describe('OOPIF', function() {
|
||||
beforeAll(async function(state) {
|
||||
state.browser = await state.browserType.launch(Object.assign({}, defaultBrowserOptions, {
|
||||
args: (defaultBrowserOptions.args || []).concat(['--site-per-process']),
|
||||
}));
|
||||
});
|
||||
};
|
||||
beforeEach(async function(state) {
|
||||
state.context = await state.browser.newContext();
|
||||
state.page = await state.context.newPage();
|
||||
});
|
||||
afterEach(async function(state) {
|
||||
await state.context.close();
|
||||
state.page = null;
|
||||
state.context = null;
|
||||
});
|
||||
afterAll(async function(state) {
|
||||
await state.browser.close();
|
||||
state.browser = null;
|
||||
});
|
||||
it('should report oopif frames', async function({browser, page, server, context}) {
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
expect(page.frames().length).toBe(2);
|
||||
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
});
|
||||
it('should handle remote -> local -> remote transitions', async function({browser, page, server, context}) {
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
await Promise.all([
|
||||
page.frames()[1].waitForNavigation(),
|
||||
page.evaluate(() => goLocal()),
|
||||
]);
|
||||
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.PREFIX + '/grid.html');
|
||||
await Promise.all([
|
||||
page.frames()[1].waitForNavigation(),
|
||||
page.evaluate(() => goRemote()),
|
||||
]);
|
||||
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
});
|
||||
it('should load oopif iframes with subresources and request interception', async function({browser, page, server, context}) {
|
||||
await page.route('**/*', route => route.continue());
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
});
|
||||
// @see https://github.com/microsoft/playwright/issues/1240
|
||||
xit('should click a button when it overlays oopif', async function({browser, page, server, context}) {
|
||||
await page.goto(server.PREFIX + '/button-overlay-oopif.html');
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => window.BUTTON_CLICKED)).toBe(true);
|
||||
});
|
||||
it('should report google.com frame with headful', async({browserType, server}) => {
|
||||
// TODO: Support OOOPIF. @see https://github.com/GoogleChrome/puppeteer/issues/2548
|
||||
// https://google.com is isolated by default in Chromium embedder.
|
||||
const browser = await browserType.launch(headfulOptions);
|
||||
const page = await browser.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.route('**/*', route => {
|
||||
route.fulfill({body: 'YO, GOOGLE.COM'});
|
||||
});
|
||||
await page.evaluate(() => {
|
||||
const frame = document.createElement('iframe');
|
||||
frame.setAttribute('src', 'https://google.com/');
|
||||
document.body.appendChild(frame);
|
||||
return new Promise(x => frame.onload = x);
|
||||
});
|
||||
await page.waitForSelector('iframe[src="https://google.com/"]');
|
||||
const urls = page.frames().map(frame => frame.url());
|
||||
expect(urls).toEqual([
|
||||
server.EMPTY_PAGE,
|
||||
'https://google.com/'
|
||||
]);
|
||||
await browser.close();
|
||||
});
|
||||
});
|
||||
|
||||
async function countOOPIFs(browser) {
|
||||
const browserSession = await browser.newBrowserCDPSession();
|
||||
|
|
|
|||
|
|
@ -16,19 +16,14 @@
|
|||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const {FFOX, CHROMIUM, WEBKIT, headless, OUTPUT_DIR} = require('../utils').testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {ChromiumTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({headless, OUTPUT_DIR}) {
|
||||
|
||||
// Printing to pdf is currently only supported in headless
|
||||
describe.fail(!headless)('Page.pdf', function() {
|
||||
it('should be able to save file', async({page, server}) => {
|
||||
const outputFile = path.join(OUTPUT_DIR, 'output.pdf');
|
||||
await page.pdf({path: outputFile});
|
||||
expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0);
|
||||
fs.unlinkSync(outputFile);
|
||||
});
|
||||
// Printing to pdf is currently only supported in headless
|
||||
describe.fail(!headless)('Page.pdf', function() {
|
||||
it('should be able to save file', async({page, server}) => {
|
||||
const outputFile = path.join(OUTPUT_DIR, 'output.pdf');
|
||||
await page.pdf({path: outputFile});
|
||||
expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0);
|
||||
fs.unlinkSync(outputFile);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,94 +14,90 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {ChromiumTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({FFOX, CHROMIUM, WEBKIT}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('../utils').testOptions(browserType);
|
||||
|
||||
describe('ChromiumBrowserContext.createSession', function() {
|
||||
it('should work', async function({page, browser, server}) {
|
||||
const client = await page.context().newCDPSession(page);
|
||||
describe('ChromiumBrowserContext.createSession', function() {
|
||||
it('should work', async function({page, browser, server}) {
|
||||
const client = await page.context().newCDPSession(page);
|
||||
|
||||
await Promise.all([
|
||||
client.send('Runtime.enable'),
|
||||
client.send('Runtime.evaluate', { expression: 'window.foo = "bar"' })
|
||||
]);
|
||||
const foo = await page.evaluate(() => window.foo);
|
||||
expect(foo).toBe('bar');
|
||||
});
|
||||
it('should send events', async function({page, browser, server}) {
|
||||
const client = await page.context().newCDPSession(page);
|
||||
await client.send('Network.enable');
|
||||
const events = [];
|
||||
client.on('Network.requestWillBeSent', event => events.push(event));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(events.length).toBe(1);
|
||||
});
|
||||
it('should enable and disable domains independently', async function({page, browser, server}) {
|
||||
const client = await page.context().newCDPSession(page);
|
||||
await client.send('Runtime.enable');
|
||||
await client.send('Debugger.enable');
|
||||
// JS coverage enables and then disables Debugger domain.
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.coverage.stopJSCoverage();
|
||||
// generate a script in page and wait for the event.
|
||||
const [event] = await Promise.all([
|
||||
new Promise(f => client.on('Debugger.scriptParsed', f)),
|
||||
page.evaluate('//# sourceURL=foo.js')
|
||||
]);
|
||||
// expect events to be dispatched.
|
||||
expect(event.url).toBe('foo.js');
|
||||
});
|
||||
it('should be able to detach session', async function({page, browser, server}) {
|
||||
const client = await page.context().newCDPSession(page);
|
||||
await client.send('Runtime.enable');
|
||||
const evalResponse = await client.send('Runtime.evaluate', {expression: '1 + 2', returnByValue: true});
|
||||
expect(evalResponse.result.value).toBe(3);
|
||||
await client.detach();
|
||||
let error = null;
|
||||
try {
|
||||
await client.send('Runtime.evaluate', {expression: '3 + 1', returnByValue: true});
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toContain('Session closed.');
|
||||
});
|
||||
it('should throw nice errors', async function({page, browser}) {
|
||||
const client = await page.context().newCDPSession(page);
|
||||
const error = await theSourceOfTheProblems().catch(error => error);
|
||||
expect(error.stack).toContain('theSourceOfTheProblems');
|
||||
expect(error.message).toContain('ThisCommand.DoesNotExist');
|
||||
|
||||
async function theSourceOfTheProblems() {
|
||||
await client.send('ThisCommand.DoesNotExist');
|
||||
}
|
||||
});
|
||||
it('should not break page.close()', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const session = await page.context().newCDPSession(page);
|
||||
await session.detach();
|
||||
await page.close();
|
||||
await context.close();
|
||||
});
|
||||
it('should detach when page closes', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const session = await context.newCDPSession(page);
|
||||
await page.close();
|
||||
let error;
|
||||
await session.detach().catch(e => error = e);
|
||||
expect(error).toBeTruthy('Calling detach on a closed page\'s session should throw');
|
||||
await context.close();
|
||||
});
|
||||
await Promise.all([
|
||||
client.send('Runtime.enable'),
|
||||
client.send('Runtime.evaluate', { expression: 'window.foo = "bar"' })
|
||||
]);
|
||||
const foo = await page.evaluate(() => window.foo);
|
||||
expect(foo).toBe('bar');
|
||||
});
|
||||
describe('ChromiumBrowser.newBrowserCDPSession', function() {
|
||||
it('should work', async function({page, browser, server}) {
|
||||
const session = await browser.newBrowserCDPSession();
|
||||
const version = await session.send('Browser.getVersion');
|
||||
expect(version.userAgent).toBeTruthy();
|
||||
await session.detach();
|
||||
});
|
||||
it('should send events', async function({page, browser, server}) {
|
||||
const client = await page.context().newCDPSession(page);
|
||||
await client.send('Network.enable');
|
||||
const events = [];
|
||||
client.on('Network.requestWillBeSent', event => events.push(event));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(events.length).toBe(1);
|
||||
});
|
||||
};
|
||||
it('should enable and disable domains independently', async function({page, browser, server}) {
|
||||
const client = await page.context().newCDPSession(page);
|
||||
await client.send('Runtime.enable');
|
||||
await client.send('Debugger.enable');
|
||||
// JS coverage enables and then disables Debugger domain.
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.coverage.stopJSCoverage();
|
||||
// generate a script in page and wait for the event.
|
||||
const [event] = await Promise.all([
|
||||
new Promise(f => client.on('Debugger.scriptParsed', f)),
|
||||
page.evaluate('//# sourceURL=foo.js')
|
||||
]);
|
||||
// expect events to be dispatched.
|
||||
expect(event.url).toBe('foo.js');
|
||||
});
|
||||
it('should be able to detach session', async function({page, browser, server}) {
|
||||
const client = await page.context().newCDPSession(page);
|
||||
await client.send('Runtime.enable');
|
||||
const evalResponse = await client.send('Runtime.evaluate', {expression: '1 + 2', returnByValue: true});
|
||||
expect(evalResponse.result.value).toBe(3);
|
||||
await client.detach();
|
||||
let error = null;
|
||||
try {
|
||||
await client.send('Runtime.evaluate', {expression: '3 + 1', returnByValue: true});
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toContain('Session closed.');
|
||||
});
|
||||
it('should throw nice errors', async function({page, browser}) {
|
||||
const client = await page.context().newCDPSession(page);
|
||||
const error = await theSourceOfTheProblems().catch(error => error);
|
||||
expect(error.stack).toContain('theSourceOfTheProblems');
|
||||
expect(error.message).toContain('ThisCommand.DoesNotExist');
|
||||
|
||||
async function theSourceOfTheProblems() {
|
||||
await client.send('ThisCommand.DoesNotExist');
|
||||
}
|
||||
});
|
||||
it('should not break page.close()', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const session = await page.context().newCDPSession(page);
|
||||
await session.detach();
|
||||
await page.close();
|
||||
await context.close();
|
||||
});
|
||||
it('should detach when page closes', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const session = await context.newCDPSession(page);
|
||||
await page.close();
|
||||
let error;
|
||||
await session.detach().catch(e => error = e);
|
||||
expect(error).toBeTruthy('Calling detach on a closed page\'s session should throw');
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
describe('ChromiumBrowser.newBrowserCDPSession', function() {
|
||||
it('should work', async function({page, browser, server}) {
|
||||
const session = await browser.newBrowserCDPSession();
|
||||
const version = await session.send('Browser.getVersion');
|
||||
expect(version.userAgent).toBeTruthy();
|
||||
await session.detach();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,67 +16,62 @@
|
|||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const {FFOX, CHROMIUM, WEBKIT, OUTPUT_DIR, defaultBrowserOptions} = require('../utils').testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {ChromiumTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({defaultBrowserOptions, browserType, OUTPUT_DIR}) {
|
||||
|
||||
describe('Chromium.startTracing', function() {
|
||||
beforeEach(async function(state) {
|
||||
state.outputFile = path.join(OUTPUT_DIR, `trace-${state.parallelIndex}.json`);
|
||||
state.browser = await browserType.launch(defaultBrowserOptions);
|
||||
state.page = await state.browser.newPage();
|
||||
});
|
||||
afterEach(async function(state) {
|
||||
await state.browser.close();
|
||||
state.browser = null;
|
||||
state.page = null;
|
||||
if (fs.existsSync(state.outputFile)) {
|
||||
fs.unlinkSync(state.outputFile);
|
||||
state.outputFile = null;
|
||||
}
|
||||
});
|
||||
it('should output a trace', async({browser, page, server, outputFile}) => {
|
||||
await browser.startTracing(page, {screenshots: true, path: outputFile});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await browser.stopTracing();
|
||||
expect(fs.existsSync(outputFile)).toBe(true);
|
||||
});
|
||||
it('should run with custom categories if provided', async({browser, page, outputFile}) => {
|
||||
await browser.startTracing(page, {path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']});
|
||||
await browser.stopTracing();
|
||||
|
||||
const traceJson = JSON.parse(fs.readFileSync(outputFile).toString());
|
||||
expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires', 'Does not contain expected category');
|
||||
});
|
||||
it('should throw if tracing on two pages', async({browser, page, server, outputFile}) => {
|
||||
await browser.startTracing(page, {path: outputFile});
|
||||
const newPage = await browser.newPage();
|
||||
let error = null;
|
||||
await browser.startTracing(newPage, {path: outputFile}).catch(e => error = e);
|
||||
await newPage.close();
|
||||
expect(error).toBeTruthy();
|
||||
await browser.stopTracing();
|
||||
});
|
||||
it('should return a buffer', async({browser, page, server, outputFile}) => {
|
||||
await browser.startTracing(page, {screenshots: true, path: outputFile});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const trace = await browser.stopTracing();
|
||||
const buf = fs.readFileSync(outputFile);
|
||||
expect(trace.toString()).toEqual(buf.toString(), 'Tracing buffer mismatch');
|
||||
});
|
||||
it('should work without options', async({browser, page, server, outputFile}) => {
|
||||
await browser.startTracing(page);
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const trace = await browser.stopTracing();
|
||||
expect(trace).toBeTruthy();
|
||||
});
|
||||
it('should support a buffer without a path', async({browser, page, server}) => {
|
||||
await browser.startTracing(page, {screenshots: true});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const trace = await browser.stopTracing();
|
||||
expect(trace.toString()).toContain('screenshot', 'Does not contain screenshot');
|
||||
});
|
||||
describe('Chromium.startTracing', function() {
|
||||
beforeEach(async function(state) {
|
||||
state.outputFile = path.join(OUTPUT_DIR, `trace-${state.parallelIndex}.json`);
|
||||
state.browser = await state.browserType.launch(defaultBrowserOptions);
|
||||
state.page = await state.browser.newPage();
|
||||
});
|
||||
};
|
||||
afterEach(async function(state) {
|
||||
await state.browser.close();
|
||||
state.browser = null;
|
||||
state.page = null;
|
||||
if (fs.existsSync(state.outputFile)) {
|
||||
fs.unlinkSync(state.outputFile);
|
||||
state.outputFile = null;
|
||||
}
|
||||
});
|
||||
it('should output a trace', async({browser, page, server, outputFile}) => {
|
||||
await browser.startTracing(page, {screenshots: true, path: outputFile});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await browser.stopTracing();
|
||||
expect(fs.existsSync(outputFile)).toBe(true);
|
||||
});
|
||||
it('should run with custom categories if provided', async({browser, page, outputFile}) => {
|
||||
await browser.startTracing(page, {path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']});
|
||||
await browser.stopTracing();
|
||||
|
||||
const traceJson = JSON.parse(fs.readFileSync(outputFile).toString());
|
||||
expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires', 'Does not contain expected category');
|
||||
});
|
||||
it('should throw if tracing on two pages', async({browser, page, server, outputFile}) => {
|
||||
await browser.startTracing(page, {path: outputFile});
|
||||
const newPage = await browser.newPage();
|
||||
let error = null;
|
||||
await browser.startTracing(newPage, {path: outputFile}).catch(e => error = e);
|
||||
await newPage.close();
|
||||
expect(error).toBeTruthy();
|
||||
await browser.stopTracing();
|
||||
});
|
||||
it('should return a buffer', async({browser, page, server, outputFile}) => {
|
||||
await browser.startTracing(page, {screenshots: true, path: outputFile});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const trace = await browser.stopTracing();
|
||||
const buf = fs.readFileSync(outputFile);
|
||||
expect(trace.toString()).toEqual(buf.toString(), 'Tracing buffer mismatch');
|
||||
});
|
||||
it('should work without options', async({browser, page, server, outputFile}) => {
|
||||
await browser.startTracing(page);
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const trace = await browser.stopTracing();
|
||||
expect(trace).toBeTruthy();
|
||||
});
|
||||
it('should support a buffer without a path', async({browser, page, server}) => {
|
||||
await browser.startTracing(page, {screenshots: true});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const trace = await browser.stopTracing();
|
||||
expect(trace.toString()).toContain('screenshot', 'Does not contain screenshot');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
1179
test/click.spec.js
1179
test/click.spec.js
File diff suppressed because it is too large
Load diff
|
|
@ -15,458 +15,454 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({browserType, defaultBrowserOptions, MAC, FFOX, CHROMIUM, WEBKIT}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC, defaultBrowserOptions} = require('./utils').testOptions(browserType);
|
||||
|
||||
describe('BrowserContext.cookies', function() {
|
||||
it('should return no cookies in pristine browser context', async({context, page, server}) => {
|
||||
expect(await context.cookies()).toEqual([]);
|
||||
});
|
||||
it('should get a cookie', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
});
|
||||
expect(await context.cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('should get a non-session cookie', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
// @see https://en.wikipedia.org/wiki/Year_2038_problem
|
||||
const date = +(new Date('1/1/2038'));
|
||||
await page.evaluate(timestamp => {
|
||||
const date = new Date(timestamp);
|
||||
document.cookie = `username=John Doe;expires=${date.toUTCString()}`;
|
||||
}, date);
|
||||
expect(await context.cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: date / 1000,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('should properly report httpOnly cookie', async({context, page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'name=value;HttpOnly; Path=/');
|
||||
res.end();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies[0].httpOnly).toBe(true);
|
||||
});
|
||||
it.fail(WEBKIT && !MAC)('should properly report "Strict" sameSite cookie', async({context, page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'name=value;SameSite=Strict');
|
||||
res.end();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies[0].sameSite).toBe('Strict');
|
||||
});
|
||||
it.fail(WEBKIT && !MAC)('should properly report "Lax" sameSite cookie', async({context, page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'name=value;SameSite=Lax');
|
||||
res.end();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies[0].sameSite).toBe('Lax');
|
||||
});
|
||||
it('should get multiple cookies', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
document.cookie = 'password=1234';
|
||||
});
|
||||
const cookies = await context.cookies();
|
||||
cookies.sort((a, b) => a.name.localeCompare(b.name));
|
||||
expect(cookies).toEqual([
|
||||
{
|
||||
name: 'password',
|
||||
value: '1234',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
},
|
||||
{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
},
|
||||
]);
|
||||
});
|
||||
it('should get cookies from multiple urls', async({context}) => {
|
||||
await context.addCookies([{
|
||||
url: 'https://foo.com',
|
||||
name: 'doggo',
|
||||
value: 'woofs',
|
||||
}, {
|
||||
url: 'https://bar.com',
|
||||
name: 'catto',
|
||||
value: 'purrs',
|
||||
}, {
|
||||
url: 'https://baz.com',
|
||||
name: 'birdo',
|
||||
value: 'tweets',
|
||||
}]);
|
||||
const cookies = await context.cookies(['https://foo.com', 'https://baz.com']);
|
||||
cookies.sort((a, b) => a.name.localeCompare(b.name));
|
||||
expect(cookies).toEqual([{
|
||||
name: 'birdo',
|
||||
value: 'tweets',
|
||||
domain: 'baz.com',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: true,
|
||||
sameSite: 'None',
|
||||
}, {
|
||||
name: 'doggo',
|
||||
value: 'woofs',
|
||||
domain: 'foo.com',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: true,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
describe('BrowserContext.cookies', function() {
|
||||
it('should return no cookies in pristine browser context', async({context, page, server}) => {
|
||||
expect(await context.cookies()).toEqual([]);
|
||||
});
|
||||
|
||||
describe('BrowserContext.addCookies', function() {
|
||||
it('should work', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
it('should get a cookie', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
});
|
||||
expect(await context.cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('should get a non-session cookie', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
// @see https://en.wikipedia.org/wiki/Year_2038_problem
|
||||
const date = +(new Date('1/1/2038'));
|
||||
await page.evaluate(timestamp => {
|
||||
const date = new Date(timestamp);
|
||||
document.cookie = `username=John Doe;expires=${date.toUTCString()}`;
|
||||
}, date);
|
||||
expect(await context.cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: date / 1000,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('should properly report httpOnly cookie', async({context, page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'name=value;HttpOnly; Path=/');
|
||||
res.end();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies[0].httpOnly).toBe(true);
|
||||
});
|
||||
it.fail(WEBKIT && !MAC)('should properly report "Strict" sameSite cookie', async({context, page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'name=value;SameSite=Strict');
|
||||
res.end();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies[0].sameSite).toBe('Strict');
|
||||
});
|
||||
it.fail(WEBKIT && !MAC)('should properly report "Lax" sameSite cookie', async({context, page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'name=value;SameSite=Lax');
|
||||
res.end();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies[0].sameSite).toBe('Lax');
|
||||
});
|
||||
it('should get multiple cookies', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
document.cookie = 'password=1234';
|
||||
});
|
||||
const cookies = await context.cookies();
|
||||
cookies.sort((a, b) => a.name.localeCompare(b.name));
|
||||
expect(cookies).toEqual([
|
||||
{
|
||||
name: 'password',
|
||||
value: '123456'
|
||||
}]);
|
||||
expect(await page.evaluate(() => document.cookie)).toEqual('password=123456');
|
||||
});
|
||||
it('should roundtrip cookie', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
// @see https://en.wikipedia.org/wiki/Year_2038_problem
|
||||
const date = +(new Date('1/1/2038'));
|
||||
await page.evaluate(timestamp => {
|
||||
const date = new Date(timestamp);
|
||||
document.cookie = `username=John Doe;expires=${date.toUTCString()}`;
|
||||
}, date);
|
||||
const cookies = await context.cookies();
|
||||
await context.clearCookies();
|
||||
expect(await context.cookies()).toEqual([]);
|
||||
await context.addCookies(cookies);
|
||||
expect(await context.cookies()).toEqual(cookies);
|
||||
});
|
||||
it('should send cookie header', async({server, context}) => {
|
||||
let cookie = '';
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
cookie = req.headers.cookie;
|
||||
res.end();
|
||||
});
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'cookie', value: 'value'}]);
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(cookie).toBe('cookie=value');
|
||||
});
|
||||
it('should isolate cookies in browser contexts', async({context, server, browser}) => {
|
||||
const anotherContext = await browser.newContext();
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'isolatecookie', value: 'page1value'}]);
|
||||
await anotherContext.addCookies([{url: server.EMPTY_PAGE, name: 'isolatecookie', value: 'page2value'}]);
|
||||
value: '1234',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
},
|
||||
{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
},
|
||||
]);
|
||||
});
|
||||
it('should get cookies from multiple urls', async({context}) => {
|
||||
await context.addCookies([{
|
||||
url: 'https://foo.com',
|
||||
name: 'doggo',
|
||||
value: 'woofs',
|
||||
}, {
|
||||
url: 'https://bar.com',
|
||||
name: 'catto',
|
||||
value: 'purrs',
|
||||
}, {
|
||||
url: 'https://baz.com',
|
||||
name: 'birdo',
|
||||
value: 'tweets',
|
||||
}]);
|
||||
const cookies = await context.cookies(['https://foo.com', 'https://baz.com']);
|
||||
cookies.sort((a, b) => a.name.localeCompare(b.name));
|
||||
expect(cookies).toEqual([{
|
||||
name: 'birdo',
|
||||
value: 'tweets',
|
||||
domain: 'baz.com',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: true,
|
||||
sameSite: 'None',
|
||||
}, {
|
||||
name: 'doggo',
|
||||
value: 'woofs',
|
||||
domain: 'foo.com',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: true,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
const cookies1 = await context.cookies();
|
||||
const cookies2 = await anotherContext.cookies();
|
||||
expect(cookies1.length).toBe(1);
|
||||
expect(cookies2.length).toBe(1);
|
||||
expect(cookies1[0].name).toBe('isolatecookie');
|
||||
expect(cookies1[0].value).toBe('page1value');
|
||||
expect(cookies2[0].name).toBe('isolatecookie');
|
||||
expect(cookies2[0].value).toBe('page2value');
|
||||
await anotherContext.close();
|
||||
describe('BrowserContext.addCookies', function() {
|
||||
it('should work', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'password',
|
||||
value: '123456'
|
||||
}]);
|
||||
expect(await page.evaluate(() => document.cookie)).toEqual('password=123456');
|
||||
});
|
||||
it('should roundtrip cookie', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
// @see https://en.wikipedia.org/wiki/Year_2038_problem
|
||||
const date = +(new Date('1/1/2038'));
|
||||
await page.evaluate(timestamp => {
|
||||
const date = new Date(timestamp);
|
||||
document.cookie = `username=John Doe;expires=${date.toUTCString()}`;
|
||||
}, date);
|
||||
const cookies = await context.cookies();
|
||||
await context.clearCookies();
|
||||
expect(await context.cookies()).toEqual([]);
|
||||
await context.addCookies(cookies);
|
||||
expect(await context.cookies()).toEqual(cookies);
|
||||
});
|
||||
it('should send cookie header', async({server, context}) => {
|
||||
let cookie = '';
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
cookie = req.headers.cookie;
|
||||
res.end();
|
||||
});
|
||||
it('should isolate session cookies', async({context, server, browser}) => {
|
||||
server.setRoute('/setcookie.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'session=value');
|
||||
res.end();
|
||||
});
|
||||
{
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/setcookie.html');
|
||||
}
|
||||
{
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies.map(c => c.value).join(',')).toBe('value');
|
||||
}
|
||||
{
|
||||
const context2 = await browser.newContext();
|
||||
const page = await context2.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context2.cookies();
|
||||
expect(cookies[0] && cookies[0].name).toBe(undefined);
|
||||
await context2.close();
|
||||
}
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'cookie', value: 'value'}]);
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(cookie).toBe('cookie=value');
|
||||
});
|
||||
it('should isolate cookies in browser contexts', async({context, server, browser}) => {
|
||||
const anotherContext = await browser.newContext();
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'isolatecookie', value: 'page1value'}]);
|
||||
await anotherContext.addCookies([{url: server.EMPTY_PAGE, name: 'isolatecookie', value: 'page2value'}]);
|
||||
|
||||
const cookies1 = await context.cookies();
|
||||
const cookies2 = await anotherContext.cookies();
|
||||
expect(cookies1.length).toBe(1);
|
||||
expect(cookies2.length).toBe(1);
|
||||
expect(cookies1[0].name).toBe('isolatecookie');
|
||||
expect(cookies1[0].value).toBe('page1value');
|
||||
expect(cookies2[0].name).toBe('isolatecookie');
|
||||
expect(cookies2[0].value).toBe('page2value');
|
||||
await anotherContext.close();
|
||||
});
|
||||
it('should isolate session cookies', async({context, server, browser}) => {
|
||||
server.setRoute('/setcookie.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'session=value');
|
||||
res.end();
|
||||
});
|
||||
it('should isolate persistent cookies', async({context, server, browser}) => {
|
||||
server.setRoute('/setcookie.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'persistent=persistent-value; max-age=3600');
|
||||
res.end();
|
||||
});
|
||||
{
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/setcookie.html');
|
||||
|
||||
const context1 = context;
|
||||
}
|
||||
{
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies.map(c => c.value).join(',')).toBe('value');
|
||||
}
|
||||
{
|
||||
const context2 = await browser.newContext();
|
||||
const [page1, page2] = await Promise.all([context1.newPage(), context2.newPage()]);
|
||||
await Promise.all([page1.goto(server.EMPTY_PAGE), page2.goto(server.EMPTY_PAGE)]);
|
||||
const [cookies1, cookies2] = await Promise.all([context1.cookies(), context2.cookies()]);
|
||||
expect(cookies1.length).toBe(1);
|
||||
expect(cookies1[0].name).toBe('persistent');
|
||||
expect(cookies1[0].value).toBe('persistent-value');
|
||||
expect(cookies2.length).toBe(0);
|
||||
await context2.close();
|
||||
});
|
||||
it('should isolate send cookie header', async({server, context, browser}) => {
|
||||
let cookie = [];
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
cookie = req.headers.cookie || '';
|
||||
res.end();
|
||||
});
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'sendcookie', value: 'value'}]);
|
||||
{
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(cookie).toBe('sendcookie=value');
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(cookie).toBe('');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it.slow()('should isolate cookies between launches', async({server}) => {
|
||||
const browser1 = await browserType.launch(defaultBrowserOptions);
|
||||
const context1 = await browser1.newContext();
|
||||
await context1.addCookies([{url: server.EMPTY_PAGE, name: 'cookie-in-context-1', value: 'value', expires: Date.now() / 1000 + 10000}]);
|
||||
await browser1.close();
|
||||
|
||||
const browser2 = await browserType.launch(defaultBrowserOptions);
|
||||
const context2 = await browser2.newContext();
|
||||
const page = await context2.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context2.cookies();
|
||||
expect(cookies.length).toBe(0);
|
||||
await browser2.close();
|
||||
expect(cookies[0] && cookies[0].name).toBe(undefined);
|
||||
await context2.close();
|
||||
}
|
||||
});
|
||||
it('should isolate persistent cookies', async({context, server, browser}) => {
|
||||
server.setRoute('/setcookie.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'persistent=persistent-value; max-age=3600');
|
||||
res.end();
|
||||
});
|
||||
it('should set multiple cookies', async({context, page, server}) => {
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/setcookie.html');
|
||||
|
||||
const context1 = context;
|
||||
const context2 = await browser.newContext();
|
||||
const [page1, page2] = await Promise.all([context1.newPage(), context2.newPage()]);
|
||||
await Promise.all([page1.goto(server.EMPTY_PAGE), page2.goto(server.EMPTY_PAGE)]);
|
||||
const [cookies1, cookies2] = await Promise.all([context1.cookies(), context2.cookies()]);
|
||||
expect(cookies1.length).toBe(1);
|
||||
expect(cookies1[0].name).toBe('persistent');
|
||||
expect(cookies1[0].value).toBe('persistent-value');
|
||||
expect(cookies2.length).toBe(0);
|
||||
await context2.close();
|
||||
});
|
||||
it('should isolate send cookie header', async({server, context, browser}) => {
|
||||
let cookie = [];
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
cookie = req.headers.cookie || '';
|
||||
res.end();
|
||||
});
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'sendcookie', value: 'value'}]);
|
||||
{
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'multiple-1',
|
||||
value: '123456'
|
||||
}, {
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'multiple-2',
|
||||
value: 'bar'
|
||||
}]);
|
||||
expect(await page.evaluate(() => {
|
||||
const cookies = document.cookie.split(';');
|
||||
return cookies.map(cookie => cookie.trim()).sort();
|
||||
})).toEqual([
|
||||
'multiple-1=123456',
|
||||
'multiple-2=bar',
|
||||
]);
|
||||
});
|
||||
it('should have |expires| set to |-1| for session cookies', async({context, server}) => {
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'expires',
|
||||
value: '123456'
|
||||
}]);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies[0].expires).toBe(-1);
|
||||
});
|
||||
it('should set cookie with reasonable defaults', async({context, server}) => {
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'defaults',
|
||||
value: '123456'
|
||||
}]);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.sort((a, b) => a.name.localeCompare(b.name))).toEqual([{
|
||||
name: 'defaults',
|
||||
value: '123456',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('should set a cookie with a path', async({context, page, server}) => {
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await context.addCookies([{
|
||||
domain: 'localhost',
|
||||
path: '/grid.html',
|
||||
name: 'gridcookie',
|
||||
value: 'GRID',
|
||||
}]);
|
||||
expect(await context.cookies()).toEqual([{
|
||||
name: 'gridcookie',
|
||||
value: 'GRID',
|
||||
domain: 'localhost',
|
||||
path: '/grid.html',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID');
|
||||
});
|
||||
it('should not set a cookie with blank page URL', async function({context, server}) {
|
||||
let error = null;
|
||||
try {
|
||||
await context.addCookies([
|
||||
{url: server.EMPTY_PAGE, name: 'example-cookie', value: 'best'},
|
||||
{url: 'about:blank', name: 'example-cookie-blank', value: 'best'}
|
||||
]);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toEqual(
|
||||
`Blank page can not have cookie "example-cookie-blank"`
|
||||
);
|
||||
});
|
||||
it('should not set a cookie on a data URL page', async function({context}) {
|
||||
let error = null;
|
||||
try {
|
||||
await context.addCookies([{url: 'data:,Hello%2C%20World!', name: 'example-cookie', value: 'best'}]);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toContain('Data URL page can not have cookie "example-cookie"');
|
||||
});
|
||||
it('should default to setting secure cookie for HTTPS websites', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const SECURE_URL = 'https://example.com';
|
||||
await context.addCookies([{
|
||||
url: SECURE_URL,
|
||||
name: 'foo',
|
||||
value: 'bar',
|
||||
}]);
|
||||
const [cookie] = await context.cookies(SECURE_URL);
|
||||
expect(cookie.secure).toBe(true);
|
||||
});
|
||||
it('should be able to set unsecure cookie for HTTP website', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const HTTP_URL = 'http://example.com';
|
||||
await context.addCookies([{
|
||||
url: HTTP_URL,
|
||||
name: 'foo',
|
||||
value: 'bar',
|
||||
}]);
|
||||
const [cookie] = await context.cookies(HTTP_URL);
|
||||
expect(cookie.secure).toBe(false);
|
||||
});
|
||||
it('should set a cookie on a different domain', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: 'https://www.example.com',
|
||||
name: 'example-cookie',
|
||||
value: 'best',
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
expect(await context.cookies('https://www.example.com')).toEqual([{
|
||||
name: 'example-cookie',
|
||||
value: 'best',
|
||||
domain: 'www.example.com',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: true,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('should set cookies for a frame', async({context, page, server}) => {
|
||||
expect(cookie).toBe('sendcookie=value');
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(cookie).toBe('');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it.slow()('should isolate cookies between launches', async({browserType, server}) => {
|
||||
const browser1 = await browserType.launch(defaultBrowserOptions);
|
||||
const context1 = await browser1.newContext();
|
||||
await context1.addCookies([{url: server.EMPTY_PAGE, name: 'cookie-in-context-1', value: 'value', expires: Date.now() / 1000 + 10000}]);
|
||||
await browser1.close();
|
||||
|
||||
const browser2 = await browserType.launch(defaultBrowserOptions);
|
||||
const context2 = await browser2.newContext();
|
||||
const cookies = await context2.cookies();
|
||||
expect(cookies.length).toBe(0);
|
||||
await browser2.close();
|
||||
});
|
||||
it('should set multiple cookies', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'multiple-1',
|
||||
value: '123456'
|
||||
}, {
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'multiple-2',
|
||||
value: 'bar'
|
||||
}]);
|
||||
expect(await page.evaluate(() => {
|
||||
const cookies = document.cookie.split(';');
|
||||
return cookies.map(cookie => cookie.trim()).sort();
|
||||
})).toEqual([
|
||||
'multiple-1=123456',
|
||||
'multiple-2=bar',
|
||||
]);
|
||||
});
|
||||
it('should have |expires| set to |-1| for session cookies', async({context, server}) => {
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'expires',
|
||||
value: '123456'
|
||||
}]);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies[0].expires).toBe(-1);
|
||||
});
|
||||
it('should set cookie with reasonable defaults', async({context, server}) => {
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'defaults',
|
||||
value: '123456'
|
||||
}]);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.sort((a, b) => a.name.localeCompare(b.name))).toEqual([{
|
||||
name: 'defaults',
|
||||
value: '123456',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('should set a cookie with a path', async({context, page, server}) => {
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await context.addCookies([{
|
||||
domain: 'localhost',
|
||||
path: '/grid.html',
|
||||
name: 'gridcookie',
|
||||
value: 'GRID',
|
||||
}]);
|
||||
expect(await context.cookies()).toEqual([{
|
||||
name: 'gridcookie',
|
||||
value: 'GRID',
|
||||
domain: 'localhost',
|
||||
path: '/grid.html',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID');
|
||||
});
|
||||
it('should not set a cookie with blank page URL', async function({context, server}) {
|
||||
let error = null;
|
||||
try {
|
||||
await context.addCookies([
|
||||
{url: server.PREFIX, name: 'frame-cookie', value: 'value'}
|
||||
{url: server.EMPTY_PAGE, name: 'example-cookie', value: 'best'},
|
||||
{url: 'about:blank', name: 'example-cookie-blank', value: 'best'}
|
||||
]);
|
||||
await page.evaluate(src => {
|
||||
let fulfill;
|
||||
const promise = new Promise(x => fulfill = x);
|
||||
const iframe = document.createElement('iframe');
|
||||
document.body.appendChild(iframe);
|
||||
iframe.onload = fulfill;
|
||||
iframe.src = src;
|
||||
return promise;
|
||||
}, server.PREFIX + '/grid.html');
|
||||
|
||||
expect(await page.frames()[1].evaluate('document.cookie')).toBe('frame-cookie=value');
|
||||
});
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toEqual(
|
||||
`Blank page can not have cookie "example-cookie-blank"`
|
||||
);
|
||||
});
|
||||
|
||||
describe('BrowserContext.clearCookies', function() {
|
||||
it('should clear cookies', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'cookie1',
|
||||
value: '1'
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('cookie1=1');
|
||||
await context.clearCookies();
|
||||
expect(await context.cookies()).toEqual([]);
|
||||
await page.reload();
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
});
|
||||
it('should isolate cookies when clearing', async({context, server, browser}) => {
|
||||
const anotherContext = await browser.newContext();
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'page1cookie', value: 'page1value'}]);
|
||||
await anotherContext.addCookies([{url: server.EMPTY_PAGE, name: 'page2cookie', value: 'page2value'}]);
|
||||
|
||||
expect((await context.cookies()).length).toBe(1);
|
||||
expect((await anotherContext.cookies()).length).toBe(1);
|
||||
|
||||
await context.clearCookies();
|
||||
expect((await context.cookies()).length).toBe(0);
|
||||
expect((await anotherContext.cookies()).length).toBe(1);
|
||||
|
||||
await anotherContext.clearCookies();
|
||||
expect((await context.cookies()).length).toBe(0);
|
||||
expect((await anotherContext.cookies()).length).toBe(0);
|
||||
await anotherContext.close();
|
||||
});
|
||||
it('should not set a cookie on a data URL page', async function({context}) {
|
||||
let error = null;
|
||||
try {
|
||||
await context.addCookies([{url: 'data:,Hello%2C%20World!', name: 'example-cookie', value: 'best'}]);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toContain('Data URL page can not have cookie "example-cookie"');
|
||||
});
|
||||
};
|
||||
it('should default to setting secure cookie for HTTPS websites', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const SECURE_URL = 'https://example.com';
|
||||
await context.addCookies([{
|
||||
url: SECURE_URL,
|
||||
name: 'foo',
|
||||
value: 'bar',
|
||||
}]);
|
||||
const [cookie] = await context.cookies(SECURE_URL);
|
||||
expect(cookie.secure).toBe(true);
|
||||
});
|
||||
it('should be able to set unsecure cookie for HTTP website', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const HTTP_URL = 'http://example.com';
|
||||
await context.addCookies([{
|
||||
url: HTTP_URL,
|
||||
name: 'foo',
|
||||
value: 'bar',
|
||||
}]);
|
||||
const [cookie] = await context.cookies(HTTP_URL);
|
||||
expect(cookie.secure).toBe(false);
|
||||
});
|
||||
it('should set a cookie on a different domain', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: 'https://www.example.com',
|
||||
name: 'example-cookie',
|
||||
value: 'best',
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
expect(await context.cookies('https://www.example.com')).toEqual([{
|
||||
name: 'example-cookie',
|
||||
value: 'best',
|
||||
domain: 'www.example.com',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: true,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('should set cookies for a frame', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([
|
||||
{url: server.PREFIX, name: 'frame-cookie', value: 'value'}
|
||||
]);
|
||||
await page.evaluate(src => {
|
||||
let fulfill;
|
||||
const promise = new Promise(x => fulfill = x);
|
||||
const iframe = document.createElement('iframe');
|
||||
document.body.appendChild(iframe);
|
||||
iframe.onload = fulfill;
|
||||
iframe.src = src;
|
||||
return promise;
|
||||
}, server.PREFIX + '/grid.html');
|
||||
|
||||
expect(await page.frames()[1].evaluate('document.cookie')).toBe('frame-cookie=value');
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext.clearCookies', function() {
|
||||
it('should clear cookies', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'cookie1',
|
||||
value: '1'
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('cookie1=1');
|
||||
await context.clearCookies();
|
||||
expect(await context.cookies()).toEqual([]);
|
||||
await page.reload();
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
});
|
||||
it('should isolate cookies when clearing', async({context, server, browser}) => {
|
||||
const anotherContext = await browser.newContext();
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'page1cookie', value: 'page1value'}]);
|
||||
await anotherContext.addCookies([{url: server.EMPTY_PAGE, name: 'page2cookie', value: 'page2value'}]);
|
||||
|
||||
expect((await context.cookies()).length).toBe(1);
|
||||
expect((await anotherContext.cookies()).length).toBe(1);
|
||||
|
||||
await context.clearCookies();
|
||||
expect((await context.cookies()).length).toBe(0);
|
||||
expect((await anotherContext.cookies()).length).toBe(1);
|
||||
|
||||
await anotherContext.clearCookies();
|
||||
expect((await context.cookies()).length).toBe(0);
|
||||
expect((await anotherContext.cookies()).length).toBe(0);
|
||||
await anotherContext.close();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,76 +15,72 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const { makeUserDataDir, removeUserDataDir } = require('./utils');
|
||||
const utils = require('./utils');
|
||||
const {makeUserDataDir, removeUserDataDir} = utils;
|
||||
const {FFOX, CHROMIUM, WEBKIT, defaultBrowserOptions} = utils.testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function ({ defaultBrowserOptions, browserType, WEBKIT }) {
|
||||
|
||||
describe('launchPersistentContext()', function() {
|
||||
beforeEach(async state => {
|
||||
state.userDataDir = await makeUserDataDir();
|
||||
state.browserContext = await browserType.launchPersistentContext(state.userDataDir, defaultBrowserOptions);
|
||||
state.page = await state.browserContext.newPage();
|
||||
});
|
||||
afterEach(async state => {
|
||||
await state.browserContext.close();
|
||||
delete state.browserContext;
|
||||
delete state.page;
|
||||
await removeUserDataDir(state.userDataDir);
|
||||
});
|
||||
it('context.cookies() should work', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
});
|
||||
expect(await page.context().cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('context.addCookies() should work', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.context().addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'username',
|
||||
value: 'John Doe'
|
||||
}]);
|
||||
expect(await page.evaluate(() => document.cookie)).toBe('username=John Doe');
|
||||
expect(await page.context().cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('context.clearCookies() should work', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.context().addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'cookie1',
|
||||
value: '1'
|
||||
}, {
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'cookie2',
|
||||
value: '2'
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2');
|
||||
await page.context().clearCookies();
|
||||
await page.reload();
|
||||
expect(await page.context().cookies([])).toEqual([]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
});
|
||||
describe('launchPersistentContext()', function() {
|
||||
beforeEach(async state => {
|
||||
state.userDataDir = await makeUserDataDir();
|
||||
state.browserContext = await state.browserType.launchPersistentContext(state.userDataDir, defaultBrowserOptions);
|
||||
state.page = await state.browserContext.newPage();
|
||||
});
|
||||
};
|
||||
afterEach(async state => {
|
||||
await state.browserContext.close();
|
||||
delete state.browserContext;
|
||||
delete state.page;
|
||||
await removeUserDataDir(state.userDataDir);
|
||||
});
|
||||
it('context.cookies() should work', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
});
|
||||
expect(await page.context().cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('context.addCookies() should work', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.context().addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'username',
|
||||
value: 'John Doe'
|
||||
}]);
|
||||
expect(await page.evaluate(() => document.cookie)).toBe('username=John Doe');
|
||||
expect(await page.context().cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('context.clearCookies() should work', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.context().addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'cookie1',
|
||||
value: '1'
|
||||
}, {
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'cookie2',
|
||||
value: '2'
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2');
|
||||
await page.context().clearCookies();
|
||||
await page.reload();
|
||||
expect(await page.context().cookies([])).toEqual([]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,51 +15,47 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({FFOX, CHROMIUM, WEBKIT}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
|
||||
describe('Page.Events.Dialog', function() {
|
||||
it('should fire', async({page, server}) => {
|
||||
page.on('dialog', dialog => {
|
||||
expect(dialog.type()).toBe('alert');
|
||||
expect(dialog.defaultValue()).toBe('');
|
||||
expect(dialog.message()).toBe('yo');
|
||||
dialog.accept();
|
||||
});
|
||||
await page.evaluate(() => alert('yo'));
|
||||
});
|
||||
it('should allow accepting prompts', async({page, server}) => {
|
||||
page.on('dialog', dialog => {
|
||||
expect(dialog.type()).toBe('prompt');
|
||||
expect(dialog.defaultValue()).toBe('yes.');
|
||||
expect(dialog.message()).toBe('question?');
|
||||
dialog.accept('answer!');
|
||||
});
|
||||
const result = await page.evaluate(() => prompt('question?', 'yes.'));
|
||||
expect(result).toBe('answer!');
|
||||
});
|
||||
it('should dismiss the prompt', async({page, server}) => {
|
||||
page.on('dialog', dialog => {
|
||||
dialog.dismiss();
|
||||
});
|
||||
const result = await page.evaluate(() => prompt('question?'));
|
||||
expect(result).toBe(null);
|
||||
});
|
||||
it('should accept the confirm prompt', async({page, server}) => {
|
||||
page.on('dialog', dialog => {
|
||||
dialog.accept();
|
||||
});
|
||||
const result = await page.evaluate(() => confirm('boolean?'));
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
it('should dismiss the confirm prompt', async({page, server}) => {
|
||||
page.on('dialog', dialog => {
|
||||
dialog.dismiss();
|
||||
});
|
||||
const result = await page.evaluate(() => confirm('boolean?'));
|
||||
expect(result).toBe(false);
|
||||
describe('Page.Events.Dialog', function() {
|
||||
it('should fire', async({page, server}) => {
|
||||
page.on('dialog', dialog => {
|
||||
expect(dialog.type()).toBe('alert');
|
||||
expect(dialog.defaultValue()).toBe('');
|
||||
expect(dialog.message()).toBe('yo');
|
||||
dialog.accept();
|
||||
});
|
||||
await page.evaluate(() => alert('yo'));
|
||||
});
|
||||
};
|
||||
it('should allow accepting prompts', async({page, server}) => {
|
||||
page.on('dialog', dialog => {
|
||||
expect(dialog.type()).toBe('prompt');
|
||||
expect(dialog.defaultValue()).toBe('yes.');
|
||||
expect(dialog.message()).toBe('question?');
|
||||
dialog.accept('answer!');
|
||||
});
|
||||
const result = await page.evaluate(() => prompt('question?', 'yes.'));
|
||||
expect(result).toBe('answer!');
|
||||
});
|
||||
it('should dismiss the prompt', async({page, server}) => {
|
||||
page.on('dialog', dialog => {
|
||||
dialog.dismiss();
|
||||
});
|
||||
const result = await page.evaluate(() => prompt('question?'));
|
||||
expect(result).toBe(null);
|
||||
});
|
||||
it('should accept the confirm prompt', async({page, server}) => {
|
||||
page.on('dialog', dialog => {
|
||||
dialog.accept();
|
||||
});
|
||||
const result = await page.evaluate(() => confirm('boolean?'));
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
it('should dismiss the confirm prompt', async({page, server}) => {
|
||||
page.on('dialog', dialog => {
|
||||
dialog.dismiss();
|
||||
});
|
||||
const result = await page.evaluate(() => confirm('boolean?'));
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,107 +16,105 @@
|
|||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const {FFOX, CHROMIUM, WEBKIT, defaultBrowserOptions} = require('./utils').testOptions(browserType);
|
||||
|
||||
module.exports.describe = function({browserType, defaultBrowserOptions, CHROMIUM, WEBKIT, FFOX, WIN, MAC}) {
|
||||
|
||||
describe('Download', function() {
|
||||
beforeEach(async(state) => {
|
||||
state.server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
});
|
||||
|
||||
it('should report downloads with acceptDownloads: false', async({page, server}) => {
|
||||
await page.setContent(`<a download=true href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
let error;
|
||||
expect(download.url()).toBe(`${server.PREFIX}/download`);
|
||||
await download.path().catch(e => error = e);
|
||||
expect(await download.failure()).toContain('acceptDownloads');
|
||||
expect(error.message).toContain('acceptDownloads: true');
|
||||
});
|
||||
it('should report downloads with acceptDownloads: true', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a download=true href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
it('should delete file', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a download=true href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
await download.delete();
|
||||
expect(fs.existsSync(path)).toBeFalsy();
|
||||
});
|
||||
it('should expose stream', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a download=true href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const stream = await download.createReadStream();
|
||||
let content = '';
|
||||
stream.on('data', data => content += data.toString());
|
||||
await new Promise(f => stream.on('end', f));
|
||||
expect(content).toBe('Hello world');
|
||||
stream.close();
|
||||
});
|
||||
it('should delete downloads on context destruction', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a download=true href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download1 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const [ download2 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path1 = await download1.path();
|
||||
const path2 = await download2.path();
|
||||
expect(fs.existsSync(path1)).toBeTruthy();
|
||||
expect(fs.existsSync(path2)).toBeTruthy();
|
||||
await page.context().close();
|
||||
expect(fs.existsSync(path1)).toBeFalsy();
|
||||
expect(fs.existsSync(path2)).toBeFalsy();
|
||||
});
|
||||
it('should delete downloads on browser gone', async ({ server }) => {
|
||||
const browser = await browserType.launch(defaultBrowserOptions);
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a download=true href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download1 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const [ download2 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path1 = await download1.path();
|
||||
const path2 = await download2.path();
|
||||
expect(fs.existsSync(path1)).toBeTruthy();
|
||||
expect(fs.existsSync(path2)).toBeTruthy();
|
||||
await browser.close();
|
||||
expect(fs.existsSync(path1)).toBeFalsy();
|
||||
expect(fs.existsSync(path2)).toBeFalsy();
|
||||
expect(fs.existsSync(path.join(path1, '..'))).toBeFalsy();
|
||||
describe('Download', function() {
|
||||
beforeEach(async(state) => {
|
||||
state.server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
it('should report downloads with acceptDownloads: false', async({page, server}) => {
|
||||
await page.setContent(`<a download=true href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
let error;
|
||||
expect(download.url()).toBe(`${server.PREFIX}/download`);
|
||||
await download.path().catch(e => error = e);
|
||||
expect(await download.failure()).toContain('acceptDownloads');
|
||||
expect(error.message).toContain('acceptDownloads: true');
|
||||
});
|
||||
it('should report downloads with acceptDownloads: true', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a download=true href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
it('should delete file', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a download=true href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
await download.delete();
|
||||
expect(fs.existsSync(path)).toBeFalsy();
|
||||
});
|
||||
it('should expose stream', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a download=true href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const stream = await download.createReadStream();
|
||||
let content = '';
|
||||
stream.on('data', data => content += data.toString());
|
||||
await new Promise(f => stream.on('end', f));
|
||||
expect(content).toBe('Hello world');
|
||||
stream.close();
|
||||
});
|
||||
it('should delete downloads on context destruction', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a download=true href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download1 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const [ download2 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path1 = await download1.path();
|
||||
const path2 = await download2.path();
|
||||
expect(fs.existsSync(path1)).toBeTruthy();
|
||||
expect(fs.existsSync(path2)).toBeTruthy();
|
||||
await page.context().close();
|
||||
expect(fs.existsSync(path1)).toBeFalsy();
|
||||
expect(fs.existsSync(path2)).toBeFalsy();
|
||||
});
|
||||
it('should delete downloads on browser gone', async ({ server, browserType }) => {
|
||||
const browser = await browserType.launch(defaultBrowserOptions);
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a download=true href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download1 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const [ download2 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path1 = await download1.path();
|
||||
const path2 = await download2.path();
|
||||
expect(fs.existsSync(path1)).toBeTruthy();
|
||||
expect(fs.existsSync(path2)).toBeTruthy();
|
||||
await browser.close();
|
||||
expect(fs.existsSync(path1)).toBeFalsy();
|
||||
expect(fs.existsSync(path2)).toBeFalsy();
|
||||
expect(fs.existsSync(path.join(path1, '..'))).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,318 +16,313 @@
|
|||
*/
|
||||
|
||||
const utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({FFOX, CHROMIUM, WEBKIT}) {
|
||||
|
||||
describe('ElementHandle.boundingBox', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const elementHandle = await page.$('.box:nth-of-type(13)');
|
||||
const box = await elementHandle.boundingBox();
|
||||
expect(box).toEqual({ x: 100, y: 50, width: 50, height: 50 });
|
||||
});
|
||||
it('should handle nested frames', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/frames/nested-frames.html');
|
||||
const nestedFrame = page.frames().find(frame => frame.name() === 'dos');
|
||||
const elementHandle = await nestedFrame.$('div');
|
||||
const box = await elementHandle.boundingBox();
|
||||
expect(box).toEqual({ x: 24, y: 224, width: 268, height: 18 });
|
||||
});
|
||||
it('should return null for invisible elements', async({page, server}) => {
|
||||
await page.setContent('<div style="display:none">hi</div>');
|
||||
const element = await page.$('div');
|
||||
expect(await element.boundingBox()).toBe(null);
|
||||
});
|
||||
it('should force a layout', async({page, server}) => {
|
||||
await page.setViewportSize({ width: 500, height: 500 });
|
||||
await page.setContent('<div style="width: 100px; height: 100px">hello</div>');
|
||||
const elementHandle = await page.$('div');
|
||||
await page.evaluate(element => element.style.height = '200px', elementHandle);
|
||||
const box = await elementHandle.boundingBox();
|
||||
expect(box).toEqual({ x: 8, y: 8, width: 100, height: 200 });
|
||||
});
|
||||
it('should work with SVG nodes', async({page, server}) => {
|
||||
await page.setContent(`
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
|
||||
<rect id="theRect" x="30" y="50" width="200" height="300"></rect>
|
||||
</svg>
|
||||
`);
|
||||
const element = await page.$('#therect');
|
||||
const pwBoundingBox = await element.boundingBox();
|
||||
const webBoundingBox = await page.evaluate(e => {
|
||||
const rect = e.getBoundingClientRect();
|
||||
return {x: rect.x, y: rect.y, width: rect.width, height: rect.height};
|
||||
}, element);
|
||||
expect(pwBoundingBox).toEqual(webBoundingBox);
|
||||
});
|
||||
it.skip(FFOX)('should work with page scale', async({browser, server}) => {
|
||||
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');
|
||||
const button = await page.$('button');
|
||||
await button.evaluate(button => {
|
||||
document.body.style.margin = '0';
|
||||
button.style.borderWidth = '0';
|
||||
button.style.width = '200px';
|
||||
button.style.height = '20px';
|
||||
button.style.marginLeft = '17px';
|
||||
button.style.marginTop = '23px';
|
||||
});
|
||||
const box = await button.boundingBox();
|
||||
expect(Math.round(box.x * 100)).toBe(17 * 100);
|
||||
expect(Math.round(box.y * 100)).toBe(23 * 100);
|
||||
expect(Math.round(box.width * 100)).toBe(200 * 100);
|
||||
expect(Math.round(box.height * 100)).toBe(20 * 100);
|
||||
await context.close();
|
||||
});
|
||||
it('should work when inline box child is outside of viewport', async({page, server}) => {
|
||||
await page.setContent(`
|
||||
<style>
|
||||
i {
|
||||
position: absolute;
|
||||
top: -1000px;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
<span><i>woof</i><b>doggo</b></span>
|
||||
`);
|
||||
const handle = await page.$('span');
|
||||
const box = await handle.boundingBox();
|
||||
const webBoundingBox = await handle.evaluate(e => {
|
||||
const rect = e.getBoundingClientRect();
|
||||
return {x: rect.x, y: rect.y, width: rect.width, height: rect.height};
|
||||
});
|
||||
const round = box => ({
|
||||
x: Math.round(box.x * 100),
|
||||
y: Math.round(box.y * 100),
|
||||
width: Math.round(box.width * 100),
|
||||
height: Math.round(box.height * 100),
|
||||
});
|
||||
expect(round(box)).toEqual(round(webBoundingBox));
|
||||
});
|
||||
describe('ElementHandle.boundingBox', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const elementHandle = await page.$('.box:nth-of-type(13)');
|
||||
const box = await elementHandle.boundingBox();
|
||||
expect(box).toEqual({ x: 100, y: 50, width: 50, height: 50 });
|
||||
});
|
||||
|
||||
describe('ElementHandle.contentFrame', function() {
|
||||
it('should work', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const elementHandle = await page.$('#frame1');
|
||||
const frame = await elementHandle.contentFrame();
|
||||
expect(frame).toBe(page.frames()[1]);
|
||||
});
|
||||
it('should work for cross-process iframes', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const elementHandle = await page.$('#frame1');
|
||||
const frame = await elementHandle.contentFrame();
|
||||
expect(frame).toBe(page.frames()[1]);
|
||||
});
|
||||
it('should work for cross-frame evaluations', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => window.top.document.querySelector('#frame1'));
|
||||
expect(await elementHandle.contentFrame()).toBe(frame);
|
||||
});
|
||||
it('should return null for non-iframes', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.body);
|
||||
expect(await elementHandle.contentFrame()).toBe(null);
|
||||
});
|
||||
it('should return null for document.documentElement', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.documentElement);
|
||||
expect(await elementHandle.contentFrame()).toBe(null);
|
||||
});
|
||||
it('should handle nested frames', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/frames/nested-frames.html');
|
||||
const nestedFrame = page.frames().find(frame => frame.name() === 'dos');
|
||||
const elementHandle = await nestedFrame.$('div');
|
||||
const box = await elementHandle.boundingBox();
|
||||
expect(box).toEqual({ x: 24, y: 224, width: 268, height: 18 });
|
||||
});
|
||||
|
||||
describe('ElementHandle.ownerFrame', function() {
|
||||
it('should work', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.body);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
});
|
||||
it('should work for cross-process iframes', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.body);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
});
|
||||
it('should work for document', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
});
|
||||
it('should work for iframe elements', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
const elementHandle = await frame.evaluateHandle(() => document.querySelector('#frame1'));
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
});
|
||||
it('should work for cross-frame evaluations', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
const elementHandle = await frame.evaluateHandle(() => document.querySelector('#frame1').contentWindow.document.body);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame.childFrames()[0]);
|
||||
});
|
||||
it('should work for detached elements', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const divHandle = await page.evaluateHandle(() => {
|
||||
const div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
});
|
||||
expect(await divHandle.ownerFrame()).toBe(page.mainFrame());
|
||||
await page.evaluate(() => {
|
||||
const div = document.querySelector('div');
|
||||
document.body.removeChild(div);
|
||||
});
|
||||
expect(await divHandle.ownerFrame()).toBe(page.mainFrame());
|
||||
});
|
||||
it('should work for adopted elements', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window.__popup = window.open(url), server.EMPTY_PAGE),
|
||||
]);
|
||||
const divHandle = await page.evaluateHandle(() => {
|
||||
const div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
});
|
||||
expect(await divHandle.ownerFrame()).toBe(page.mainFrame());
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
await page.evaluate(() => {
|
||||
const div = document.querySelector('div');
|
||||
window.__popup.document.body.appendChild(div);
|
||||
});
|
||||
expect(await divHandle.ownerFrame()).toBe(popup.mainFrame());
|
||||
});
|
||||
it('should return null for invisible elements', async({page, server}) => {
|
||||
await page.setContent('<div style="display:none">hi</div>');
|
||||
const element = await page.$('div');
|
||||
expect(await element.boundingBox()).toBe(null);
|
||||
});
|
||||
|
||||
describe('ElementHandle.click', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await button.click();
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should work with Node removed', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.evaluate(() => delete window['Node']);
|
||||
const button = await page.$('button');
|
||||
await button.click();
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should work for Shadow DOM v1', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/shadow.html');
|
||||
const buttonHandle = await page.evaluateHandle(() => button);
|
||||
await buttonHandle.click();
|
||||
expect(await page.evaluate(() => clicked)).toBe(true);
|
||||
});
|
||||
it('should work for TextNodes', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const buttonTextNode = await page.evaluateHandle(() => document.querySelector('button').firstChild);
|
||||
await buttonTextNode.click();
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should throw for detached nodes', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(button => button.remove(), button);
|
||||
let error = null;
|
||||
await button.click().catch(err => error = err);
|
||||
expect(error.message).toContain('Element is not attached to the DOM');
|
||||
});
|
||||
it('should throw for hidden nodes', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(button => button.style.display = 'none', button);
|
||||
const error = await button.click({ force: true }).catch(err => err);
|
||||
expect(error.message).toBe('Node is either not visible or not an HTMLElement');
|
||||
});
|
||||
it('should throw for recursively hidden nodes', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(button => button.parentElement.style.display = 'none', button);
|
||||
const error = await button.click({ force: true }).catch(err => err);
|
||||
expect(error.message).toBe('Node is either not visible or not an HTMLElement');
|
||||
});
|
||||
it('should throw for <br> elements', async({page, server}) => {
|
||||
await page.setContent('hello<br>goodbye');
|
||||
const br = await page.$('br');
|
||||
const error = await br.click({ force: true }).catch(err => err);
|
||||
expect(error.message).toBe('Node is either not visible or not an HTMLElement');
|
||||
});
|
||||
it('should force a layout', async({page, server}) => {
|
||||
await page.setViewportSize({ width: 500, height: 500 });
|
||||
await page.setContent('<div style="width: 100px; height: 100px">hello</div>');
|
||||
const elementHandle = await page.$('div');
|
||||
await page.evaluate(element => element.style.height = '200px', elementHandle);
|
||||
const box = await elementHandle.boundingBox();
|
||||
expect(box).toEqual({ x: 8, y: 8, width: 100, height: 200 });
|
||||
});
|
||||
|
||||
describe('ElementHandle.hover', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||
const button = await page.$('#button-6');
|
||||
await button.hover();
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
||||
});
|
||||
it('should work when Node is removed', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||
await page.evaluate(() => delete window['Node']);
|
||||
const button = await page.$('#button-6');
|
||||
await button.hover();
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
||||
});
|
||||
it('should work with SVG nodes', async({page, server}) => {
|
||||
await page.setContent(`
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
|
||||
<rect id="theRect" x="30" y="50" width="200" height="300"></rect>
|
||||
</svg>
|
||||
`);
|
||||
const element = await page.$('#therect');
|
||||
const pwBoundingBox = await element.boundingBox();
|
||||
const webBoundingBox = await page.evaluate(e => {
|
||||
const rect = e.getBoundingClientRect();
|
||||
return {x: rect.x, y: rect.y, width: rect.width, height: rect.height};
|
||||
}, element);
|
||||
expect(pwBoundingBox).toEqual(webBoundingBox);
|
||||
});
|
||||
|
||||
describe('ElementHandle.scrollIntoViewIfNeeded', function() {
|
||||
it.fail(FFOX)('should work', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/offscreenbuttons.html');
|
||||
for (let i = 0; i < 11; ++i) {
|
||||
const button = await page.$('#btn' + i);
|
||||
const before = await button.evaluate(button => {
|
||||
return button.getBoundingClientRect().right - window.innerWidth;
|
||||
});
|
||||
expect(before).toBe(10 * i);
|
||||
await button.scrollIntoViewIfNeeded();
|
||||
const after = await button.evaluate(button => {
|
||||
return button.getBoundingClientRect().right - window.innerWidth;
|
||||
});
|
||||
expect(after <= 0).toBe(true);
|
||||
await page.evaluate(() => window.scrollTo(0, 0));
|
||||
it.skip(FFOX)('should work with page scale', async({browser, server}) => {
|
||||
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');
|
||||
const button = await page.$('button');
|
||||
await button.evaluate(button => {
|
||||
document.body.style.margin = '0';
|
||||
button.style.borderWidth = '0';
|
||||
button.style.width = '200px';
|
||||
button.style.height = '20px';
|
||||
button.style.marginLeft = '17px';
|
||||
button.style.marginTop = '23px';
|
||||
});
|
||||
const box = await button.boundingBox();
|
||||
expect(Math.round(box.x * 100)).toBe(17 * 100);
|
||||
expect(Math.round(box.y * 100)).toBe(23 * 100);
|
||||
expect(Math.round(box.width * 100)).toBe(200 * 100);
|
||||
expect(Math.round(box.height * 100)).toBe(20 * 100);
|
||||
await context.close();
|
||||
});
|
||||
it('should work when inline box child is outside of viewport', async({page, server}) => {
|
||||
await page.setContent(`
|
||||
<style>
|
||||
i {
|
||||
position: absolute;
|
||||
top: -1000px;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
<span><i>woof</i><b>doggo</b></span>
|
||||
`);
|
||||
const handle = await page.$('span');
|
||||
const box = await handle.boundingBox();
|
||||
const webBoundingBox = await handle.evaluate(e => {
|
||||
const rect = e.getBoundingClientRect();
|
||||
return {x: rect.x, y: rect.y, width: rect.width, height: rect.height};
|
||||
});
|
||||
const round = box => ({
|
||||
x: Math.round(box.x * 100),
|
||||
y: Math.round(box.y * 100),
|
||||
width: Math.round(box.width * 100),
|
||||
height: Math.round(box.height * 100),
|
||||
});
|
||||
expect(round(box)).toEqual(round(webBoundingBox));
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.fill', function() {
|
||||
it('should fill input', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
const handle = await page.$('input');
|
||||
await handle.fill('some value');
|
||||
expect(await page.evaluate(() => result)).toBe('some value');
|
||||
});
|
||||
it('should fill input when Node is removed', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.evaluate(() => delete window['Node']);
|
||||
const handle = await page.$('input');
|
||||
await handle.fill('some value');
|
||||
expect(await page.evaluate(() => result)).toBe('some value');
|
||||
});
|
||||
describe('ElementHandle.contentFrame', function() {
|
||||
it('should work', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const elementHandle = await page.$('#frame1');
|
||||
const frame = await elementHandle.contentFrame();
|
||||
expect(frame).toBe(page.frames()[1]);
|
||||
});
|
||||
};
|
||||
it('should work for cross-process iframes', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const elementHandle = await page.$('#frame1');
|
||||
const frame = await elementHandle.contentFrame();
|
||||
expect(frame).toBe(page.frames()[1]);
|
||||
});
|
||||
it('should work for cross-frame evaluations', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => window.top.document.querySelector('#frame1'));
|
||||
expect(await elementHandle.contentFrame()).toBe(frame);
|
||||
});
|
||||
it('should return null for non-iframes', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.body);
|
||||
expect(await elementHandle.contentFrame()).toBe(null);
|
||||
});
|
||||
it('should return null for document.documentElement', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.documentElement);
|
||||
expect(await elementHandle.contentFrame()).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.ownerFrame', function() {
|
||||
it('should work', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.body);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
});
|
||||
it('should work for cross-process iframes', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document.body);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
});
|
||||
it('should work for document', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const elementHandle = await frame.evaluateHandle(() => document);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
});
|
||||
it('should work for iframe elements', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
const elementHandle = await frame.evaluateHandle(() => document.querySelector('#frame1'));
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame);
|
||||
});
|
||||
it('should work for cross-frame evaluations', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
const elementHandle = await frame.evaluateHandle(() => document.querySelector('#frame1').contentWindow.document.body);
|
||||
expect(await elementHandle.ownerFrame()).toBe(frame.childFrames()[0]);
|
||||
});
|
||||
it('should work for detached elements', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const divHandle = await page.evaluateHandle(() => {
|
||||
const div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
});
|
||||
expect(await divHandle.ownerFrame()).toBe(page.mainFrame());
|
||||
await page.evaluate(() => {
|
||||
const div = document.querySelector('div');
|
||||
document.body.removeChild(div);
|
||||
});
|
||||
expect(await divHandle.ownerFrame()).toBe(page.mainFrame());
|
||||
});
|
||||
it('should work for adopted elements', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window.__popup = window.open(url), server.EMPTY_PAGE),
|
||||
]);
|
||||
const divHandle = await page.evaluateHandle(() => {
|
||||
const div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
});
|
||||
expect(await divHandle.ownerFrame()).toBe(page.mainFrame());
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
await page.evaluate(() => {
|
||||
const div = document.querySelector('div');
|
||||
window.__popup.document.body.appendChild(div);
|
||||
});
|
||||
expect(await divHandle.ownerFrame()).toBe(popup.mainFrame());
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.click', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await button.click();
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should work with Node removed', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.evaluate(() => delete window['Node']);
|
||||
const button = await page.$('button');
|
||||
await button.click();
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should work for Shadow DOM v1', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/shadow.html');
|
||||
const buttonHandle = await page.evaluateHandle(() => button);
|
||||
await buttonHandle.click();
|
||||
expect(await page.evaluate(() => clicked)).toBe(true);
|
||||
});
|
||||
it('should work for TextNodes', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const buttonTextNode = await page.evaluateHandle(() => document.querySelector('button').firstChild);
|
||||
await buttonTextNode.click();
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should throw for detached nodes', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(button => button.remove(), button);
|
||||
let error = null;
|
||||
await button.click().catch(err => error = err);
|
||||
expect(error.message).toContain('Element is not attached to the DOM');
|
||||
});
|
||||
it('should throw for hidden nodes', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(button => button.style.display = 'none', button);
|
||||
const error = await button.click({ force: true }).catch(err => err);
|
||||
expect(error.message).toBe('Node is either not visible or not an HTMLElement');
|
||||
});
|
||||
it('should throw for recursively hidden nodes', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(button => button.parentElement.style.display = 'none', button);
|
||||
const error = await button.click({ force: true }).catch(err => err);
|
||||
expect(error.message).toBe('Node is either not visible or not an HTMLElement');
|
||||
});
|
||||
it('should throw for <br> elements', async({page, server}) => {
|
||||
await page.setContent('hello<br>goodbye');
|
||||
const br = await page.$('br');
|
||||
const error = await br.click({ force: true }).catch(err => err);
|
||||
expect(error.message).toBe('Node is either not visible or not an HTMLElement');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.hover', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||
const button = await page.$('#button-6');
|
||||
await button.hover();
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
||||
});
|
||||
it('should work when Node is removed', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||
await page.evaluate(() => delete window['Node']);
|
||||
const button = await page.$('#button-6');
|
||||
await button.hover();
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.scrollIntoViewIfNeeded', function() {
|
||||
it.fail(FFOX)('should work', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/offscreenbuttons.html');
|
||||
for (let i = 0; i < 11; ++i) {
|
||||
const button = await page.$('#btn' + i);
|
||||
const before = await button.evaluate(button => {
|
||||
return button.getBoundingClientRect().right - window.innerWidth;
|
||||
});
|
||||
expect(before).toBe(10 * i);
|
||||
await button.scrollIntoViewIfNeeded();
|
||||
const after = await button.evaluate(button => {
|
||||
return button.getBoundingClientRect().right - window.innerWidth;
|
||||
});
|
||||
expect(after <= 0).toBe(true);
|
||||
await page.evaluate(() => window.scrollTo(0, 0));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.fill', function() {
|
||||
it('should fill input', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
const handle = await page.$('input');
|
||||
await handle.fill('some value');
|
||||
expect(await page.evaluate(() => result)).toBe('some value');
|
||||
});
|
||||
it('should fill input when Node is removed', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.evaluate(() => delete window['Node']);
|
||||
const handle = await page.$('input');
|
||||
await handle.fill('some value');
|
||||
expect(await page.evaluate(() => result)).toBe('some value');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,449 +16,444 @@
|
|||
*/
|
||||
|
||||
const utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, headless} = utils.testOptions(browserType);
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const iPhoneLandscape = playwright.devices['iPhone 6 landscape'];
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({playwright, headless, FFOX, CHROMIUM, WEBKIT, MAC, WIN}) {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const iPhoneLandscape = playwright.devices['iPhone 6 landscape'];
|
||||
|
||||
describe('BrowserContext({viewport})', function() {
|
||||
it('should get the proper viewport size', async({page, server}) => {
|
||||
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(1280);
|
||||
expect(await page.evaluate(() => window.innerHeight)).toBe(720);
|
||||
});
|
||||
it('should set the proper viewport size', async({page, server}) => {
|
||||
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
|
||||
await page.setViewportSize({width: 123, height: 456});
|
||||
expect(page.viewportSize()).toEqual({width: 123, height: 456});
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(123);
|
||||
expect(await page.evaluate(() => window.innerHeight)).toBe(456);
|
||||
});
|
||||
it('should emulate device width', async({page, server}) => {
|
||||
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
|
||||
await page.setViewportSize({width: 200, height: 200});
|
||||
expect(await page.evaluate(() => window.screen.width)).toBe(200);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 100px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 300px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 100px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 300px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 500px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 200px)').matches)).toBe(true);
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
expect(await page.evaluate(() => window.screen.width)).toBe(500);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 400px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 600px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 400px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 600px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 200px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 500px)').matches)).toBe(true);
|
||||
});
|
||||
it('should emulate device height', async({page, server}) => {
|
||||
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
|
||||
await page.setViewportSize({width: 200, height: 200});
|
||||
expect(await page.evaluate(() => window.screen.height)).toBe(200);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 100px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 300px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 100px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 300px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 500px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 200px)').matches)).toBe(true);
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
expect(await page.evaluate(() => window.screen.height)).toBe(500);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 400px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 600px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 400px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 600px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 200px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 500px)').matches)).toBe(true);
|
||||
});
|
||||
it('should not have touch by default', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false);
|
||||
await page.goto(server.PREFIX + '/detect-touch.html');
|
||||
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO');
|
||||
});
|
||||
describe('BrowserContext({viewport})', function() {
|
||||
it('should get the proper viewport size', async({page, server}) => {
|
||||
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(1280);
|
||||
expect(await page.evaluate(() => window.innerHeight)).toBe(720);
|
||||
});
|
||||
it('should set the proper viewport size', async({page, server}) => {
|
||||
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
|
||||
await page.setViewportSize({width: 123, height: 456});
|
||||
expect(page.viewportSize()).toEqual({width: 123, height: 456});
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(123);
|
||||
expect(await page.evaluate(() => window.innerHeight)).toBe(456);
|
||||
});
|
||||
it('should emulate device width', async({page, server}) => {
|
||||
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
|
||||
await page.setViewportSize({width: 200, height: 200});
|
||||
expect(await page.evaluate(() => window.screen.width)).toBe(200);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 100px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 300px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 100px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 300px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 500px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 200px)').matches)).toBe(true);
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
expect(await page.evaluate(() => window.screen.width)).toBe(500);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 400px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 600px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 400px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 600px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 200px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 500px)').matches)).toBe(true);
|
||||
});
|
||||
it('should emulate device height', async({page, server}) => {
|
||||
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
|
||||
await page.setViewportSize({width: 200, height: 200});
|
||||
expect(await page.evaluate(() => window.screen.height)).toBe(200);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 100px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 300px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 100px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 300px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 500px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 200px)').matches)).toBe(true);
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
expect(await page.evaluate(() => window.screen.height)).toBe(500);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 400px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 600px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 400px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 600px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 200px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 500px)').matches)).toBe(true);
|
||||
});
|
||||
it('should not have touch by default', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false);
|
||||
await page.goto(server.PREFIX + '/detect-touch.html');
|
||||
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO');
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip(FFOX)('viewport.isMobile', () => {
|
||||
// Firefox does not support isMobile.
|
||||
it('should support mobile emulation', async({browser, server}) => {
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(375);
|
||||
await page.setViewportSize({width: 400, height: 300});
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(400);
|
||||
await context.close();
|
||||
describe.skip(FFOX)('viewport.isMobile', () => {
|
||||
// Firefox does not support isMobile.
|
||||
it('should support mobile emulation', async({browser, server}) => {
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(375);
|
||||
await page.setViewportSize({width: 400, height: 300});
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(400);
|
||||
await context.close();
|
||||
});
|
||||
it('should support touch emulation', async({browser, server}) => {
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true);
|
||||
expect(await page.evaluate(dispatchTouch)).toBe('Received touch');
|
||||
await context.close();
|
||||
|
||||
function dispatchTouch() {
|
||||
let fulfill;
|
||||
const promise = new Promise(x => fulfill = x);
|
||||
window.ontouchstart = function(e) {
|
||||
fulfill('Received touch');
|
||||
};
|
||||
window.dispatchEvent(new Event('touchstart'));
|
||||
|
||||
fulfill('Did not receive touch');
|
||||
|
||||
return promise;
|
||||
}
|
||||
});
|
||||
it('should be detectable by Modernizr', async({browser, server}) => {
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/detect-touch.html');
|
||||
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES');
|
||||
await context.close();
|
||||
});
|
||||
it('should detect touch when applying viewport with touches', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 800, height: 600 }, hasTouch: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.addScriptTag({url: server.PREFIX + '/modernizr.js'});
|
||||
expect(await page.evaluate(() => Modernizr.touchevents)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should support landscape emulation', async({browser, server}) => {
|
||||
const context1 = await browser.newContext({ ...iPhone });
|
||||
const page1 = await context1.newPage();
|
||||
await page1.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page1.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(false);
|
||||
const context2 = await browser.newContext({ ...iPhoneLandscape });
|
||||
const page2 = await context2.newPage();
|
||||
expect(await page2.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(true);
|
||||
await context1.close();
|
||||
await context2.close();
|
||||
});
|
||||
it.fail(WEBKIT)('should fire orientationchange event', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 300, height: 400 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
await page.evaluate(() => {
|
||||
window.counter = 0;
|
||||
window.addEventListener('orientationchange', () => console.log(++window.counter));
|
||||
});
|
||||
it('should support touch emulation', async({browser, server}) => {
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true);
|
||||
expect(await page.evaluate(dispatchTouch)).toBe('Received touch');
|
||||
await context.close();
|
||||
|
||||
function dispatchTouch() {
|
||||
let fulfill;
|
||||
const promise = new Promise(x => fulfill = x);
|
||||
window.ontouchstart = function(e) {
|
||||
fulfill('Received touch');
|
||||
};
|
||||
window.dispatchEvent(new Event('touchstart'));
|
||||
const event1 = page.waitForEvent('console');
|
||||
await page.setViewportSize({width: 400, height: 300});
|
||||
expect((await event1).text()).toBe('1');
|
||||
|
||||
fulfill('Did not receive touch');
|
||||
const event2 = page.waitForEvent('console');
|
||||
await page.setViewportSize({width: 300, height: 400});
|
||||
expect((await event2).text()).toBe('2');
|
||||
await context.close();
|
||||
});
|
||||
it('default mobile viewports to 980 width', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(980);
|
||||
await context.close();
|
||||
});
|
||||
it('respect meta viewport tag', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(320);
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
});
|
||||
it('should be detectable by Modernizr', async({browser, server}) => {
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/detect-touch.html');
|
||||
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES');
|
||||
await context.close();
|
||||
});
|
||||
it('should detect touch when applying viewport with touches', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 800, height: 600 }, hasTouch: true });
|
||||
describe.skip(FFOX)('Page.emulate', function() {
|
||||
it('should work', async({browser, server}) => {
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(375);
|
||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone');
|
||||
await context.close();
|
||||
});
|
||||
it('should support clicking', async({browser, server}) => {
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(button => button.style.marginTop = '200px', button);
|
||||
await button.click();
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.emulateMedia type', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
|
||||
await page.emulateMedia({ media: 'print' });
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true);
|
||||
await page.emulateMedia({});
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true);
|
||||
await page.emulateMedia({ media: '' });
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
|
||||
});
|
||||
it('should throw in case of bad type argument', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.emulateMedia({ media: 'bad' }).catch(e => error = e);
|
||||
expect(error.message).toBe('Unsupported media: bad');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.emulateMedia colorScheme', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.emulateMedia({ colorScheme: 'light' });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
|
||||
await page.emulateMedia({ colorScheme: 'dark' });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
|
||||
if (!WEBKIT) {
|
||||
// WebKit will always provide the value.
|
||||
await page.emulateMedia({ colorScheme: 'no-preference' });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(true);
|
||||
}
|
||||
});
|
||||
it('should default to light', async({page, server}) => {
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
|
||||
|
||||
await page.emulateMedia({ colorScheme: 'dark' });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
|
||||
|
||||
await page.emulateMedia({ colorScheme: null });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
|
||||
});
|
||||
it('should throw in case of bad argument', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.emulateMedia({ colorScheme: 'bad' }).catch(e => error = e);
|
||||
expect(error.message).toBe('Unsupported color scheme: bad');
|
||||
});
|
||||
it('should work during navigation', async({page, server}) => {
|
||||
await page.emulateMedia({ colorScheme: 'light' });
|
||||
const navigated = page.goto(server.EMPTY_PAGE);
|
||||
for (let i = 0; i < 9; i++) {
|
||||
await Promise.all([
|
||||
page.emulateMedia({ colorScheme: ['dark', 'light'][i & 1] }),
|
||||
new Promise(f => setTimeout(f, 1)),
|
||||
]);
|
||||
}
|
||||
await navigated;
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
});
|
||||
it('should work in popup', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext({ colorScheme: 'dark' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.addScriptTag({url: server.PREFIX + '/modernizr.js'});
|
||||
expect(await page.evaluate(() => Modernizr.touchevents)).toBe(true);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should support landscape emulation', async({browser, server}) => {
|
||||
const context1 = await browser.newContext({ ...iPhone });
|
||||
const page1 = await context1.newPage();
|
||||
await page1.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page1.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(false);
|
||||
const context2 = await browser.newContext({ ...iPhoneLandscape });
|
||||
const page2 = await context2.newPage();
|
||||
expect(await page2.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(true);
|
||||
await context1.close();
|
||||
await context2.close();
|
||||
});
|
||||
it.fail(WEBKIT)('should fire orientationchange event', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 300, height: 400 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
await page.evaluate(() => {
|
||||
window.counter = 0;
|
||||
window.addEventListener('orientationchange', () => console.log(++window.counter));
|
||||
});
|
||||
|
||||
const event1 = page.waitForEvent('console');
|
||||
await page.setViewportSize({width: 400, height: 300});
|
||||
expect((await event1).text()).toBe('1');
|
||||
|
||||
const event2 = page.waitForEvent('console');
|
||||
await page.setViewportSize({width: 300, height: 400});
|
||||
expect((await event2).text()).toBe('2');
|
||||
await context.close();
|
||||
});
|
||||
it('default mobile viewports to 980 width', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(980);
|
||||
await context.close();
|
||||
});
|
||||
it('respect meta viewport tag', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(320);
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip(FFOX)('Page.emulate', function() {
|
||||
it('should work', async({browser, server}) => {
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(375);
|
||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone');
|
||||
await context.close();
|
||||
});
|
||||
it('should support clicking', async({browser, server}) => {
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(button => button.style.marginTop = '200px', button);
|
||||
await button.click();
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.emulateMedia type', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
|
||||
await page.emulateMedia({ media: 'print' });
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true);
|
||||
await page.emulateMedia({});
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true);
|
||||
await page.emulateMedia({ media: '' });
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
|
||||
});
|
||||
it('should throw in case of bad type argument', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.emulateMedia({ media: 'bad' }).catch(e => error = e);
|
||||
expect(error.message).toBe('Unsupported media: bad');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.emulateMedia colorScheme', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.emulateMedia({ colorScheme: 'light' });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
|
||||
await page.emulateMedia({ colorScheme: 'dark' });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
|
||||
if (!WEBKIT) {
|
||||
// WebKit will always provide the value.
|
||||
await page.emulateMedia({ colorScheme: 'no-preference' });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(true);
|
||||
}
|
||||
});
|
||||
it('should default to light', async({page, server}) => {
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
|
||||
|
||||
await page.emulateMedia({ colorScheme: 'dark' });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
|
||||
|
||||
await page.emulateMedia({ colorScheme: null });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
|
||||
});
|
||||
it('should throw in case of bad argument', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.emulateMedia({ colorScheme: 'bad' }).catch(e => error = e);
|
||||
expect(error.message).toBe('Unsupported color scheme: bad');
|
||||
});
|
||||
it('should work during navigation', async({page, server}) => {
|
||||
await page.emulateMedia({ colorScheme: 'light' });
|
||||
const navigated = page.goto(server.EMPTY_PAGE);
|
||||
for (let i = 0; i < 9; i++) {
|
||||
await Promise.all([
|
||||
page.emulateMedia({ colorScheme: ['dark', 'light'][i & 1] }),
|
||||
new Promise(f => setTimeout(f, 1)),
|
||||
]);
|
||||
}
|
||||
await navigated;
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
});
|
||||
it('should work in popup', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext({ colorScheme: 'dark' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const page = await browser.newPage({ colorScheme: 'light' });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
await page.close();
|
||||
}
|
||||
});
|
||||
it('should work in cross-process iframe', async({browser, server}) => {
|
||||
const page = await browser.newPage({ colorScheme: 'dark' });
|
||||
}
|
||||
{
|
||||
const page = await browser.newPage({ colorScheme: 'light' });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const frame = page.frames()[1];
|
||||
expect(await frame.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
await page.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
it('should work in cross-process iframe', async({browser, server}) => {
|
||||
const page = await browser.newPage({ colorScheme: 'dark' });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const frame = page.frames()[1];
|
||||
expect(await frame.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
await page.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext({timezoneId})', function() {
|
||||
it('should work', async ({ browser }) => {
|
||||
const func = () => new Date(1479579154987).toString();
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'America/Jamaica' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'Pacific/Honolulu' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 08:12:34 GMT-1000 (Hawaii-Aleutian Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'America/Buenos_Aires' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 15:12:34 GMT-0300 (Argentina Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'Europe/Berlin' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 19:12:34 GMT+0100 (Central European Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
describe('BrowserContext({timezoneId})', function() {
|
||||
it('should work', async ({ browser }) => {
|
||||
const func = () => new Date(1479579154987).toString();
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'America/Jamaica' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'Pacific/Honolulu' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 08:12:34 GMT-1000 (Hawaii-Aleutian Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'America/Buenos_Aires' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 15:12:34 GMT-0300 (Argentina Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'Europe/Berlin' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 19:12:34 GMT+0100 (Central European Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it('should throw for invalid timezone IDs when creating pages', async({browser}) => {
|
||||
for (const timezoneId of ['Foo/Bar', 'Baz/Qux']) {
|
||||
let error = null;
|
||||
const context = await browser.newContext({ timezoneId });
|
||||
const page = await context.newPage().catch(e => error = e);
|
||||
expect(error.message).toBe(`Invalid timezone ID: ${timezoneId}`);
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it('should work for multiple pages sharing same process', async({browser, server}) => {
|
||||
const context = await browser.newContext({ timezoneId: 'Europe/Moscow' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
[popup] = await Promise.all([
|
||||
popup.waitForEvent('popup'),
|
||||
popup.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw for invalid timezone IDs when creating pages', async({browser}) => {
|
||||
for (const timezoneId of ['Foo/Bar', 'Baz/Qux']) {
|
||||
let error = null;
|
||||
const context = await browser.newContext({ timezoneId });
|
||||
const page = await context.newPage().catch(e => error = e);
|
||||
expect(error.message).toBe(`Invalid timezone ID: ${timezoneId}`);
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it('should work for multiple pages sharing same process', async({browser, server}) => {
|
||||
const context = await browser.newContext({ timezoneId: 'Europe/Moscow' });
|
||||
describe('BrowserContext({locale})', function() {
|
||||
it('should affect accept-language header', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['accept-language'].substr(0, 5)).toBe('fr-CH');
|
||||
await context.close();
|
||||
});
|
||||
it('should affect navigator.language', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(() => navigator.language)).toBe('fr-CH');
|
||||
await context.close();
|
||||
});
|
||||
it('should format number', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'en-US' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
[popup] = await Promise.all([
|
||||
popup.waitForEvent('popup'),
|
||||
popup.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(await page.evaluate(() => (1000000.50).toLocaleString())).toBe('1,000,000.5');
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext({locale})', function() {
|
||||
it('should affect accept-language header', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['accept-language'].substr(0, 5)).toBe('fr-CH');
|
||||
await context.close();
|
||||
});
|
||||
it('should affect navigator.language', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(() => navigator.language)).toBe('fr-CH');
|
||||
await context.close();
|
||||
});
|
||||
it('should format number', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'en-US' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate(() => (1000000.50).toLocaleString())).toBe('1,000,000.5');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate(() => (1000000.50).toLocaleString().replace(/\s/g, ' '))).toBe('1 000 000,5');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it('should format date', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'en-US', timezoneId: 'America/Los_Angeles' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const formatted = 'Sat Nov 19 2016 10:12:34 GMT-0800 (Pacific Standard Time)';
|
||||
expect(await page.evaluate(() => new Date(1479579154987).toString())).toBe(formatted);
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'de-DE', timezoneId: 'Europe/Berlin' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate(() => new Date(1479579154987).toString())).toBe(
|
||||
'Sat Nov 19 2016 19:12:34 GMT+0100 (Mitteleuropäische Normalzeit)');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it('should format number in popups', async({browser, server}) => {
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window._popup = window.open(url), server.PREFIX + '/formatted-number.html'),
|
||||
]);
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
const result = await popup.evaluate(() => window.result);
|
||||
expect(result).toBe('1 000 000,5');
|
||||
expect(await page.evaluate(() => (1000000.50).toLocaleString().replace(/\s/g, ' '))).toBe('1 000 000,5');
|
||||
await context.close();
|
||||
});
|
||||
it('should affect navigator.language in popups', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
}
|
||||
});
|
||||
it('should format date', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'en-US', timezoneId: 'America/Los_Angeles' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window._popup = window.open(url), server.PREFIX + '/formatted-number.html'),
|
||||
]);
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
const result = await popup.evaluate(() => window.initialNavigatorLanguage);
|
||||
expect(result).toBe('fr-CH');
|
||||
const formatted = 'Sat Nov 19 2016 10:12:34 GMT-0800 (Pacific Standard Time)';
|
||||
expect(await page.evaluate(() => new Date(1479579154987).toString())).toBe(formatted);
|
||||
await context.close();
|
||||
});
|
||||
it('should work for multiple pages sharing same process', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'ru-RU' });
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'de-DE', timezoneId: 'Europe/Berlin' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
[popup] = await Promise.all([
|
||||
popup.waitForEvent('popup'),
|
||||
popup.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(await page.evaluate(() => new Date(1479579154987).toString())).toBe(
|
||||
'Sat Nov 19 2016 19:12:34 GMT+0100 (Mitteleuropäische Normalzeit)');
|
||||
await context.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
it('should format number in popups', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
||||
describe.fail(!headless)('focus', function() {
|
||||
it('should think that it is focused by default', async({page}) => {
|
||||
expect(await page.evaluate('document.hasFocus()')).toBe(true);
|
||||
});
|
||||
it.fail(FFOX)('should think that all pages are focused', async({page}) => {
|
||||
const page2 = await page.context().newPage();
|
||||
expect(await page.evaluate('document.hasFocus()')).toBe(true);
|
||||
expect(await page2.evaluate('document.hasFocus()')).toBe(true);
|
||||
await page2.close();
|
||||
});
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window._popup = window.open(url), server.PREFIX + '/formatted-number.html'),
|
||||
]);
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
const result = await popup.evaluate(() => window.result);
|
||||
expect(result).toBe('1 000 000,5');
|
||||
await context.close();
|
||||
});
|
||||
};
|
||||
it('should affect navigator.language in popups', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window._popup = window.open(url), server.PREFIX + '/formatted-number.html'),
|
||||
]);
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
const result = await popup.evaluate(() => window.initialNavigatorLanguage);
|
||||
expect(result).toBe('fr-CH');
|
||||
await context.close();
|
||||
});
|
||||
it('should work for multiple pages sharing same process', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'ru-RU' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
[popup] = await Promise.all([
|
||||
popup.waitForEvent('popup'),
|
||||
popup.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe.fail(!headless)('focus', function() {
|
||||
it('should think that it is focused by default', async({page}) => {
|
||||
expect(await page.evaluate('document.hasFocus()')).toBe(true);
|
||||
});
|
||||
it.fail(FFOX)('should think that all pages are focused', async({page}) => {
|
||||
const page2 = await page.context().newPage();
|
||||
expect(await page.evaluate('document.hasFocus()')).toBe(true);
|
||||
expect(await page2.evaluate('document.hasFocus()')).toBe(true);
|
||||
await page2.close();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,441 +17,436 @@
|
|||
|
||||
const utils = require('./utils');
|
||||
const path = require('path');
|
||||
const {FFOX, CHROMIUM, WEBKIT} = utils.testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({FFOX, CHROMIUM, WEBKIT, LINUX}) {
|
||||
|
||||
describe('Page.evaluate', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const result = await page.evaluate(() => 7 * 3);
|
||||
expect(result).toBe(21);
|
||||
describe('Page.evaluate', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const result = await page.evaluate(() => 7 * 3);
|
||||
expect(result).toBe(21);
|
||||
});
|
||||
it('should transfer NaN', async({page, server}) => {
|
||||
const result = await page.evaluate(a => a, NaN);
|
||||
expect(Object.is(result, NaN)).toBe(true);
|
||||
});
|
||||
it('should transfer -0', async({page, server}) => {
|
||||
const result = await page.evaluate(a => a, -0);
|
||||
expect(Object.is(result, -0)).toBe(true);
|
||||
});
|
||||
it('should transfer Infinity', async({page, server}) => {
|
||||
const result = await page.evaluate(a => a, Infinity);
|
||||
expect(Object.is(result, Infinity)).toBe(true);
|
||||
});
|
||||
it('should transfer -Infinity', async({page, server}) => {
|
||||
const result = await page.evaluate(a => a, -Infinity);
|
||||
expect(Object.is(result, -Infinity)).toBe(true);
|
||||
});
|
||||
it('should transfer arrays', async({page, server}) => {
|
||||
const result = await page.evaluate(a => a, [1, 2, 3]);
|
||||
expect(result).toEqual([1,2,3]);
|
||||
});
|
||||
it('should transfer arrays as arrays, not objects', async({page, server}) => {
|
||||
const result = await page.evaluate(a => Array.isArray(a), [1, 2, 3]);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
it('should transfer maps as empty objects', async({page, server}) => {
|
||||
const result = await page.evaluate(a => a.x.constructor.name + ' ' + JSON.stringify(a.x), {x: new Map([[1, 2]])});
|
||||
expect(result).toBe('Object {}');
|
||||
});
|
||||
it('should modify global environment', async({page}) => {
|
||||
await page.evaluate(() => window.globalVar = 123);
|
||||
expect(await page.evaluate('globalVar')).toBe(123);
|
||||
});
|
||||
it('should evaluate in the page context', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/global-var.html');
|
||||
expect(await page.evaluate('globalVar')).toBe(123);
|
||||
});
|
||||
it('should return undefined for objects with symbols', async({page, server}) => {
|
||||
expect(await page.evaluate(() => [Symbol('foo4')])).toBe(undefined);
|
||||
expect(await page.evaluate(() => {
|
||||
const a = { };
|
||||
a[Symbol('foo4')] = 42;
|
||||
return a;
|
||||
})).toEqual({});
|
||||
expect(await page.evaluate(() => {
|
||||
return { foo: [{ a: Symbol('foo4') }] };
|
||||
})).toBe(undefined);
|
||||
});
|
||||
it('should work with function shorthands', async({page, server}) => {
|
||||
const a = {
|
||||
sum([a, b]) { return a + b; },
|
||||
async mult([a, b]) { return a * b; }
|
||||
};
|
||||
expect(await page.evaluate(a.sum, [1, 2])).toBe(3);
|
||||
expect(await page.evaluate(a.mult, [2, 4])).toBe(8);
|
||||
});
|
||||
it('should work with unicode chars', async({page, server}) => {
|
||||
const result = await page.evaluate(a => a['中文字符'], {'中文字符': 42});
|
||||
expect(result).toBe(42);
|
||||
});
|
||||
it('should throw when evaluation triggers reload', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.evaluate(() => {
|
||||
location.reload();
|
||||
return new Promise(() => {});
|
||||
}).catch(e => error = e);
|
||||
expect(error.message).toContain('navigation');
|
||||
});
|
||||
it('should await promise', async({page, server}) => {
|
||||
const result = await page.evaluate(() => Promise.resolve(8 * 7));
|
||||
expect(result).toBe(56);
|
||||
});
|
||||
it('should work right after framenavigated', async({page, server}) => {
|
||||
let frameEvaluation = null;
|
||||
page.on('framenavigated', async frame => {
|
||||
frameEvaluation = frame.evaluate(() => 6 * 7);
|
||||
});
|
||||
it('should transfer NaN', async({page, server}) => {
|
||||
const result = await page.evaluate(a => a, NaN);
|
||||
expect(Object.is(result, NaN)).toBe(true);
|
||||
});
|
||||
it('should transfer -0', async({page, server}) => {
|
||||
const result = await page.evaluate(a => a, -0);
|
||||
expect(Object.is(result, -0)).toBe(true);
|
||||
});
|
||||
it('should transfer Infinity', async({page, server}) => {
|
||||
const result = await page.evaluate(a => a, Infinity);
|
||||
expect(Object.is(result, Infinity)).toBe(true);
|
||||
});
|
||||
it('should transfer -Infinity', async({page, server}) => {
|
||||
const result = await page.evaluate(a => a, -Infinity);
|
||||
expect(Object.is(result, -Infinity)).toBe(true);
|
||||
});
|
||||
it('should transfer arrays', async({page, server}) => {
|
||||
const result = await page.evaluate(a => a, [1, 2, 3]);
|
||||
expect(result).toEqual([1,2,3]);
|
||||
});
|
||||
it('should transfer arrays as arrays, not objects', async({page, server}) => {
|
||||
const result = await page.evaluate(a => Array.isArray(a), [1, 2, 3]);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
it('should transfer maps as empty objects', async({page, server}) => {
|
||||
const result = await page.evaluate(a => a.x.constructor.name + ' ' + JSON.stringify(a.x), {x: new Map([[1, 2]])});
|
||||
expect(result).toBe('Object {}');
|
||||
});
|
||||
it('should modify global environment', async({page}) => {
|
||||
await page.evaluate(() => window.globalVar = 123);
|
||||
expect(await page.evaluate('globalVar')).toBe(123);
|
||||
});
|
||||
it('should evaluate in the page context', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/global-var.html');
|
||||
expect(await page.evaluate('globalVar')).toBe(123);
|
||||
});
|
||||
it('should return undefined for objects with symbols', async({page, server}) => {
|
||||
expect(await page.evaluate(() => [Symbol('foo4')])).toBe(undefined);
|
||||
expect(await page.evaluate(() => {
|
||||
const a = { };
|
||||
a[Symbol('foo4')] = 42;
|
||||
return a;
|
||||
})).toEqual({});
|
||||
expect(await page.evaluate(() => {
|
||||
return { foo: [{ a: Symbol('foo4') }] };
|
||||
})).toBe(undefined);
|
||||
});
|
||||
it('should work with function shorthands', async({page, server}) => {
|
||||
const a = {
|
||||
sum([a, b]) { return a + b; },
|
||||
async mult([a, b]) { return a * b; }
|
||||
};
|
||||
expect(await page.evaluate(a.sum, [1, 2])).toBe(3);
|
||||
expect(await page.evaluate(a.mult, [2, 4])).toBe(8);
|
||||
});
|
||||
it('should work with unicode chars', async({page, server}) => {
|
||||
const result = await page.evaluate(a => a['中文字符'], {'中文字符': 42});
|
||||
expect(result).toBe(42);
|
||||
});
|
||||
it('should throw when evaluation triggers reload', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.evaluate(() => {
|
||||
location.reload();
|
||||
return new Promise(() => {});
|
||||
}).catch(e => error = e);
|
||||
expect(error.message).toContain('navigation');
|
||||
});
|
||||
it('should await promise', async({page, server}) => {
|
||||
const result = await page.evaluate(() => Promise.resolve(8 * 7));
|
||||
expect(result).toBe(56);
|
||||
});
|
||||
it('should work right after framenavigated', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await frameEvaluation).toBe(42);
|
||||
});
|
||||
it('should work right after a cross-origin navigation', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let frameEvaluation = null;
|
||||
page.on('framenavigated', async frame => {
|
||||
frameEvaluation = frame.evaluate(() => 6 * 7);
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
expect(await frameEvaluation).toBe(42);
|
||||
});
|
||||
it('should work from-inside an exposed function', async({page, server}) => {
|
||||
// Setup inpage callback, which calls Page.evaluate
|
||||
await page.exposeFunction('callController', async function(a, b) {
|
||||
return await page.evaluate(({ a, b }) => a * b, { a, b });
|
||||
});
|
||||
it('should work right after a cross-origin navigation', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let frameEvaluation = null;
|
||||
page.on('framenavigated', async frame => {
|
||||
frameEvaluation = frame.evaluate(() => 6 * 7);
|
||||
});
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
expect(await frameEvaluation).toBe(42);
|
||||
const result = await page.evaluate(async function() {
|
||||
return await callController(9, 3);
|
||||
});
|
||||
it('should work from-inside an exposed function', async({page, server}) => {
|
||||
// Setup inpage callback, which calls Page.evaluate
|
||||
await page.exposeFunction('callController', async function(a, b) {
|
||||
return await page.evaluate(({ a, b }) => a * b, { a, b });
|
||||
});
|
||||
const result = await page.evaluate(async function() {
|
||||
return await callController(9, 3);
|
||||
});
|
||||
expect(result).toBe(27);
|
||||
expect(result).toBe(27);
|
||||
});
|
||||
it('should reject promise with exception', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.evaluate(() => not_existing_object.property).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('not_existing_object');
|
||||
});
|
||||
it('should support thrown strings as error messages', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.evaluate(() => { throw 'qwerty'; }).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('qwerty');
|
||||
});
|
||||
it('should support thrown numbers as error messages', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.evaluate(() => { throw 100500; }).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('100500');
|
||||
});
|
||||
it('should return complex objects', async({page, server}) => {
|
||||
const object = {foo: 'bar!'};
|
||||
const result = await page.evaluate(a => a, object);
|
||||
expect(result).not.toBe(object);
|
||||
expect(result).toEqual(object);
|
||||
});
|
||||
it('should return NaN', async({page, server}) => {
|
||||
const result = await page.evaluate(() => NaN);
|
||||
expect(Object.is(result, NaN)).toBe(true);
|
||||
});
|
||||
it('should return -0', async({page, server}) => {
|
||||
const result = await page.evaluate(() => -0);
|
||||
expect(Object.is(result, -0)).toBe(true);
|
||||
});
|
||||
it('should return Infinity', async({page, server}) => {
|
||||
const result = await page.evaluate(() => Infinity);
|
||||
expect(Object.is(result, Infinity)).toBe(true);
|
||||
});
|
||||
it('should return -Infinity', async({page, server}) => {
|
||||
const result = await page.evaluate(() => -Infinity);
|
||||
expect(Object.is(result, -Infinity)).toBe(true);
|
||||
});
|
||||
it('should accept "undefined" as one of multiple parameters', async({page, server}) => {
|
||||
const result = await page.evaluate(({ a, b }) => Object.is(a, undefined) && Object.is(b, 'foo'), { a: undefined, b: 'foo' });
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
it('should properly serialize undefined arguments', async({page}) => {
|
||||
expect(await page.evaluate(x => ({a: x}), undefined)).toEqual({});
|
||||
});
|
||||
it('should properly serialize undefined fields', async({page}) => {
|
||||
expect(await page.evaluate(() => ({a: undefined}))).toEqual({});
|
||||
});
|
||||
it('should properly serialize null arguments', async({page}) => {
|
||||
expect(await page.evaluate(x => x, null)).toEqual(null);
|
||||
});
|
||||
it('should properly serialize null fields', async({page}) => {
|
||||
expect(await page.evaluate(() => ({a: null}))).toEqual({a: null});
|
||||
});
|
||||
it('should return undefined for non-serializable objects', async({page, server}) => {
|
||||
expect(await page.evaluate(() => window)).toBe(undefined);
|
||||
});
|
||||
it('should fail for circular object', async({page, server}) => {
|
||||
const result = await page.evaluate(() => {
|
||||
const a = {};
|
||||
const b = {a};
|
||||
a.b = b;
|
||||
return a;
|
||||
});
|
||||
it('should reject promise with exception', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.evaluate(() => not_existing_object.property).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('not_existing_object');
|
||||
expect(result).toBe(undefined);
|
||||
});
|
||||
it('should be able to throw a tricky error', async({page, server}) => {
|
||||
const windowHandle = await page.evaluateHandle(() => window);
|
||||
const errorText = await windowHandle.jsonValue().catch(e => e.message);
|
||||
const error = await page.evaluate(errorText => {
|
||||
throw new Error(errorText);
|
||||
}, errorText).catch(e => e);
|
||||
expect(error.message).toContain(errorText);
|
||||
});
|
||||
it('should accept a string', async({page, server}) => {
|
||||
const result = await page.evaluate('1 + 2');
|
||||
expect(result).toBe(3);
|
||||
});
|
||||
it('should accept a string with semi colons', async({page, server}) => {
|
||||
const result = await page.evaluate('1 + 5;');
|
||||
expect(result).toBe(6);
|
||||
});
|
||||
it('should accept a string with comments', async({page, server}) => {
|
||||
const result = await page.evaluate('2 + 5;\n// do some math!');
|
||||
expect(result).toBe(7);
|
||||
});
|
||||
it('should accept element handle as an argument', async({page, server}) => {
|
||||
await page.setContent('<section>42</section>');
|
||||
const element = await page.$('section');
|
||||
const text = await page.evaluate(e => e.textContent, element);
|
||||
expect(text).toBe('42');
|
||||
});
|
||||
it('should throw if underlying element was disposed', async({page, server}) => {
|
||||
await page.setContent('<section>39</section>');
|
||||
const element = await page.$('section');
|
||||
expect(element).toBeTruthy();
|
||||
await element.dispose();
|
||||
let error = null;
|
||||
await page.evaluate(e => e.textContent, element).catch(e => error = e);
|
||||
expect(error.message).toContain('JSHandle is disposed');
|
||||
});
|
||||
it('should simulate a user gesture', async({page, server}) => {
|
||||
const result = await page.evaluate(() => {
|
||||
document.body.appendChild(document.createTextNode('test'));
|
||||
document.execCommand('selectAll');
|
||||
return document.execCommand('copy');
|
||||
});
|
||||
it('should support thrown strings as error messages', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.evaluate(() => { throw 'qwerty'; }).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('qwerty');
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
it('should throw a nice error after a navigation', async({page, server}) => {
|
||||
const errorPromise = page.evaluate(() => new Promise(f => window.__resolve = f)).catch(e => e);
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.evaluate(() => {
|
||||
window.location.reload();
|
||||
setTimeout(() => window.__resolve(42), 1000);
|
||||
})
|
||||
]);
|
||||
const error = await errorPromise;
|
||||
expect(error.message).toContain('navigation');
|
||||
});
|
||||
it('should not throw an error when evaluation does a navigation', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
const result = await page.evaluate(() => {
|
||||
window.location = '/empty.html';
|
||||
return [42];
|
||||
});
|
||||
it('should support thrown numbers as error messages', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.evaluate(() => { throw 100500; }).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('100500');
|
||||
expect(result).toEqual([42]);
|
||||
});
|
||||
it.slow()('should transfer 100Mb of data from page to node.js', async({page, server}) => {
|
||||
const a = await page.evaluate(() => Array(100 * 1024 * 1024 + 1).join('a'));
|
||||
expect(a.length).toBe(100 * 1024 * 1024);
|
||||
});
|
||||
it('should throw error with detailed information on exception inside promise ', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.evaluate(() => new Promise(() => {
|
||||
throw new Error('Error in promise');
|
||||
})).catch(e => error = e);
|
||||
expect(error.message).toContain('Error in promise');
|
||||
});
|
||||
it('should work even when JSON is set to null', async ({ page }) => {
|
||||
await page.evaluate(() => { window.JSON.stringify = null; window.JSON = null; });
|
||||
const result = await page.evaluate(() => ({abc: 123}));
|
||||
expect(result).toEqual({abc: 123});
|
||||
});
|
||||
it('should await promise from popup', async function({page, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const result = await page.evaluate(() => {
|
||||
const win = window.open('about:blank');
|
||||
return new win.Promise(f => f(42));
|
||||
});
|
||||
it('should return complex objects', async({page, server}) => {
|
||||
const object = {foo: 'bar!'};
|
||||
const result = await page.evaluate(a => a, object);
|
||||
expect(result).not.toBe(object);
|
||||
expect(result).toEqual(object);
|
||||
expect(result).toBe(42);
|
||||
});
|
||||
it('should work with new Function() and CSP', async({page, server}) => {
|
||||
server.setCSP('/empty.html', 'script-src ' + server.PREFIX);
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
expect(await page.evaluate(() => new Function('return true')())).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.addInitScript', function() {
|
||||
it('should evaluate before anything else on the page', async({page, server}) => {
|
||||
await page.addInitScript(function(){
|
||||
window.injected = 123;
|
||||
});
|
||||
it('should return NaN', async({page, server}) => {
|
||||
const result = await page.evaluate(() => NaN);
|
||||
expect(Object.is(result, NaN)).toBe(true);
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.result)).toBe(123);
|
||||
});
|
||||
it('should work with a path', async({page, server}) => {
|
||||
await page.addInitScript({ path: path.join(__dirname, 'assets/injectedfile.js') });
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.result)).toBe(123);
|
||||
});
|
||||
it('should work with content', async({page, server}) => {
|
||||
await page.addInitScript({ content: 'window.injected = 123' });
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.result)).toBe(123);
|
||||
});
|
||||
it('should throw without path and content', async({page, server}) => {
|
||||
const error = await page.addInitScript({ foo: 'bar' }).catch(e => e);
|
||||
expect(error.message).toBe('Either path or content property must be present');
|
||||
});
|
||||
it('should work with browser context scripts', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.addInitScript(() => window.temp = 123);
|
||||
const page = await context.newPage();
|
||||
await page.addInitScript(() => window.injected = window.temp);
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.result)).toBe(123);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with browser context scripts with a path', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.addInitScript({ path: path.join(__dirname, 'assets/injectedfile.js') });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.result)).toBe(123);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with browser context scripts for already created pages', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await context.addInitScript(() => window.temp = 123);
|
||||
await page.addInitScript(() => window.injected = window.temp);
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.result)).toBe(123);
|
||||
await context.close();
|
||||
});
|
||||
it('should support multiple scripts', async({page, server}) => {
|
||||
await page.addInitScript(function(){
|
||||
window.script1 = 1;
|
||||
});
|
||||
it('should return -0', async({page, server}) => {
|
||||
const result = await page.evaluate(() => -0);
|
||||
expect(Object.is(result, -0)).toBe(true);
|
||||
await page.addInitScript(function(){
|
||||
window.script2 = 2;
|
||||
});
|
||||
it('should return Infinity', async({page, server}) => {
|
||||
const result = await page.evaluate(() => Infinity);
|
||||
expect(Object.is(result, Infinity)).toBe(true);
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.script1)).toBe(1);
|
||||
expect(await page.evaluate(() => window.script2)).toBe(2);
|
||||
});
|
||||
it('should work with CSP', async({page, server}) => {
|
||||
server.setCSP('/empty.html', 'script-src ' + server.PREFIX);
|
||||
await page.addInitScript(function(){
|
||||
window.injected = 123;
|
||||
});
|
||||
it('should return -Infinity', async({page, server}) => {
|
||||
const result = await page.evaluate(() => -Infinity);
|
||||
expect(Object.is(result, -Infinity)).toBe(true);
|
||||
});
|
||||
it('should accept "undefined" as one of multiple parameters', async({page, server}) => {
|
||||
const result = await page.evaluate(({ a, b }) => Object.is(a, undefined) && Object.is(b, 'foo'), { a: undefined, b: 'foo' });
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
it('should properly serialize undefined arguments', async({page}) => {
|
||||
expect(await page.evaluate(x => ({a: x}), undefined)).toEqual({});
|
||||
});
|
||||
it('should properly serialize undefined fields', async({page}) => {
|
||||
expect(await page.evaluate(() => ({a: undefined}))).toEqual({});
|
||||
});
|
||||
it('should properly serialize null arguments', async({page}) => {
|
||||
expect(await page.evaluate(x => x, null)).toEqual(null);
|
||||
});
|
||||
it('should properly serialize null fields', async({page}) => {
|
||||
expect(await page.evaluate(() => ({a: null}))).toEqual({a: null});
|
||||
});
|
||||
it('should return undefined for non-serializable objects', async({page, server}) => {
|
||||
expect(await page.evaluate(() => window)).toBe(undefined);
|
||||
});
|
||||
it('should fail for circular object', async({page, server}) => {
|
||||
const result = await page.evaluate(() => {
|
||||
const a = {};
|
||||
const b = {a};
|
||||
a.b = b;
|
||||
return a;
|
||||
});
|
||||
expect(result).toBe(undefined);
|
||||
});
|
||||
it('should be able to throw a tricky error', async({page, server}) => {
|
||||
const windowHandle = await page.evaluateHandle(() => window);
|
||||
const errorText = await windowHandle.jsonValue().catch(e => e.message);
|
||||
const error = await page.evaluate(errorText => {
|
||||
throw new Error(errorText);
|
||||
}, errorText).catch(e => e);
|
||||
expect(error.message).toContain(errorText);
|
||||
});
|
||||
it('should accept a string', async({page, server}) => {
|
||||
const result = await page.evaluate('1 + 2');
|
||||
expect(result).toBe(3);
|
||||
});
|
||||
it('should accept a string with semi colons', async({page, server}) => {
|
||||
const result = await page.evaluate('1 + 5;');
|
||||
expect(result).toBe(6);
|
||||
});
|
||||
it('should accept a string with comments', async({page, server}) => {
|
||||
const result = await page.evaluate('2 + 5;\n// do some math!');
|
||||
expect(result).toBe(7);
|
||||
});
|
||||
it('should accept element handle as an argument', async({page, server}) => {
|
||||
await page.setContent('<section>42</section>');
|
||||
const element = await page.$('section');
|
||||
const text = await page.evaluate(e => e.textContent, element);
|
||||
expect(text).toBe('42');
|
||||
});
|
||||
it('should throw if underlying element was disposed', async({page, server}) => {
|
||||
await page.setContent('<section>39</section>');
|
||||
const element = await page.$('section');
|
||||
expect(element).toBeTruthy();
|
||||
await element.dispose();
|
||||
let error = null;
|
||||
await page.evaluate(e => e.textContent, element).catch(e => error = e);
|
||||
expect(error.message).toContain('JSHandle is disposed');
|
||||
});
|
||||
it('should simulate a user gesture', async({page, server}) => {
|
||||
const result = await page.evaluate(() => {
|
||||
document.body.appendChild(document.createTextNode('test'));
|
||||
document.execCommand('selectAll');
|
||||
return document.execCommand('copy');
|
||||
});
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
it('should throw a nice error after a navigation', async({page, server}) => {
|
||||
const errorPromise = page.evaluate(() => new Promise(f => window.__resolve = f)).catch(e => e);
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.evaluate(() => {
|
||||
window.location.reload();
|
||||
setTimeout(() => window.__resolve(42), 1000);
|
||||
})
|
||||
]);
|
||||
const error = await errorPromise;
|
||||
expect(error.message).toContain('navigation');
|
||||
});
|
||||
it('should not throw an error when evaluation does a navigation', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
const result = await page.evaluate(() => {
|
||||
window.location = '/empty.html';
|
||||
return [42];
|
||||
});
|
||||
expect(result).toEqual([42]);
|
||||
});
|
||||
it.slow()('should transfer 100Mb of data from page to node.js', async({page, server}) => {
|
||||
const a = await page.evaluate(() => Array(100 * 1024 * 1024 + 1).join('a'));
|
||||
expect(a.length).toBe(100 * 1024 * 1024);
|
||||
});
|
||||
it('should throw error with detailed information on exception inside promise ', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.evaluate(() => new Promise(() => {
|
||||
throw new Error('Error in promise');
|
||||
})).catch(e => error = e);
|
||||
expect(error.message).toContain('Error in promise');
|
||||
});
|
||||
it('should work even when JSON is set to null', async ({ page }) => {
|
||||
await page.evaluate(() => { window.JSON.stringify = null; window.JSON = null; });
|
||||
const result = await page.evaluate(() => ({abc: 123}));
|
||||
expect(result).toEqual({abc: 123});
|
||||
});
|
||||
it('should await promise from popup', async function({page, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const result = await page.evaluate(() => {
|
||||
const win = window.open('about:blank');
|
||||
return new win.Promise(f => f(42));
|
||||
});
|
||||
expect(result).toBe(42);
|
||||
});
|
||||
it('should work with new Function() and CSP', async({page, server}) => {
|
||||
server.setCSP('/empty.html', 'script-src ' + server.PREFIX);
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
expect(await page.evaluate(() => new Function('return true')())).toBe(true);
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
expect(await page.evaluate(() => window.injected)).toBe(123);
|
||||
|
||||
// Make sure CSP works.
|
||||
await page.addScriptTag({content: 'window.e = 10;'}).catch(e => void e);
|
||||
expect(await page.evaluate(() => window.e)).toBe(undefined);
|
||||
});
|
||||
it('should work after a cross origin navigation', async({page, server}) => {
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX);
|
||||
await page.addInitScript(function(){
|
||||
window.injected = 123;
|
||||
});
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.result)).toBe(123);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Frame.evaluate', function() {
|
||||
it('should have different execution contexts', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
expect(page.frames().length).toBe(2);
|
||||
await page.frames()[0].evaluate(() => window.FOO = 'foo');
|
||||
await page.frames()[1].evaluate(() => window.FOO = 'bar');
|
||||
expect(await page.frames()[0].evaluate(() => window.FOO)).toBe('foo');
|
||||
expect(await page.frames()[1].evaluate(() => window.FOO)).toBe('bar');
|
||||
});
|
||||
it('should have correct execution contexts', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expect(await page.frames()[0].evaluate(() => document.body.textContent.trim())).toBe('');
|
||||
expect(await page.frames()[1].evaluate(() => document.body.textContent.trim())).toBe(`Hi, I'm frame`);
|
||||
});
|
||||
|
||||
describe('Page.addInitScript', function() {
|
||||
it('should evaluate before anything else on the page', async({page, server}) => {
|
||||
await page.addInitScript(function(){
|
||||
window.injected = 123;
|
||||
});
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.result)).toBe(123);
|
||||
});
|
||||
it('should work with a path', async({page, server}) => {
|
||||
await page.addInitScript({ path: path.join(__dirname, 'assets/injectedfile.js') });
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.result)).toBe(123);
|
||||
});
|
||||
it('should work with content', async({page, server}) => {
|
||||
await page.addInitScript({ content: 'window.injected = 123' });
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.result)).toBe(123);
|
||||
});
|
||||
it('should throw without path and content', async({page, server}) => {
|
||||
const error = await page.addInitScript({ foo: 'bar' }).catch(e => e);
|
||||
expect(error.message).toBe('Either path or content property must be present');
|
||||
});
|
||||
it('should work with browser context scripts', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.addInitScript(() => window.temp = 123);
|
||||
const page = await context.newPage();
|
||||
await page.addInitScript(() => window.injected = window.temp);
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.result)).toBe(123);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with browser context scripts with a path', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.addInitScript({ path: path.join(__dirname, 'assets/injectedfile.js') });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.result)).toBe(123);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with browser context scripts for already created pages', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await context.addInitScript(() => window.temp = 123);
|
||||
await page.addInitScript(() => window.injected = window.temp);
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.result)).toBe(123);
|
||||
await context.close();
|
||||
});
|
||||
it('should support multiple scripts', async({page, server}) => {
|
||||
await page.addInitScript(function(){
|
||||
window.script1 = 1;
|
||||
});
|
||||
await page.addInitScript(function(){
|
||||
window.script2 = 2;
|
||||
});
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.script1)).toBe(1);
|
||||
expect(await page.evaluate(() => window.script2)).toBe(2);
|
||||
});
|
||||
it('should work with CSP', async({page, server}) => {
|
||||
server.setCSP('/empty.html', 'script-src ' + server.PREFIX);
|
||||
await page.addInitScript(function(){
|
||||
window.injected = 123;
|
||||
});
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
expect(await page.evaluate(() => window.injected)).toBe(123);
|
||||
|
||||
// Make sure CSP works.
|
||||
await page.addScriptTag({content: 'window.e = 10;'}).catch(e => void e);
|
||||
expect(await page.evaluate(() => window.e)).toBe(undefined);
|
||||
});
|
||||
it('should work after a cross origin navigation', async({page, server}) => {
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX);
|
||||
await page.addInitScript(function(){
|
||||
window.injected = 123;
|
||||
});
|
||||
await page.goto(server.PREFIX + '/tamperable.html');
|
||||
expect(await page.evaluate(() => window.result)).toBe(123);
|
||||
});
|
||||
function expectContexts(page, count) {
|
||||
if (CHROMIUM)
|
||||
expect(page._delegate._mainFrameSession._contextIdToContext.size).toBe(count);
|
||||
else
|
||||
expect(page._delegate._contextIdToContext.size).toBe(count);
|
||||
}
|
||||
it('should dispose context on navigation', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expectContexts(page, 4);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expectContexts(page, 2);
|
||||
});
|
||||
it('should dispose context on cross-origin navigation', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expectContexts(page, 4);
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
expectContexts(page, 2);
|
||||
});
|
||||
|
||||
describe('Frame.evaluate', function() {
|
||||
it('should have different execution contexts', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
expect(page.frames().length).toBe(2);
|
||||
await page.frames()[0].evaluate(() => window.FOO = 'foo');
|
||||
await page.frames()[1].evaluate(() => window.FOO = 'bar');
|
||||
expect(await page.frames()[0].evaluate(() => window.FOO)).toBe('foo');
|
||||
expect(await page.frames()[1].evaluate(() => window.FOO)).toBe('bar');
|
||||
});
|
||||
it('should have correct execution contexts', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expect(await page.frames()[0].evaluate(() => document.body.textContent.trim())).toBe('');
|
||||
expect(await page.frames()[1].evaluate(() => document.body.textContent.trim())).toBe(`Hi, I'm frame`);
|
||||
});
|
||||
|
||||
function expectContexts(page, count) {
|
||||
if (CHROMIUM)
|
||||
expect(page._delegate._mainFrameSession._contextIdToContext.size).toBe(count);
|
||||
else
|
||||
expect(page._delegate._contextIdToContext.size).toBe(count);
|
||||
}
|
||||
it('should dispose context on navigation', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expectContexts(page, 4);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expectContexts(page, 2);
|
||||
});
|
||||
it('should dispose context on cross-origin navigation', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expectContexts(page, 4);
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
expectContexts(page, 2);
|
||||
});
|
||||
|
||||
it('should execute after cross-site navigation', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const mainFrame = page.mainFrame();
|
||||
expect(await mainFrame.evaluate(() => window.location.href)).toContain('localhost');
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
expect(await mainFrame.evaluate(() => window.location.href)).toContain('127');
|
||||
});
|
||||
it('should not allow cross-frame js handles', async({page, server}) => {
|
||||
// TODO: this should actually be possible because frames script each other,
|
||||
// but protocol implementations do not support this. For now, assume current
|
||||
// behavior.
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
const handle = await page.evaluateHandle(() => {
|
||||
const iframe = document.querySelector('iframe');
|
||||
const foo = { bar: 'baz' };
|
||||
iframe.contentWindow.__foo = foo;
|
||||
return foo;
|
||||
});
|
||||
const childFrame = page.mainFrame().childFrames()[0];
|
||||
const childResult = await childFrame.evaluate(() => window.__foo);
|
||||
expect(childResult).toEqual({ bar: 'baz' });
|
||||
const error = await childFrame.evaluate(foo => foo.bar, handle).catch(e => e);
|
||||
expect(error.message).toBe('JSHandles can be evaluated only in the context they were created!');
|
||||
});
|
||||
it('should allow cross-frame element handles', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
const bodyHandle = await page.mainFrame().childFrames()[0].$('body');
|
||||
const result = await page.evaluate(body => body.innerHTML, bodyHandle);
|
||||
expect(result.trim()).toBe('<div>Hi, I\'m frame</div>');
|
||||
});
|
||||
it('should not allow cross-frame element handles when frames do not script each other', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const bodyHandle = await frame.$('body');
|
||||
const error = await page.evaluate(body => body.innerHTML, bodyHandle).catch(e => e);
|
||||
expect(error.message).toContain('Unable to adopt element handle from a different document');
|
||||
});
|
||||
it.fail(FFOX)('should return non-empty Node.constructor.name in utility context', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const context = await frame._utilityContext();
|
||||
const elementHandle = await context.evaluateHandleInternal(() => window.top.document.querySelector('#frame1'));
|
||||
const constructorName = await context.evaluateInternal(node => node.constructor.name, elementHandle);
|
||||
expect(constructorName).toBe('HTMLIFrameElement');
|
||||
});
|
||||
it('should execute after cross-site navigation', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const mainFrame = page.mainFrame();
|
||||
expect(await mainFrame.evaluate(() => window.location.href)).toContain('localhost');
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
expect(await mainFrame.evaluate(() => window.location.href)).toContain('127');
|
||||
});
|
||||
};
|
||||
it('should not allow cross-frame js handles', async({page, server}) => {
|
||||
// TODO: this should actually be possible because frames script each other,
|
||||
// but protocol implementations do not support this. For now, assume current
|
||||
// behavior.
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
const handle = await page.evaluateHandle(() => {
|
||||
const iframe = document.querySelector('iframe');
|
||||
const foo = { bar: 'baz' };
|
||||
iframe.contentWindow.__foo = foo;
|
||||
return foo;
|
||||
});
|
||||
const childFrame = page.mainFrame().childFrames()[0];
|
||||
const childResult = await childFrame.evaluate(() => window.__foo);
|
||||
expect(childResult).toEqual({ bar: 'baz' });
|
||||
const error = await childFrame.evaluate(foo => foo.bar, handle).catch(e => e);
|
||||
expect(error.message).toBe('JSHandles can be evaluated only in the context they were created!');
|
||||
});
|
||||
it('should allow cross-frame element handles', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
const bodyHandle = await page.mainFrame().childFrames()[0].$('body');
|
||||
const result = await page.evaluate(body => body.innerHTML, bodyHandle);
|
||||
expect(result.trim()).toBe('<div>Hi, I\'m frame</div>');
|
||||
});
|
||||
it('should not allow cross-frame element handles when frames do not script each other', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const bodyHandle = await frame.$('body');
|
||||
const error = await page.evaluate(body => body.innerHTML, bodyHandle).catch(e => e);
|
||||
expect(error.message).toContain('Unable to adopt element handle from a different document');
|
||||
});
|
||||
it.fail(FFOX)('should return non-empty Node.constructor.name in utility context', async({page,server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
const context = await frame._utilityContext();
|
||||
const elementHandle = await context.evaluateHandleInternal(() => window.top.document.querySelector('#frame1'));
|
||||
const constructorName = await context.evaluateInternal(node => node.constructor.name, elementHandle);
|
||||
expect(constructorName).toBe('HTMLIFrameElement');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,133 +17,128 @@
|
|||
|
||||
const path = require('path');
|
||||
const {spawn, execSync} = require('child_process');
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN, playwrightPath, defaultBrowserOptions} = require('./utils').testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {TestSuite}
|
||||
*/
|
||||
module.exports.describe = function({browserType, playwrightPath, defaultBrowserOptions, WIN, FFOX, CHROMIUM, WEBKIT}) {
|
||||
async function testSignal(browserType, action, exitOnClose) {
|
||||
const options = Object.assign({}, defaultBrowserOptions, {
|
||||
// Disable DUMPIO to cleanly read stdout.
|
||||
dumpio: false,
|
||||
handleSIGINT: true,
|
||||
handleSIGTERM: true,
|
||||
handleSIGHUP: true,
|
||||
});
|
||||
const res = spawn('node', [path.join(__dirname, 'fixtures', 'closeme.js'), playwrightPath, browserType.name(), JSON.stringify(options), exitOnClose ? 'true' : '']);
|
||||
let wsEndPointCallback;
|
||||
const wsEndPointPromise = new Promise(x => wsEndPointCallback = x);
|
||||
let output = '';
|
||||
let browserExitCode = 'none';
|
||||
let browserSignal = 'none';
|
||||
let browserPid;
|
||||
res.stdout.on('data', data => {
|
||||
output += data.toString();
|
||||
// Uncomment to debug these tests.
|
||||
// console.log(data.toString());
|
||||
let match = output.match(/browserWS:(.+):browserWS/);
|
||||
if (match)
|
||||
wsEndPointCallback(match[1]);
|
||||
match = output.match(/browserClose:([^:]+):([^:]+):browserClose/);
|
||||
if (match) {
|
||||
browserExitCode = match[1];
|
||||
browserSignal = match[2];
|
||||
}
|
||||
match = output.match(/browserPid:([^:]+):browserPid/);
|
||||
if (match)
|
||||
browserPid = +match[1];
|
||||
});
|
||||
res.on('error', (...args) => console.log("ERROR", ...args));
|
||||
const browser = await browserType.connect({ wsEndpoint: await wsEndPointPromise });
|
||||
const promises = [
|
||||
new Promise(resolve => browser.once('disconnected', resolve)),
|
||||
new Promise(resolve => res.on('exit', resolve)),
|
||||
];
|
||||
action(res, browserPid);
|
||||
const [, exitCode] = await Promise.all(promises);
|
||||
return { exitCode, browserSignal, browserExitCode, output };
|
||||
}
|
||||
|
||||
async function testSignal(action, exitOnClose) {
|
||||
const options = Object.assign({}, defaultBrowserOptions, {
|
||||
// Disable DUMPIO to cleanly read stdout.
|
||||
dumpio: false,
|
||||
handleSIGINT: true,
|
||||
handleSIGTERM: true,
|
||||
handleSIGHUP: true,
|
||||
describe('Fixtures', function() {
|
||||
it.slow()('should dump browser process stderr', async({browserType}) => {
|
||||
let dumpioData = '';
|
||||
const res = spawn('node', [path.join(__dirname, 'fixtures', 'dumpio.js'), playwrightPath, browserType.name()]);
|
||||
res.stdout.on('data', data => dumpioData += data.toString('utf8'));
|
||||
await new Promise(resolve => res.on('close', resolve));
|
||||
expect(dumpioData).toContain('message from dumpio');
|
||||
});
|
||||
it.slow()('should close the browser when the node process closes', async ({browserType}) => {
|
||||
const result = await testSignal(browserType, child => {
|
||||
if (WIN)
|
||||
execSync(`taskkill /pid ${child.pid} /T /F`);
|
||||
else
|
||||
process.kill(child.pid);
|
||||
});
|
||||
const res = spawn('node', [path.join(__dirname, 'fixtures', 'closeme.js'), playwrightPath, browserType.name(), JSON.stringify(options), exitOnClose ? 'true' : '']);
|
||||
let wsEndPointCallback;
|
||||
const wsEndPointPromise = new Promise(x => wsEndPointCallback = x);
|
||||
let output = '';
|
||||
let browserExitCode = 'none';
|
||||
let browserSignal = 'none';
|
||||
let browserPid;
|
||||
res.stdout.on('data', data => {
|
||||
output += data.toString();
|
||||
// Uncomment to debug these tests.
|
||||
// console.log(data.toString());
|
||||
let match = output.match(/browserWS:(.+):browserWS/);
|
||||
if (match)
|
||||
wsEndPointCallback(match[1]);
|
||||
match = output.match(/browserClose:([^:]+):([^:]+):browserClose/);
|
||||
if (match) {
|
||||
browserExitCode = match[1];
|
||||
browserSignal = match[2];
|
||||
}
|
||||
match = output.match(/browserPid:([^:]+):browserPid/);
|
||||
if (match)
|
||||
browserPid = +match[1];
|
||||
});
|
||||
res.on('error', (...args) => console.log("ERROR", ...args));
|
||||
const browser = await browserType.connect({ wsEndpoint: await wsEndPointPromise });
|
||||
const promises = [
|
||||
new Promise(resolve => browser.once('disconnected', resolve)),
|
||||
new Promise(resolve => res.on('exit', resolve)),
|
||||
];
|
||||
action(res, browserPid);
|
||||
const [, exitCode] = await Promise.all(promises);
|
||||
return { exitCode, browserSignal, browserExitCode, output };
|
||||
}
|
||||
expect(result.exitCode).toBe(WIN ? 1 : 0);
|
||||
// We might not get browser exitCode in time when killing the parent node process,
|
||||
// so we don't check it here.
|
||||
});
|
||||
|
||||
describe('Fixtures', function() {
|
||||
it.slow()('should dump browser process stderr', async({server}) => {
|
||||
let dumpioData = '';
|
||||
const res = spawn('node', [path.join(__dirname, 'fixtures', 'dumpio.js'), playwrightPath, browserType.name()]);
|
||||
res.stdout.on('data', data => dumpioData += data.toString('utf8'));
|
||||
await new Promise(resolve => res.on('close', resolve));
|
||||
expect(dumpioData).toContain('message from dumpio');
|
||||
describe.skip(WIN)('signals', () => {
|
||||
// Cannot reliably send signals on Windows.
|
||||
it.slow()('should report browser close signal', async ({browserType}) => {
|
||||
const result = await testSignal(browserType, (child, browserPid) => process.kill(browserPid), true);
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.browserExitCode).toBe('null');
|
||||
expect(result.browserSignal).toBe('SIGTERM');
|
||||
});
|
||||
it.slow()('should close the browser when the node process closes', async () => {
|
||||
const result = await testSignal(child => {
|
||||
if (WIN)
|
||||
execSync(`taskkill /pid ${child.pid} /T /F`);
|
||||
else
|
||||
process.kill(child.pid);
|
||||
});
|
||||
expect(result.exitCode).toBe(WIN ? 1 : 0);
|
||||
// We might not get browser exitCode in time when killing the parent node process,
|
||||
// so we don't check it here.
|
||||
it.slow()('should report browser close signal 2', async ({browserType}) => {
|
||||
const result = await testSignal(browserType, (child, browserPid) => process.kill(browserPid, 'SIGKILL'), true);
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.browserExitCode).toBe('null');
|
||||
expect(result.browserSignal).toBe('SIGKILL');
|
||||
});
|
||||
|
||||
describe.skip(WIN)('signals', () => {
|
||||
// Cannot reliably send signals on Windows.
|
||||
it.slow()('should report browser close signal', async () => {
|
||||
const result = await testSignal((child, browserPid) => process.kill(browserPid), true);
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.browserExitCode).toBe('null');
|
||||
expect(result.browserSignal).toBe('SIGTERM');
|
||||
it.slow()('should close the browser on SIGINT', async ({browserType}) => {
|
||||
const result = await testSignal(browserType, child => process.kill(child.pid, 'SIGINT'));
|
||||
expect(result.exitCode).toBe(130);
|
||||
expect(result.browserExitCode).toBe('0');
|
||||
expect(result.browserSignal).toBe('null');
|
||||
});
|
||||
it.slow()('should close the browser on SIGTERM', async ({browserType}) => {
|
||||
const result = await testSignal(browserType, child => process.kill(child.pid, 'SIGTERM'));
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.browserExitCode).toBe('0');
|
||||
expect(result.browserSignal).toBe('null');
|
||||
});
|
||||
it.slow()('should close the browser on SIGHUP', async ({browserType}) => {
|
||||
const result = await testSignal(browserType, child => process.kill(child.pid, 'SIGHUP'));
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.browserExitCode).toBe('0');
|
||||
expect(result.browserSignal).toBe('null');
|
||||
});
|
||||
it.slow()('should kill the browser on double SIGINT', async ({browserType}) => {
|
||||
const result = await testSignal(browserType, child => {
|
||||
process.kill(child.pid, 'SIGINT');
|
||||
process.kill(child.pid, 'SIGINT');
|
||||
});
|
||||
it.slow()('should report browser close signal 2', async (state, test) => {
|
||||
const result = await testSignal((child, browserPid) => process.kill(browserPid, 'SIGKILL'), true);
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.browserExitCode).toBe('null');
|
||||
expect(result.browserSignal).toBe('SIGKILL');
|
||||
expect(result.exitCode).toBe(130);
|
||||
// TODO: ideally, we would expect the SIGKILL on the browser from
|
||||
// force kill, but that's racy with sending two signals.
|
||||
});
|
||||
it.slow()('should kill the browser on SIGINT + SIGTERM', async ({browserType}) => {
|
||||
const result = await testSignal(browserType, child => {
|
||||
process.kill(child.pid, 'SIGINT');
|
||||
process.kill(child.pid, 'SIGTERM');
|
||||
});
|
||||
it.slow()('should close the browser on SIGINT', async () => {
|
||||
const result = await testSignal(child => process.kill(child.pid, 'SIGINT'));
|
||||
expect(result.exitCode).toBe(130);
|
||||
expect(result.browserExitCode).toBe('0');
|
||||
expect(result.browserSignal).toBe('null');
|
||||
});
|
||||
it.slow()('should close the browser on SIGTERM', async () => {
|
||||
const result = await testSignal(child => process.kill(child.pid, 'SIGTERM'));
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.browserExitCode).toBe('0');
|
||||
expect(result.browserSignal).toBe('null');
|
||||
});
|
||||
it.slow()('should close the browser on SIGHUP', async () => {
|
||||
const result = await testSignal(child => process.kill(child.pid, 'SIGHUP'));
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.browserExitCode).toBe('0');
|
||||
expect(result.browserSignal).toBe('null');
|
||||
});
|
||||
it.slow()('should kill the browser on double SIGINT', async () => {
|
||||
const result = await testSignal(child => {
|
||||
process.kill(child.pid, 'SIGINT');
|
||||
process.kill(child.pid, 'SIGINT');
|
||||
});
|
||||
expect(result.exitCode).toBe(130);
|
||||
// TODO: ideally, we would expect the SIGKILL on the browser from
|
||||
// force kill, but that's racy with sending two signals.
|
||||
});
|
||||
it.slow()('should kill the browser on SIGINT + SIGTERM', async () => {
|
||||
const result = await testSignal(child => {
|
||||
process.kill(child.pid, 'SIGINT');
|
||||
process.kill(child.pid, 'SIGTERM');
|
||||
});
|
||||
expect(result.exitCode).toBe(130);
|
||||
// TODO: ideally, we would expect the SIGKILL on the browser from
|
||||
// force kill, but that's racy with sending two signals.
|
||||
});
|
||||
it.slow()('should kill the browser on SIGTERM + SIGINT', async () => {
|
||||
const result = await testSignal(child => {
|
||||
process.kill(child.pid, 'SIGTERM');
|
||||
process.kill(child.pid, 'SIGINT');
|
||||
});
|
||||
expect(result.exitCode).toBe(130);
|
||||
// TODO: ideally, we would expect the SIGKILL on the browser from
|
||||
// force kill, but that's racy with sending two signals.
|
||||
expect(result.exitCode).toBe(130);
|
||||
// TODO: ideally, we would expect the SIGKILL on the browser from
|
||||
// force kill, but that's racy with sending two signals.
|
||||
});
|
||||
it.slow()('should kill the browser on SIGTERM + SIGINT', async ({browserType}) => {
|
||||
const result = await testSignal(browserType, child => {
|
||||
process.kill(child.pid, 'SIGTERM');
|
||||
process.kill(child.pid, 'SIGINT');
|
||||
});
|
||||
expect(result.exitCode).toBe(130);
|
||||
// TODO: ideally, we would expect the SIGKILL on the browser from
|
||||
// force kill, but that's racy with sending two signals.
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,38 +14,34 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
|
||||
describe('Page.focus', function() {
|
||||
it('should work', async function({page, server}) {
|
||||
await page.setContent(`<div id=d1 tabIndex=0></div>`);
|
||||
expect(await page.evaluate(() => document.activeElement.nodeName)).toBe('BODY');
|
||||
await page.focus('#d1');
|
||||
expect(await page.evaluate(() => document.activeElement.id)).toBe('d1');
|
||||
});
|
||||
it('should emit focus event', async function({page, server}) {
|
||||
await page.setContent(`<div id=d1 tabIndex=0></div>`);
|
||||
let focused = false;
|
||||
await page.exposeFunction('focusEvent', () => focused = true);
|
||||
await page.evaluate(() => d1.addEventListener('focus', focusEvent));
|
||||
await page.focus('#d1');
|
||||
expect(focused).toBe(true);
|
||||
});
|
||||
it('should emit blur event', async function({page, server}) {
|
||||
await page.setContent(`<div id=d1 tabIndex=0>DIV1</div><div id=d2 tabIndex=0>DIV2</div>`);
|
||||
await page.focus('#d1');
|
||||
let focused = false;
|
||||
let blurred = false;
|
||||
await page.exposeFunction('focusEvent', () => focused = true);
|
||||
await page.exposeFunction('blurEvent', () => blurred = true);
|
||||
await page.evaluate(() => d1.addEventListener('blur', blurEvent));
|
||||
await page.evaluate(() => d2.addEventListener('focus', focusEvent));
|
||||
await page.focus('#d2');
|
||||
expect(focused).toBe(true);
|
||||
expect(blurred).toBe(true);
|
||||
});
|
||||
describe('Page.focus', function() {
|
||||
it('should work', async function({page, server}) {
|
||||
await page.setContent(`<div id=d1 tabIndex=0></div>`);
|
||||
expect(await page.evaluate(() => document.activeElement.nodeName)).toBe('BODY');
|
||||
await page.focus('#d1');
|
||||
expect(await page.evaluate(() => document.activeElement.id)).toBe('d1');
|
||||
});
|
||||
};
|
||||
it('should emit focus event', async function({page, server}) {
|
||||
await page.setContent(`<div id=d1 tabIndex=0></div>`);
|
||||
let focused = false;
|
||||
await page.exposeFunction('focusEvent', () => focused = true);
|
||||
await page.evaluate(() => d1.addEventListener('focus', focusEvent));
|
||||
await page.focus('#d1');
|
||||
expect(focused).toBe(true);
|
||||
});
|
||||
it('should emit blur event', async function({page, server}) {
|
||||
await page.setContent(`<div id=d1 tabIndex=0>DIV1</div><div id=d2 tabIndex=0>DIV2</div>`);
|
||||
await page.focus('#d1');
|
||||
let focused = false;
|
||||
let blurred = false;
|
||||
await page.exposeFunction('focusEvent', () => focused = true);
|
||||
await page.exposeFunction('blurEvent', () => blurred = true);
|
||||
await page.evaluate(() => d1.addEventListener('blur', blurEvent));
|
||||
await page.evaluate(() => d2.addEventListener('focus', focusEvent));
|
||||
await page.focus('#d2');
|
||||
expect(focused).toBe(true);
|
||||
expect(blurred).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,222 +16,217 @@
|
|||
*/
|
||||
|
||||
const utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT} = utils.testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({FFOX, CHROMIUM, WEBKIT}) {
|
||||
|
||||
describe('Frame.evaluateHandle', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const mainFrame = page.mainFrame();
|
||||
const windowHandle = await mainFrame.evaluateHandle(() => window);
|
||||
expect(windowHandle).toBeTruthy();
|
||||
});
|
||||
describe('Frame.evaluateHandle', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const mainFrame = page.mainFrame();
|
||||
const windowHandle = await mainFrame.evaluateHandle(() => window);
|
||||
expect(windowHandle).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Frame.frameElement', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame2 = await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
const frame3 = await utils.attachFrame(page, 'frame3', server.EMPTY_PAGE);
|
||||
const frame1handle1 = await page.$('#frame1');
|
||||
const frame1handle2 = await frame1.frameElement();
|
||||
const frame3handle1 = await page.$('#frame3');
|
||||
const frame3handle2 = await frame3.frameElement();
|
||||
expect(await frame1handle1.evaluate((a, b) => a === b, frame1handle2)).toBe(true);
|
||||
expect(await frame3handle1.evaluate((a, b) => a === b, frame3handle2)).toBe(true);
|
||||
expect(await frame1handle1.evaluate((a, b) => a === b, frame3handle1)).toBe(false);
|
||||
});
|
||||
it('should work with contentFrame', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const handle = await frame.frameElement();
|
||||
const contentFrame = await handle.contentFrame();
|
||||
expect(contentFrame).toBe(frame);
|
||||
});
|
||||
it('should throw when detached', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await page.$eval('#frame1', e => e.remove());
|
||||
const error = await frame1.frameElement().catch(e => e);
|
||||
expect(error.message).toBe('Frame has been detached.');
|
||||
});
|
||||
describe('Frame.frameElement', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame2 = await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
const frame3 = await utils.attachFrame(page, 'frame3', server.EMPTY_PAGE);
|
||||
const frame1handle1 = await page.$('#frame1');
|
||||
const frame1handle2 = await frame1.frameElement();
|
||||
const frame3handle1 = await page.$('#frame3');
|
||||
const frame3handle2 = await frame3.frameElement();
|
||||
expect(await frame1handle1.evaluate((a, b) => a === b, frame1handle2)).toBe(true);
|
||||
expect(await frame3handle1.evaluate((a, b) => a === b, frame3handle2)).toBe(true);
|
||||
expect(await frame1handle1.evaluate((a, b) => a === b, frame3handle1)).toBe(false);
|
||||
});
|
||||
|
||||
describe('Frame.evaluate', function() {
|
||||
it('should throw for detached frames', async({page, server}) => {
|
||||
const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await utils.detachFrame(page, 'frame1');
|
||||
let error = null;
|
||||
await frame1.evaluate(() => 7 * 8).catch(e => error = e);
|
||||
expect(error.message).toContain('Execution Context is not available in detached frame');
|
||||
});
|
||||
it('should be isolated between frames', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
expect(page.frames().length).toBe(2);
|
||||
const [frame1, frame2] = page.frames();
|
||||
expect(frame1 !== frame2).toBeTruthy();
|
||||
|
||||
await Promise.all([
|
||||
frame1.evaluate(() => window.a = 1),
|
||||
frame2.evaluate(() => window.a = 2)
|
||||
]);
|
||||
const [a1, a2] = await Promise.all([
|
||||
frame1.evaluate(() => window.a),
|
||||
frame2.evaluate(() => window.a)
|
||||
]);
|
||||
expect(a1).toBe(1);
|
||||
expect(a2).toBe(2);
|
||||
});
|
||||
it('should work with contentFrame', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const handle = await frame.frameElement();
|
||||
const contentFrame = await handle.contentFrame();
|
||||
expect(contentFrame).toBe(frame);
|
||||
});
|
||||
|
||||
describe('Frame Management', function() {
|
||||
it('should handle nested frames', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/frames/nested-frames.html');
|
||||
expect(utils.dumpFrames(page.mainFrame())).toEqual([
|
||||
'http://localhost:<PORT>/frames/nested-frames.html',
|
||||
' http://localhost:<PORT>/frames/frame.html (aframe)',
|
||||
' http://localhost:<PORT>/frames/two-frames.html (2frames)',
|
||||
' http://localhost:<PORT>/frames/frame.html (dos)',
|
||||
' http://localhost:<PORT>/frames/frame.html (uno)',
|
||||
]);
|
||||
});
|
||||
it('should send events when frames are manipulated dynamically', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
// validate frameattached events
|
||||
const attachedFrames = [];
|
||||
page.on('frameattached', frame => attachedFrames.push(frame));
|
||||
await utils.attachFrame(page, 'frame1', './assets/frame.html');
|
||||
expect(attachedFrames.length).toBe(1);
|
||||
expect(attachedFrames[0].url()).toContain('/assets/frame.html');
|
||||
|
||||
// validate framenavigated events
|
||||
const navigatedFrames = [];
|
||||
page.on('framenavigated', frame => navigatedFrames.push(frame));
|
||||
await page.evaluate(() => {
|
||||
const frame = document.getElementById('frame1');
|
||||
frame.src = './empty.html';
|
||||
return new Promise(x => frame.onload = x);
|
||||
});
|
||||
expect(navigatedFrames.length).toBe(1);
|
||||
expect(navigatedFrames[0].url()).toBe(server.EMPTY_PAGE);
|
||||
|
||||
// validate framedetached events
|
||||
const detachedFrames = [];
|
||||
page.on('framedetached', frame => detachedFrames.push(frame));
|
||||
await utils.detachFrame(page, 'frame1');
|
||||
expect(detachedFrames.length).toBe(1);
|
||||
expect(detachedFrames[0].isDetached()).toBe(true);
|
||||
});
|
||||
it('should send "framenavigated" when navigating on anchor URLs', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await Promise.all([
|
||||
page.goto(server.EMPTY_PAGE + '#foo'),
|
||||
page.waitForEvent('framenavigated')
|
||||
]);
|
||||
expect(page.url()).toBe(server.EMPTY_PAGE + '#foo');
|
||||
});
|
||||
it('should persist mainFrame on cross-process navigation', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const mainFrame = page.mainFrame();
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
expect(page.mainFrame() === mainFrame).toBeTruthy();
|
||||
});
|
||||
it('should not send attach/detach events for main frame', async({page, server}) => {
|
||||
let hasEvents = false;
|
||||
page.on('frameattached', frame => hasEvents = true);
|
||||
page.on('framedetached', frame => hasEvents = true);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(hasEvents).toBe(false);
|
||||
});
|
||||
it('should detach child frames on navigation', async({page, server}) => {
|
||||
let attachedFrames = [];
|
||||
let detachedFrames = [];
|
||||
let navigatedFrames = [];
|
||||
page.on('frameattached', frame => attachedFrames.push(frame));
|
||||
page.on('framedetached', frame => detachedFrames.push(frame));
|
||||
page.on('framenavigated', frame => navigatedFrames.push(frame));
|
||||
await page.goto(server.PREFIX + '/frames/nested-frames.html');
|
||||
expect(attachedFrames.length).toBe(4);
|
||||
expect(detachedFrames.length).toBe(0);
|
||||
expect(navigatedFrames.length).toBe(5);
|
||||
|
||||
attachedFrames = [];
|
||||
detachedFrames = [];
|
||||
navigatedFrames = [];
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(attachedFrames.length).toBe(0);
|
||||
expect(detachedFrames.length).toBe(4);
|
||||
expect(navigatedFrames.length).toBe(1);
|
||||
});
|
||||
it('should support framesets', async({page, server}) => {
|
||||
let attachedFrames = [];
|
||||
let detachedFrames = [];
|
||||
let navigatedFrames = [];
|
||||
page.on('frameattached', frame => attachedFrames.push(frame));
|
||||
page.on('framedetached', frame => detachedFrames.push(frame));
|
||||
page.on('framenavigated', frame => navigatedFrames.push(frame));
|
||||
await page.goto(server.PREFIX + '/frames/frameset.html');
|
||||
expect(attachedFrames.length).toBe(4);
|
||||
expect(detachedFrames.length).toBe(0);
|
||||
expect(navigatedFrames.length).toBe(5);
|
||||
|
||||
attachedFrames = [];
|
||||
detachedFrames = [];
|
||||
navigatedFrames = [];
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(attachedFrames.length).toBe(0);
|
||||
expect(detachedFrames.length).toBe(4);
|
||||
expect(navigatedFrames.length).toBe(1);
|
||||
});
|
||||
it('should report frame from-inside shadow DOM', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/shadow.html');
|
||||
await page.evaluate(async url => {
|
||||
const frame = document.createElement('iframe');
|
||||
frame.src = url;
|
||||
document.body.shadowRoot.appendChild(frame);
|
||||
await new Promise(x => frame.onload = x);
|
||||
}, server.EMPTY_PAGE);
|
||||
expect(page.frames().length).toBe(2);
|
||||
expect(page.frames()[1].url()).toBe(server.EMPTY_PAGE);
|
||||
});
|
||||
it('should report frame.name()', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'theFrameId', server.EMPTY_PAGE);
|
||||
await page.evaluate(url => {
|
||||
const frame = document.createElement('iframe');
|
||||
frame.name = 'theFrameName';
|
||||
frame.src = url;
|
||||
document.body.appendChild(frame);
|
||||
return new Promise(x => frame.onload = x);
|
||||
}, server.EMPTY_PAGE);
|
||||
expect(page.frames()[0].name()).toBe('');
|
||||
expect(page.frames()[1].name()).toBe('theFrameId');
|
||||
expect(page.frames()[2].name()).toBe('theFrameName');
|
||||
});
|
||||
it('should report frame.parent()', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
expect(page.frames()[0].parentFrame()).toBe(null);
|
||||
expect(page.frames()[1].parentFrame()).toBe(page.mainFrame());
|
||||
expect(page.frames()[2].parentFrame()).toBe(page.mainFrame());
|
||||
});
|
||||
it('should report different frame instance when frame re-attaches', async({page, server}) => {
|
||||
const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
window.frame = document.querySelector('#frame1');
|
||||
window.frame.remove();
|
||||
});
|
||||
expect(frame1.isDetached()).toBe(true);
|
||||
const [frame2] = await Promise.all([
|
||||
page.waitForEvent('frameattached'),
|
||||
page.evaluate(() => document.body.appendChild(window.frame)),
|
||||
]);
|
||||
expect(frame2.isDetached()).toBe(false);
|
||||
expect(frame1).not.toBe(frame2);
|
||||
});
|
||||
it('should throw when detached', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await page.$eval('#frame1', e => e.remove());
|
||||
const error = await frame1.frameElement().catch(e => e);
|
||||
expect(error.message).toBe('Frame has been detached.');
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
describe('Frame.evaluate', function() {
|
||||
it('should throw for detached frames', async({page, server}) => {
|
||||
const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await utils.detachFrame(page, 'frame1');
|
||||
let error = null;
|
||||
await frame1.evaluate(() => 7 * 8).catch(e => error = e);
|
||||
expect(error.message).toContain('Execution Context is not available in detached frame');
|
||||
});
|
||||
it('should be isolated between frames', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
expect(page.frames().length).toBe(2);
|
||||
const [frame1, frame2] = page.frames();
|
||||
expect(frame1 !== frame2).toBeTruthy();
|
||||
|
||||
await Promise.all([
|
||||
frame1.evaluate(() => window.a = 1),
|
||||
frame2.evaluate(() => window.a = 2)
|
||||
]);
|
||||
const [a1, a2] = await Promise.all([
|
||||
frame1.evaluate(() => window.a),
|
||||
frame2.evaluate(() => window.a)
|
||||
]);
|
||||
expect(a1).toBe(1);
|
||||
expect(a2).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Frame Management', function() {
|
||||
it('should handle nested frames', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/frames/nested-frames.html');
|
||||
expect(utils.dumpFrames(page.mainFrame())).toEqual([
|
||||
'http://localhost:<PORT>/frames/nested-frames.html',
|
||||
' http://localhost:<PORT>/frames/frame.html (aframe)',
|
||||
' http://localhost:<PORT>/frames/two-frames.html (2frames)',
|
||||
' http://localhost:<PORT>/frames/frame.html (dos)',
|
||||
' http://localhost:<PORT>/frames/frame.html (uno)',
|
||||
]);
|
||||
});
|
||||
it('should send events when frames are manipulated dynamically', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
// validate frameattached events
|
||||
const attachedFrames = [];
|
||||
page.on('frameattached', frame => attachedFrames.push(frame));
|
||||
await utils.attachFrame(page, 'frame1', './assets/frame.html');
|
||||
expect(attachedFrames.length).toBe(1);
|
||||
expect(attachedFrames[0].url()).toContain('/assets/frame.html');
|
||||
|
||||
// validate framenavigated events
|
||||
const navigatedFrames = [];
|
||||
page.on('framenavigated', frame => navigatedFrames.push(frame));
|
||||
await page.evaluate(() => {
|
||||
const frame = document.getElementById('frame1');
|
||||
frame.src = './empty.html';
|
||||
return new Promise(x => frame.onload = x);
|
||||
});
|
||||
expect(navigatedFrames.length).toBe(1);
|
||||
expect(navigatedFrames[0].url()).toBe(server.EMPTY_PAGE);
|
||||
|
||||
// validate framedetached events
|
||||
const detachedFrames = [];
|
||||
page.on('framedetached', frame => detachedFrames.push(frame));
|
||||
await utils.detachFrame(page, 'frame1');
|
||||
expect(detachedFrames.length).toBe(1);
|
||||
expect(detachedFrames[0].isDetached()).toBe(true);
|
||||
});
|
||||
it('should send "framenavigated" when navigating on anchor URLs', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await Promise.all([
|
||||
page.goto(server.EMPTY_PAGE + '#foo'),
|
||||
page.waitForEvent('framenavigated')
|
||||
]);
|
||||
expect(page.url()).toBe(server.EMPTY_PAGE + '#foo');
|
||||
});
|
||||
it('should persist mainFrame on cross-process navigation', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const mainFrame = page.mainFrame();
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
expect(page.mainFrame() === mainFrame).toBeTruthy();
|
||||
});
|
||||
it('should not send attach/detach events for main frame', async({page, server}) => {
|
||||
let hasEvents = false;
|
||||
page.on('frameattached', frame => hasEvents = true);
|
||||
page.on('framedetached', frame => hasEvents = true);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(hasEvents).toBe(false);
|
||||
});
|
||||
it('should detach child frames on navigation', async({page, server}) => {
|
||||
let attachedFrames = [];
|
||||
let detachedFrames = [];
|
||||
let navigatedFrames = [];
|
||||
page.on('frameattached', frame => attachedFrames.push(frame));
|
||||
page.on('framedetached', frame => detachedFrames.push(frame));
|
||||
page.on('framenavigated', frame => navigatedFrames.push(frame));
|
||||
await page.goto(server.PREFIX + '/frames/nested-frames.html');
|
||||
expect(attachedFrames.length).toBe(4);
|
||||
expect(detachedFrames.length).toBe(0);
|
||||
expect(navigatedFrames.length).toBe(5);
|
||||
|
||||
attachedFrames = [];
|
||||
detachedFrames = [];
|
||||
navigatedFrames = [];
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(attachedFrames.length).toBe(0);
|
||||
expect(detachedFrames.length).toBe(4);
|
||||
expect(navigatedFrames.length).toBe(1);
|
||||
});
|
||||
it('should support framesets', async({page, server}) => {
|
||||
let attachedFrames = [];
|
||||
let detachedFrames = [];
|
||||
let navigatedFrames = [];
|
||||
page.on('frameattached', frame => attachedFrames.push(frame));
|
||||
page.on('framedetached', frame => detachedFrames.push(frame));
|
||||
page.on('framenavigated', frame => navigatedFrames.push(frame));
|
||||
await page.goto(server.PREFIX + '/frames/frameset.html');
|
||||
expect(attachedFrames.length).toBe(4);
|
||||
expect(detachedFrames.length).toBe(0);
|
||||
expect(navigatedFrames.length).toBe(5);
|
||||
|
||||
attachedFrames = [];
|
||||
detachedFrames = [];
|
||||
navigatedFrames = [];
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(attachedFrames.length).toBe(0);
|
||||
expect(detachedFrames.length).toBe(4);
|
||||
expect(navigatedFrames.length).toBe(1);
|
||||
});
|
||||
it('should report frame from-inside shadow DOM', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/shadow.html');
|
||||
await page.evaluate(async url => {
|
||||
const frame = document.createElement('iframe');
|
||||
frame.src = url;
|
||||
document.body.shadowRoot.appendChild(frame);
|
||||
await new Promise(x => frame.onload = x);
|
||||
}, server.EMPTY_PAGE);
|
||||
expect(page.frames().length).toBe(2);
|
||||
expect(page.frames()[1].url()).toBe(server.EMPTY_PAGE);
|
||||
});
|
||||
it('should report frame.name()', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'theFrameId', server.EMPTY_PAGE);
|
||||
await page.evaluate(url => {
|
||||
const frame = document.createElement('iframe');
|
||||
frame.name = 'theFrameName';
|
||||
frame.src = url;
|
||||
document.body.appendChild(frame);
|
||||
return new Promise(x => frame.onload = x);
|
||||
}, server.EMPTY_PAGE);
|
||||
expect(page.frames()[0].name()).toBe('');
|
||||
expect(page.frames()[1].name()).toBe('theFrameId');
|
||||
expect(page.frames()[2].name()).toBe('theFrameName');
|
||||
});
|
||||
it('should report frame.parent()', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
expect(page.frames()[0].parentFrame()).toBe(null);
|
||||
expect(page.frames()[1].parentFrame()).toBe(page.mainFrame());
|
||||
expect(page.frames()[2].parentFrame()).toBe(page.mainFrame());
|
||||
});
|
||||
it('should report different frame instance when frame re-attaches', async({page, server}) => {
|
||||
const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
window.frame = document.querySelector('#frame1');
|
||||
window.frame.remove();
|
||||
});
|
||||
expect(frame1.isDetached()).toBe(true);
|
||||
const [frame2] = await Promise.all([
|
||||
page.waitForEvent('frameattached'),
|
||||
page.evaluate(() => document.body.appendChild(window.frame)),
|
||||
]);
|
||||
expect(frame2.isDetached()).toBe(false);
|
||||
expect(frame1).not.toBe(frame2);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,110 +15,106 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function ({ FFOX, WEBKIT }) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
|
||||
describe('Overrides.setGeolocation', function() {
|
||||
it('should work', async({page, server, context}) => {
|
||||
await context.grantPermissions(['geolocation']);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.setGeolocation({longitude: 10, latitude: 10});
|
||||
const geolocation = await page.evaluate(() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {
|
||||
resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});
|
||||
})));
|
||||
expect(geolocation).toEqual({
|
||||
latitude: 10,
|
||||
longitude: 10
|
||||
});
|
||||
});
|
||||
it('should throw when invalid longitude', async({context}) => {
|
||||
let error = null;
|
||||
try {
|
||||
await context.setGeolocation({longitude: 200, latitude: 10});
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toContain('Invalid longitude "200"');
|
||||
});
|
||||
it('should throw with missing latitude', async({context}) => {
|
||||
let error = null;
|
||||
try {
|
||||
await context.setGeolocation({longitude: 10});
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toContain('Invalid latitude "undefined"');
|
||||
});
|
||||
it('should not modify passed default options object', async({browser}) => {
|
||||
const geolocation = { longitude: 10, latitude: 10 };
|
||||
const options = { geolocation };
|
||||
const context = await browser.newContext(options);
|
||||
await context.setGeolocation({ longitude: 20, latitude: 20 });
|
||||
expect(options.geolocation).toBe(geolocation);
|
||||
await context.close();
|
||||
});
|
||||
it('should throw with missing longitude in default options', async({browser}) => {
|
||||
let error = null;
|
||||
try {
|
||||
const context = await browser.newContext({ geolocation: {latitude: 10} });
|
||||
await context.close();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toContain('Invalid longitude "undefined"');
|
||||
});
|
||||
it('should use context options', async({browser, server}) => {
|
||||
const options = { geolocation: { longitude: 10, latitude: 10 }, permissions: ['geolocation'] };
|
||||
const context = await browser.newContext(options);
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
||||
const geolocation = await page.evaluate(() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {
|
||||
resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});
|
||||
})));
|
||||
expect(geolocation).toEqual({
|
||||
latitude: 10,
|
||||
longitude: 10
|
||||
});
|
||||
await context.close();
|
||||
});
|
||||
it('watchPosition should be notified', async({page, server, context}) => {
|
||||
await context.grantPermissions(['geolocation']);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const messages = [];
|
||||
page.on('console', message => messages.push(message.text()));
|
||||
|
||||
await context.setGeolocation({latitude: 0, longitude: 0});
|
||||
await page.evaluate(() => {
|
||||
navigator.geolocation.watchPosition(pos => {
|
||||
const coords = pos.coords;
|
||||
console.log(`lat=${coords.latitude} lng=${coords.longitude}`);
|
||||
}, err => {});
|
||||
});
|
||||
await context.setGeolocation({latitude: 0, longitude: 10});
|
||||
await page.waitForEvent('console', message => message.text().includes('lat=0 lng=10'));
|
||||
await context.setGeolocation({latitude: 20, longitude: 30});
|
||||
await page.waitForEvent('console', message => message.text().includes('lat=20 lng=30'));
|
||||
await context.setGeolocation({latitude: 40, longitude: 50});
|
||||
await page.waitForEvent('console', message => message.text().includes('lat=40 lng=50'));
|
||||
|
||||
const allMessages = messages.join('|');
|
||||
expect(allMessages).toContain('lat=0 lng=10');
|
||||
expect(allMessages).toContain('lat=20 lng=30');
|
||||
expect(allMessages).toContain('lat=40 lng=50');
|
||||
});
|
||||
it('should use context options for popup', async({page, context, server}) => {
|
||||
await context.grantPermissions(['geolocation']);
|
||||
await context.setGeolocation({ longitude: 10, latitude: 10 });
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window._popup = window.open(url), server.PREFIX + '/geolocation.html'),
|
||||
]);
|
||||
await popup.waitForLoadState();
|
||||
const geolocation = await popup.evaluate(() => window.geolocationPromise);
|
||||
expect(geolocation).toEqual({ longitude: 10, latitude: 10 });
|
||||
describe('Overrides.setGeolocation', function() {
|
||||
it('should work', async({page, server, context}) => {
|
||||
await context.grantPermissions(['geolocation']);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.setGeolocation({longitude: 10, latitude: 10});
|
||||
const geolocation = await page.evaluate(() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {
|
||||
resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});
|
||||
})));
|
||||
expect(geolocation).toEqual({
|
||||
latitude: 10,
|
||||
longitude: 10
|
||||
});
|
||||
});
|
||||
};
|
||||
it('should throw when invalid longitude', async({context}) => {
|
||||
let error = null;
|
||||
try {
|
||||
await context.setGeolocation({longitude: 200, latitude: 10});
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toContain('Invalid longitude "200"');
|
||||
});
|
||||
it('should throw with missing latitude', async({context}) => {
|
||||
let error = null;
|
||||
try {
|
||||
await context.setGeolocation({longitude: 10});
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toContain('Invalid latitude "undefined"');
|
||||
});
|
||||
it('should not modify passed default options object', async({browser}) => {
|
||||
const geolocation = { longitude: 10, latitude: 10 };
|
||||
const options = { geolocation };
|
||||
const context = await browser.newContext(options);
|
||||
await context.setGeolocation({ longitude: 20, latitude: 20 });
|
||||
expect(options.geolocation).toBe(geolocation);
|
||||
await context.close();
|
||||
});
|
||||
it('should throw with missing longitude in default options', async({browser}) => {
|
||||
let error = null;
|
||||
try {
|
||||
const context = await browser.newContext({ geolocation: {latitude: 10} });
|
||||
await context.close();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toContain('Invalid longitude "undefined"');
|
||||
});
|
||||
it('should use context options', async({browser, server}) => {
|
||||
const options = { geolocation: { longitude: 10, latitude: 10 }, permissions: ['geolocation'] };
|
||||
const context = await browser.newContext(options);
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
||||
const geolocation = await page.evaluate(() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {
|
||||
resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});
|
||||
})));
|
||||
expect(geolocation).toEqual({
|
||||
latitude: 10,
|
||||
longitude: 10
|
||||
});
|
||||
await context.close();
|
||||
});
|
||||
it('watchPosition should be notified', async({page, server, context}) => {
|
||||
await context.grantPermissions(['geolocation']);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const messages = [];
|
||||
page.on('console', message => messages.push(message.text()));
|
||||
|
||||
await context.setGeolocation({latitude: 0, longitude: 0});
|
||||
await page.evaluate(() => {
|
||||
navigator.geolocation.watchPosition(pos => {
|
||||
const coords = pos.coords;
|
||||
console.log(`lat=${coords.latitude} lng=${coords.longitude}`);
|
||||
}, err => {});
|
||||
});
|
||||
await context.setGeolocation({latitude: 0, longitude: 10});
|
||||
await page.waitForEvent('console', message => message.text().includes('lat=0 lng=10'));
|
||||
await context.setGeolocation({latitude: 20, longitude: 30});
|
||||
await page.waitForEvent('console', message => message.text().includes('lat=20 lng=30'));
|
||||
await context.setGeolocation({latitude: 40, longitude: 50});
|
||||
await page.waitForEvent('console', message => message.text().includes('lat=40 lng=50'));
|
||||
|
||||
const allMessages = messages.join('|');
|
||||
expect(allMessages).toContain('lat=0 lng=10');
|
||||
expect(allMessages).toContain('lat=20 lng=30');
|
||||
expect(allMessages).toContain('lat=40 lng=50');
|
||||
});
|
||||
it('should use context options for popup', async({page, context, server}) => {
|
||||
await context.grantPermissions(['geolocation']);
|
||||
await context.setGeolocation({ longitude: 10, latitude: 10 });
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window._popup = window.open(url), server.PREFIX + '/geolocation.html'),
|
||||
]);
|
||||
await popup.waitForLoadState();
|
||||
const geolocation = await popup.evaluate(() => window.geolocationPromise);
|
||||
expect(geolocation).toEqual({ longitude: 10, latitude: 10 });
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,58 +14,54 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const { makeUserDataDir, removeUserDataDir } = require('./utils');
|
||||
const utils = require('./utils');
|
||||
const { makeUserDataDir, removeUserDataDir } = utils;
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN, defaultBrowserOptions} = utils.testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {TestSuite}
|
||||
*/
|
||||
module.exports.describe = function({browserType, defaultBrowserOptions, FFOX, CHROMIUM, WEBKIT, WIN}) {
|
||||
const headfulOptions = Object.assign({}, defaultBrowserOptions, {
|
||||
headless: false
|
||||
});
|
||||
const headlessOptions = Object.assign({}, defaultBrowserOptions, {
|
||||
headless: true
|
||||
});
|
||||
|
||||
const headfulOptions = Object.assign({}, defaultBrowserOptions, {
|
||||
headless: false
|
||||
describe('Headful', function() {
|
||||
it('should have default url when launching browser', async ({browserType}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, headfulOptions);
|
||||
const urls = browserContext.pages().map(page => page.url());
|
||||
expect(urls).toEqual(['about:blank']);
|
||||
await browserContext.close();
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
const headlessOptions = Object.assign({}, defaultBrowserOptions, {
|
||||
headless: true
|
||||
it.slow().fail(WIN && CHROMIUM)('headless should be able to read cookies written by headful', async({browserType, server}) => {
|
||||
// see https://github.com/microsoft/playwright/issues/717
|
||||
const userDataDir = await makeUserDataDir();
|
||||
// Write a cookie in headful chrome
|
||||
const headfulContext = await browserType.launchPersistentContext(userDataDir, headfulOptions);
|
||||
const headfulPage = await headfulContext.newPage();
|
||||
await headfulPage.goto(server.EMPTY_PAGE);
|
||||
await headfulPage.evaluate(() => document.cookie = 'foo=true; expires=Fri, 31 Dec 9999 23:59:59 GMT');
|
||||
await headfulContext.close();
|
||||
// Read the cookie from headless chrome
|
||||
const headlessContext = await browserType.launchPersistentContext(userDataDir, headlessOptions);
|
||||
const headlessPage = await headlessContext.newPage();
|
||||
await headlessPage.goto(server.EMPTY_PAGE);
|
||||
const cookie = await headlessPage.evaluate(() => document.cookie);
|
||||
await headlessContext.close();
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
expect(cookie).toBe('foo=true');
|
||||
});
|
||||
|
||||
describe('Headful', function() {
|
||||
it('should have default url when launching browser', async function() {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, headfulOptions);
|
||||
const urls = browserContext.pages().map(page => page.url());
|
||||
expect(urls).toEqual(['about:blank']);
|
||||
await browserContext.close();
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it.slow().fail(WIN && CHROMIUM)('headless should be able to read cookies written by headful', async({server}) => {
|
||||
// see https://github.com/microsoft/playwright/issues/717
|
||||
const userDataDir = await makeUserDataDir();
|
||||
// Write a cookie in headful chrome
|
||||
const headfulContext = await browserType.launchPersistentContext(userDataDir, headfulOptions);
|
||||
const headfulPage = await headfulContext.newPage();
|
||||
await headfulPage.goto(server.EMPTY_PAGE);
|
||||
await headfulPage.evaluate(() => document.cookie = 'foo=true; expires=Fri, 31 Dec 9999 23:59:59 GMT');
|
||||
await headfulContext.close();
|
||||
// Read the cookie from headless chrome
|
||||
const headlessContext = await browserType.launchPersistentContext(userDataDir, headlessOptions);
|
||||
const headlessPage = await headlessContext.newPage();
|
||||
await headlessPage.goto(server.EMPTY_PAGE);
|
||||
const cookie = await headlessPage.evaluate(() => document.cookie);
|
||||
await headlessContext.close();
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
expect(cookie).toBe('foo=true');
|
||||
});
|
||||
it.slow()('should close browser with beforeunload page', async({server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, headfulOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.PREFIX + '/beforeunload.html');
|
||||
// We have to interact with a page so that 'beforeunload' handlers
|
||||
// fire.
|
||||
await page.click('body');
|
||||
await browserContext.close();
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it.slow()('should close browser with beforeunload page', async({browserType, server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, headfulOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.PREFIX + '/beforeunload.html');
|
||||
// We have to interact with a page so that 'beforeunload' handlers
|
||||
// fire.
|
||||
await page.click('body');
|
||||
await browserContext.close();
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,13 +15,20 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {BrowserTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({defaultBrowserOptions, playwright, FFOX, CHROMIUM, WEBKIT}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
|
||||
describe('ignoreHTTPSErrors', function() {
|
||||
it('should work', async({browser, httpsServer}) => {
|
||||
describe('ignoreHTTPSErrors', function() {
|
||||
it('should work', async({browser, httpsServer}) => {
|
||||
let error = null;
|
||||
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e);
|
||||
expect(error).toBe(null);
|
||||
expect(response.ok()).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should isolate contexts', async({browser, httpsServer}) => {
|
||||
{
|
||||
let error = null;
|
||||
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
||||
const page = await context.newPage();
|
||||
|
|
@ -29,39 +36,28 @@ module.exports.describe = function({defaultBrowserOptions, playwright, FFOX, CHR
|
|||
expect(error).toBe(null);
|
||||
expect(response.ok()).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should isolate contexts', async({browser, httpsServer}) => {
|
||||
{
|
||||
let error = null;
|
||||
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e);
|
||||
expect(error).toBe(null);
|
||||
expect(response.ok()).toBe(true);
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
let error = null;
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e);
|
||||
expect(error).not.toBe(null);
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it('should work with mixed content', async({browser, server, httpsServer}) => {
|
||||
httpsServer.setRoute('/mixedcontent.html', (req, res) => {
|
||||
res.end(`<iframe src=${server.EMPTY_PAGE}></iframe>`);
|
||||
});
|
||||
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
||||
}
|
||||
{
|
||||
let error = null;
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(httpsServer.PREFIX + '/mixedcontent.html', {waitUntil: 'domcontentloaded'});
|
||||
expect(page.frames().length).toBe(2);
|
||||
// Make sure blocked iframe has functional execution context
|
||||
// @see https://github.com/GoogleChrome/puppeteer/issues/2709
|
||||
expect(await page.frames()[0].evaluate('1 + 2')).toBe(3);
|
||||
expect(await page.frames()[1].evaluate('2 + 3')).toBe(5);
|
||||
await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e);
|
||||
expect(error).not.toBe(null);
|
||||
await context.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
it('should work with mixed content', async({browser, server, httpsServer}) => {
|
||||
httpsServer.setRoute('/mixedcontent.html', (req, res) => {
|
||||
res.end(`<iframe src=${server.EMPTY_PAGE}></iframe>`);
|
||||
});
|
||||
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(httpsServer.PREFIX + '/mixedcontent.html', {waitUntil: 'domcontentloaded'});
|
||||
expect(page.frames().length).toBe(2);
|
||||
// Make sure blocked iframe has functional execution context
|
||||
// @see https://github.com/GoogleChrome/puppeteer/issues/2709
|
||||
expect(await page.frames()[0].evaluate('1 + 2')).toBe(3);
|
||||
expect(await page.frames()[1].evaluate('2 + 3')).toBe(5);
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,227 +20,222 @@ const fs = require('fs');
|
|||
const formidable = require('formidable');
|
||||
|
||||
const FILE_TO_UPLOAD = path.join(__dirname, '/assets/file-to-upload.txt');
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({playwright, FFOX, CHROMIUM, WEBKIT}) {
|
||||
describe('input', function() {
|
||||
it('should upload the file', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/fileupload.html');
|
||||
const filePath = path.relative(process.cwd(), FILE_TO_UPLOAD);
|
||||
const input = await page.$('input');
|
||||
await input.setInputFiles(filePath);
|
||||
expect(await page.evaluate(e => e.files[0].name, input)).toBe('file-to-upload.txt');
|
||||
expect(await page.evaluate(e => {
|
||||
const reader = new FileReader();
|
||||
const promise = new Promise(fulfill => reader.onload = fulfill);
|
||||
reader.readAsText(e.files[0]);
|
||||
return promise.then(() => reader.result);
|
||||
}, input)).toBe('contents of the file');
|
||||
});
|
||||
});
|
||||
|
||||
describe('input', function() {
|
||||
it('should upload the file', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/fileupload.html');
|
||||
const filePath = path.relative(process.cwd(), FILE_TO_UPLOAD);
|
||||
const input = await page.$('input');
|
||||
await input.setInputFiles(filePath);
|
||||
expect(await page.evaluate(e => e.files[0].name, input)).toBe('file-to-upload.txt');
|
||||
expect(await page.evaluate(e => {
|
||||
describe('Page.waitForFileChooser', function() {
|
||||
it('should emit event', async({page, server}) => {
|
||||
await page.setContent(`<input type=file>`);
|
||||
const [chooser] = await Promise.all([
|
||||
new Promise(f => page.once('filechooser', f)),
|
||||
page.click('input'),
|
||||
]);
|
||||
expect(chooser).toBeTruthy();
|
||||
});
|
||||
it('should work when file input is attached to DOM', async({page, server}) => {
|
||||
await page.setContent(`<input type=file>`);
|
||||
const [chooser] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.click('input'),
|
||||
]);
|
||||
expect(chooser).toBeTruthy();
|
||||
});
|
||||
it('should work when file input is not attached to DOM', async({page, server}) => {
|
||||
const [chooser] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.evaluate(() => {
|
||||
const el = document.createElement('input');
|
||||
el.type = 'file';
|
||||
el.click();
|
||||
}),
|
||||
]);
|
||||
expect(chooser).toBeTruthy();
|
||||
});
|
||||
it('should respect timeout', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.waitForEvent('filechooser', {timeout: 1}).catch(e => error = e);
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should respect default timeout when there is no custom timeout', async({page, server}) => {
|
||||
page.setDefaultTimeout(1);
|
||||
let error = null;
|
||||
await page.waitForEvent('filechooser').catch(e => error = e);
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should prioritize exact timeout over default timeout', async({page, server}) => {
|
||||
page.setDefaultTimeout(0);
|
||||
let error = null;
|
||||
await page.waitForEvent('filechooser', {timeout: 1}).catch(e => error = e);
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should work with no timeout', async({page, server}) => {
|
||||
const [chooser] = await Promise.all([
|
||||
page.waitForEvent('filechooser', {timeout: 0}),
|
||||
page.evaluate(() => setTimeout(() => {
|
||||
const el = document.createElement('input');
|
||||
el.type = 'file';
|
||||
el.click();
|
||||
}, 50))
|
||||
]);
|
||||
expect(chooser).toBeTruthy();
|
||||
});
|
||||
it('should return the same file chooser when there are many watchdogs simultaneously', async({page, server}) => {
|
||||
await page.setContent(`<input type=file>`);
|
||||
const [fileChooser1, fileChooser2] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.waitForEvent('filechooser'),
|
||||
page.$eval('input', input => input.click()),
|
||||
]);
|
||||
expect(fileChooser1 === fileChooser2).toBe(true);
|
||||
});
|
||||
it('should accept single file', async({page, server}) => {
|
||||
await page.setContent(`<input type=file oninput='javascript:console.timeStamp()'>`);
|
||||
const [{ element }] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.click('input'),
|
||||
]);
|
||||
await element.setInputFiles(FILE_TO_UPLOAD);
|
||||
expect(await page.$eval('input', input => input.files.length)).toBe(1);
|
||||
expect(await page.$eval('input', input => input.files[0].name)).toBe('file-to-upload.txt');
|
||||
});
|
||||
it('should detect mime type', async({page, server}) => {
|
||||
let callback;
|
||||
const result = new Promise(f => callback = f);
|
||||
server.setRoute('/upload', async (req, res) => {
|
||||
const form = new formidable.IncomingForm();
|
||||
form.parse(req, function(err, fields, { file1, file2 }) {
|
||||
expect(file1.name).toBe('file-to-upload.txt');
|
||||
expect(file1.type).toBe('text/plain');
|
||||
expect(
|
||||
fs.readFileSync(file1.path).toString()
|
||||
).toBe(
|
||||
fs.readFileSync(path.join(__dirname, '/assets/file-to-upload.txt')).toString()
|
||||
);
|
||||
expect(file2.name).toBe('pptr.png');
|
||||
expect(file2.type).toBe('image/png');
|
||||
expect(
|
||||
fs.readFileSync(file2.path).toString()
|
||||
).toBe(
|
||||
fs.readFileSync(path.join(__dirname, '/assets/pptr.png')).toString()
|
||||
);
|
||||
callback();
|
||||
});
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`
|
||||
<form action="/upload" method="post" enctype="multipart/form-data" >
|
||||
<input type="file" name="file1">
|
||||
<input type="file" name="file2">
|
||||
<input type="submit" value="Submit">
|
||||
</form>`)
|
||||
await (await page.$('input[name=file1]')).setInputFiles(path.join(__dirname, '/assets/file-to-upload.txt'));
|
||||
await (await page.$('input[name=file2]')).setInputFiles(path.join(__dirname, '/assets/pptr.png'));
|
||||
page.click('input[type=submit]');
|
||||
await result;
|
||||
});
|
||||
it('should be able to read selected file', async({page, server}) => {
|
||||
await page.setContent(`<input type=file>`);
|
||||
const [, content] = await Promise.all([
|
||||
page.waitForEvent('filechooser').then(({element}) => element.setInputFiles(FILE_TO_UPLOAD)),
|
||||
page.$eval('input', async picker => {
|
||||
picker.click();
|
||||
await new Promise(x => picker.oninput = x);
|
||||
const reader = new FileReader();
|
||||
const promise = new Promise(fulfill => reader.onload = fulfill);
|
||||
reader.readAsText(e.files[0]);
|
||||
reader.readAsText(picker.files[0]);
|
||||
return promise.then(() => reader.result);
|
||||
}, input)).toBe('contents of the file');
|
||||
});
|
||||
}),
|
||||
]);
|
||||
expect(content).toBe('contents of the file');
|
||||
});
|
||||
it('should be able to reset selected files with empty file list', async({page, server}) => {
|
||||
await page.setContent(`<input type=file>`);
|
||||
const [, fileLength1] = await Promise.all([
|
||||
page.waitForEvent('filechooser').then(({element}) => element.setInputFiles(FILE_TO_UPLOAD)),
|
||||
page.$eval('input', async picker => {
|
||||
picker.click();
|
||||
await new Promise(x => picker.oninput = x);
|
||||
return picker.files.length;
|
||||
}),
|
||||
]);
|
||||
expect(fileLength1).toBe(1);
|
||||
const [, fileLength2] = await Promise.all([
|
||||
page.waitForEvent('filechooser').then(({element}) => element.setInputFiles([])),
|
||||
page.$eval('input', async picker => {
|
||||
picker.click();
|
||||
await new Promise(x => picker.oninput = x);
|
||||
return picker.files.length;
|
||||
}),
|
||||
]);
|
||||
expect(fileLength2).toBe(0);
|
||||
});
|
||||
it('should not accept multiple files for single-file input', async({page, server}) => {
|
||||
await page.setContent(`<input type=file>`);
|
||||
const [{ element }] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.click('input'),
|
||||
]);
|
||||
let error = null;
|
||||
await element.setInputFiles([
|
||||
path.relative(process.cwd(), __dirname + '/assets/file-to-upload.txt'),
|
||||
path.relative(process.cwd(), __dirname + '/assets/pptr.png')
|
||||
]).catch(e => error = e);
|
||||
expect(error).not.toBe(null);
|
||||
});
|
||||
it('should emit input and change events', async({page, server}) => {
|
||||
const events = [];
|
||||
await page.exposeFunction('eventHandled', e => events.push(e));
|
||||
await page.setContent(`
|
||||
<input id=input type=file></input>
|
||||
<script>
|
||||
input.addEventListener('input', e => eventHandled({ type: e.type }));
|
||||
input.addEventListener('change', e => eventHandled({ type: e.type }));
|
||||
</script>`);
|
||||
await (await page.$('input')).setInputFiles(FILE_TO_UPLOAD);
|
||||
expect(events.length).toBe(2);
|
||||
expect(events[0].type).toBe('input');
|
||||
expect(events[1].type).toBe('change');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.waitForFileChooser', function() {
|
||||
it('should emit event', async({page, server}) => {
|
||||
await page.setContent(`<input type=file>`);
|
||||
const [chooser] = await Promise.all([
|
||||
new Promise(f => page.once('filechooser', f)),
|
||||
page.click('input'),
|
||||
]);
|
||||
expect(chooser).toBeTruthy();
|
||||
});
|
||||
it('should work when file input is attached to DOM', async({page, server}) => {
|
||||
await page.setContent(`<input type=file>`);
|
||||
const [chooser] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.click('input'),
|
||||
]);
|
||||
expect(chooser).toBeTruthy();
|
||||
});
|
||||
it('should work when file input is not attached to DOM', async({page, server}) => {
|
||||
const [chooser] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.evaluate(() => {
|
||||
const el = document.createElement('input');
|
||||
el.type = 'file';
|
||||
el.click();
|
||||
}),
|
||||
]);
|
||||
expect(chooser).toBeTruthy();
|
||||
});
|
||||
it('should respect timeout', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.waitForEvent('filechooser', {timeout: 1}).catch(e => error = e);
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should respect default timeout when there is no custom timeout', async({page, server}) => {
|
||||
page.setDefaultTimeout(1);
|
||||
let error = null;
|
||||
await page.waitForEvent('filechooser').catch(e => error = e);
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should prioritize exact timeout over default timeout', async({page, server}) => {
|
||||
page.setDefaultTimeout(0);
|
||||
let error = null;
|
||||
await page.waitForEvent('filechooser', {timeout: 1}).catch(e => error = e);
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should work with no timeout', async({page, server}) => {
|
||||
const [chooser] = await Promise.all([
|
||||
page.waitForEvent('filechooser', {timeout: 0}),
|
||||
page.evaluate(() => setTimeout(() => {
|
||||
const el = document.createElement('input');
|
||||
el.type = 'file';
|
||||
el.click();
|
||||
}, 50))
|
||||
]);
|
||||
expect(chooser).toBeTruthy();
|
||||
});
|
||||
it('should return the same file chooser when there are many watchdogs simultaneously', async({page, server}) => {
|
||||
await page.setContent(`<input type=file>`);
|
||||
const [fileChooser1, fileChooser2] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.waitForEvent('filechooser'),
|
||||
page.$eval('input', input => input.click()),
|
||||
]);
|
||||
expect(fileChooser1 === fileChooser2).toBe(true);
|
||||
});
|
||||
it('should accept single file', async({page, server}) => {
|
||||
await page.setContent(`<input type=file oninput='javascript:console.timeStamp()'>`);
|
||||
const [{ element }] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.click('input'),
|
||||
]);
|
||||
await element.setInputFiles(FILE_TO_UPLOAD);
|
||||
expect(await page.$eval('input', input => input.files.length)).toBe(1);
|
||||
expect(await page.$eval('input', input => input.files[0].name)).toBe('file-to-upload.txt');
|
||||
});
|
||||
it('should detect mime type', async({page, server}) => {
|
||||
let callback;
|
||||
const result = new Promise(f => callback = f);
|
||||
server.setRoute('/upload', async (req, res) => {
|
||||
const form = new formidable.IncomingForm();
|
||||
form.parse(req, function(err, fields, { file1, file2 }) {
|
||||
expect(file1.name).toBe('file-to-upload.txt');
|
||||
expect(file1.type).toBe('text/plain');
|
||||
expect(
|
||||
fs.readFileSync(file1.path).toString()
|
||||
).toBe(
|
||||
fs.readFileSync(path.join(__dirname, '/assets/file-to-upload.txt')).toString()
|
||||
);
|
||||
expect(file2.name).toBe('pptr.png');
|
||||
expect(file2.type).toBe('image/png');
|
||||
expect(
|
||||
fs.readFileSync(file2.path).toString()
|
||||
).toBe(
|
||||
fs.readFileSync(path.join(__dirname, '/assets/pptr.png')).toString()
|
||||
);
|
||||
callback();
|
||||
});
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`
|
||||
<form action="/upload" method="post" enctype="multipart/form-data" >
|
||||
<input type="file" name="file1">
|
||||
<input type="file" name="file2">
|
||||
<input type="submit" value="Submit">
|
||||
</form>`)
|
||||
await (await page.$('input[name=file1]')).setInputFiles(path.join(__dirname, '/assets/file-to-upload.txt'));
|
||||
await (await page.$('input[name=file2]')).setInputFiles(path.join(__dirname, '/assets/pptr.png'));
|
||||
page.click('input[type=submit]');
|
||||
await result;
|
||||
});
|
||||
it('should be able to read selected file', async({page, server}) => {
|
||||
await page.setContent(`<input type=file>`);
|
||||
const [, content] = await Promise.all([
|
||||
page.waitForEvent('filechooser').then(({element}) => element.setInputFiles(FILE_TO_UPLOAD)),
|
||||
page.$eval('input', async picker => {
|
||||
picker.click();
|
||||
await new Promise(x => picker.oninput = x);
|
||||
const reader = new FileReader();
|
||||
const promise = new Promise(fulfill => reader.onload = fulfill);
|
||||
reader.readAsText(picker.files[0]);
|
||||
return promise.then(() => reader.result);
|
||||
}),
|
||||
]);
|
||||
expect(content).toBe('contents of the file');
|
||||
});
|
||||
it('should be able to reset selected files with empty file list', async({page, server}) => {
|
||||
await page.setContent(`<input type=file>`);
|
||||
const [, fileLength1] = await Promise.all([
|
||||
page.waitForEvent('filechooser').then(({element}) => element.setInputFiles(FILE_TO_UPLOAD)),
|
||||
page.$eval('input', async picker => {
|
||||
picker.click();
|
||||
await new Promise(x => picker.oninput = x);
|
||||
return picker.files.length;
|
||||
}),
|
||||
]);
|
||||
expect(fileLength1).toBe(1);
|
||||
const [, fileLength2] = await Promise.all([
|
||||
page.waitForEvent('filechooser').then(({element}) => element.setInputFiles([])),
|
||||
page.$eval('input', async picker => {
|
||||
picker.click();
|
||||
await new Promise(x => picker.oninput = x);
|
||||
return picker.files.length;
|
||||
}),
|
||||
]);
|
||||
expect(fileLength2).toBe(0);
|
||||
});
|
||||
it('should not accept multiple files for single-file input', async({page, server}) => {
|
||||
await page.setContent(`<input type=file>`);
|
||||
const [{ element }] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.click('input'),
|
||||
]);
|
||||
let error = null;
|
||||
await element.setInputFiles([
|
||||
path.relative(process.cwd(), __dirname + '/assets/file-to-upload.txt'),
|
||||
path.relative(process.cwd(), __dirname + '/assets/pptr.png')
|
||||
]).catch(e => error = e);
|
||||
expect(error).not.toBe(null);
|
||||
});
|
||||
it('should emit input and change events', async({page, server}) => {
|
||||
const events = [];
|
||||
await page.exposeFunction('eventHandled', e => events.push(e));
|
||||
await page.setContent(`
|
||||
<input id=input type=file></input>
|
||||
<script>
|
||||
input.addEventListener('input', e => eventHandled({ type: e.type }));
|
||||
input.addEventListener('change', e => eventHandled({ type: e.type }));
|
||||
</script>`);
|
||||
await (await page.$('input')).setInputFiles(FILE_TO_UPLOAD);
|
||||
expect(events.length).toBe(2);
|
||||
expect(events[0].type).toBe('input');
|
||||
expect(events[1].type).toBe('change');
|
||||
});
|
||||
describe('Page.waitForFileChooser isMultiple', () => {
|
||||
it('should work for single file pick', async({page, server}) => {
|
||||
await page.setContent(`<input type=file>`);
|
||||
const [{ multiple }] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.click('input'),
|
||||
]);
|
||||
expect(multiple).toBe(false);
|
||||
});
|
||||
|
||||
describe('Page.waitForFileChooser isMultiple', () => {
|
||||
it('should work for single file pick', async({page, server}) => {
|
||||
await page.setContent(`<input type=file>`);
|
||||
const [{ multiple }] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.click('input'),
|
||||
]);
|
||||
expect(multiple).toBe(false);
|
||||
});
|
||||
it('should work for "multiple"', async({page, server}) => {
|
||||
await page.setContent(`<input multiple type=file>`);
|
||||
const [{ multiple }] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.click('input'),
|
||||
]);
|
||||
expect(multiple).toBe(true);
|
||||
});
|
||||
it('should work for "webkitdirectory"', async({page, server}) => {
|
||||
await page.setContent(`<input multiple webkitdirectory type=file>`);
|
||||
const [{ multiple }] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.click('input'),
|
||||
]);
|
||||
expect(multiple).toBe(true);
|
||||
});
|
||||
it('should work for "multiple"', async({page, server}) => {
|
||||
await page.setContent(`<input multiple type=file>`);
|
||||
const [{ multiple }] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.click('input'),
|
||||
]);
|
||||
expect(multiple).toBe(true);
|
||||
});
|
||||
};
|
||||
it('should work for "webkitdirectory"', async({page, server}) => {
|
||||
await page.setContent(`<input multiple webkitdirectory type=file>`);
|
||||
const [{ multiple }] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.click('input'),
|
||||
]);
|
||||
expect(multiple).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -15,263 +15,259 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({CHROMIUM, FFOX, WEBKIT}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
|
||||
describe('Page.evaluateHandle', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const windowHandle = await page.evaluateHandle(() => window);
|
||||
expect(windowHandle).toBeTruthy();
|
||||
});
|
||||
it('should accept object handle as an argument', async({page, server}) => {
|
||||
const navigatorHandle = await page.evaluateHandle(() => navigator);
|
||||
const text = await page.evaluate(e => e.userAgent, navigatorHandle);
|
||||
expect(text).toContain('Mozilla');
|
||||
});
|
||||
it('should accept object handle to primitive types', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => 5);
|
||||
const isFive = await page.evaluate(e => Object.is(e, 5), aHandle);
|
||||
expect(isFive).toBeTruthy();
|
||||
});
|
||||
it('should accept nested handle', async({page, server}) => {
|
||||
const foo = await page.evaluateHandle(() => ({ x: 1, y: 'foo' }));
|
||||
const result = await page.evaluate(({ foo }) => {
|
||||
return foo;
|
||||
}, { foo });
|
||||
expect(result).toEqual({ x: 1, y: 'foo' });
|
||||
});
|
||||
it('should accept nested window handle', async({page, server}) => {
|
||||
const foo = await page.evaluateHandle(() => window);
|
||||
const result = await page.evaluate(({ foo }) => {
|
||||
return foo === window;
|
||||
}, { foo });
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
it('should accept multiple nested handles', async({page, server}) => {
|
||||
const foo = await page.evaluateHandle(() => ({ x: 1, y: 'foo' }));
|
||||
const bar = await page.evaluateHandle(() => 5);
|
||||
const baz = await page.evaluateHandle(() => (['baz']));
|
||||
const result = await page.evaluate(x => {
|
||||
return JSON.stringify(x);
|
||||
}, { a1: { foo }, a2: { bar, arr: [{ baz }] } });
|
||||
expect(JSON.parse(result)).toEqual({
|
||||
a1: { foo: { x: 1, y: 'foo' } },
|
||||
a2: { bar: 5, arr: [{ baz: ['baz'] }] }
|
||||
});
|
||||
});
|
||||
it('should throw for deep objects', async({page, server}) => {
|
||||
let a = { x: 1 };
|
||||
for (let i = 0; i < 98; i++)
|
||||
a = { x: a };
|
||||
expect(await page.evaluate(x => x, a)).toEqual(a);
|
||||
let error = await page.evaluate(x => x, {a}).catch(e => e);
|
||||
expect(error.message).toBe('Argument nesting is too deep');
|
||||
error = await page.evaluate(x => x, [a]).catch(e => e);
|
||||
expect(error.message).toBe('Argument nesting is too deep');
|
||||
});
|
||||
it('should throw for circular objects', async({page, server}) => {
|
||||
const a = { x: 1 };
|
||||
a.y = a;
|
||||
const error = await page.evaluate(x => x, a).catch(e => e);
|
||||
expect(error.message).toBe('Argument is a circular structure');
|
||||
});
|
||||
it('should accept same handle multiple times', async({page, server}) => {
|
||||
const foo = await page.evaluateHandle(() => 1);
|
||||
expect(await page.evaluate(x => x, { foo, bar: [foo], baz: { foo }})).toEqual({ foo: 1, bar: [1], baz: { foo: 1 } });
|
||||
});
|
||||
it('should accept same nested object multiple times', async({page, server}) => {
|
||||
const foo = { x: 1 };
|
||||
expect(await page.evaluate(x => x, { foo, bar: [foo], baz: { foo }})).toEqual({ foo: { x: 1 }, bar: [{ x : 1 }], baz: { foo: { x : 1 } } });
|
||||
});
|
||||
it('should accept object handle to unserializable value', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => Infinity);
|
||||
expect(await page.evaluate(e => Object.is(e, Infinity), aHandle)).toBe(true);
|
||||
});
|
||||
it('should pass configurable args', async({page, server}) => {
|
||||
const result = await page.evaluate(arg => {
|
||||
if (arg.foo !== 42)
|
||||
throw new Error('Not a 42');
|
||||
arg.foo = 17;
|
||||
if (arg.foo !== 17)
|
||||
throw new Error('Not 17');
|
||||
delete arg.foo;
|
||||
if (arg.foo === 17)
|
||||
throw new Error('Still 17');
|
||||
return arg;
|
||||
}, { foo: 42 });
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
it('should use the same JS wrappers', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => {
|
||||
window.FOO = 123;
|
||||
return window;
|
||||
});
|
||||
expect(await page.evaluate(e => e.FOO, aHandle)).toBe(123);
|
||||
});
|
||||
it('should work with primitives', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => {
|
||||
window.FOO = 123;
|
||||
return window;
|
||||
});
|
||||
expect(await page.evaluate(e => e.FOO, aHandle)).toBe(123);
|
||||
describe('Page.evaluateHandle', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const windowHandle = await page.evaluateHandle(() => window);
|
||||
expect(windowHandle).toBeTruthy();
|
||||
});
|
||||
it('should accept object handle as an argument', async({page, server}) => {
|
||||
const navigatorHandle = await page.evaluateHandle(() => navigator);
|
||||
const text = await page.evaluate(e => e.userAgent, navigatorHandle);
|
||||
expect(text).toContain('Mozilla');
|
||||
});
|
||||
it('should accept object handle to primitive types', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => 5);
|
||||
const isFive = await page.evaluate(e => Object.is(e, 5), aHandle);
|
||||
expect(isFive).toBeTruthy();
|
||||
});
|
||||
it('should accept nested handle', async({page, server}) => {
|
||||
const foo = await page.evaluateHandle(() => ({ x: 1, y: 'foo' }));
|
||||
const result = await page.evaluate(({ foo }) => {
|
||||
return foo;
|
||||
}, { foo });
|
||||
expect(result).toEqual({ x: 1, y: 'foo' });
|
||||
});
|
||||
it('should accept nested window handle', async({page, server}) => {
|
||||
const foo = await page.evaluateHandle(() => window);
|
||||
const result = await page.evaluate(({ foo }) => {
|
||||
return foo === window;
|
||||
}, { foo });
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
it('should accept multiple nested handles', async({page, server}) => {
|
||||
const foo = await page.evaluateHandle(() => ({ x: 1, y: 'foo' }));
|
||||
const bar = await page.evaluateHandle(() => 5);
|
||||
const baz = await page.evaluateHandle(() => (['baz']));
|
||||
const result = await page.evaluate(x => {
|
||||
return JSON.stringify(x);
|
||||
}, { a1: { foo }, a2: { bar, arr: [{ baz }] } });
|
||||
expect(JSON.parse(result)).toEqual({
|
||||
a1: { foo: { x: 1, y: 'foo' } },
|
||||
a2: { bar: 5, arr: [{ baz: ['baz'] }] }
|
||||
});
|
||||
});
|
||||
|
||||
describe('JSHandle.getProperty', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => ({
|
||||
one: 1,
|
||||
two: 2,
|
||||
three: 3
|
||||
}));
|
||||
const twoHandle = await aHandle.getProperty('two');
|
||||
expect(await twoHandle.jsonValue()).toEqual(2);
|
||||
});
|
||||
it('should work with undefined, null, and empty', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => ({
|
||||
undefined: undefined,
|
||||
null: null,
|
||||
}));
|
||||
const undefinedHandle = await aHandle.getProperty('undefined');
|
||||
expect(String(await undefinedHandle.jsonValue())).toEqual('undefined');
|
||||
const nullHandle = await aHandle.getProperty('null');
|
||||
expect(await nullHandle.jsonValue()).toEqual(null);
|
||||
const emptyhandle = await aHandle.getProperty('empty');
|
||||
expect(String(await emptyhandle.jsonValue())).toEqual('undefined');
|
||||
})
|
||||
it('should throw for deep objects', async({page, server}) => {
|
||||
let a = { x: 1 };
|
||||
for (let i = 0; i < 98; i++)
|
||||
a = { x: a };
|
||||
expect(await page.evaluate(x => x, a)).toEqual(a);
|
||||
let error = await page.evaluate(x => x, {a}).catch(e => e);
|
||||
expect(error.message).toBe('Argument nesting is too deep');
|
||||
error = await page.evaluate(x => x, [a]).catch(e => e);
|
||||
expect(error.message).toBe('Argument nesting is too deep');
|
||||
});
|
||||
|
||||
describe('JSHandle.jsonValue', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => ({foo: 'bar'}));
|
||||
const json = await aHandle.jsonValue();
|
||||
expect(json).toEqual({foo: 'bar'});
|
||||
});
|
||||
it('should not work with dates', async({page, server}) => {
|
||||
const dateHandle = await page.evaluateHandle(() => new Date('2017-09-26T00:00:00.000Z'));
|
||||
const json = await dateHandle.jsonValue();
|
||||
expect(json).toEqual({});
|
||||
});
|
||||
it('should throw for circular objects', async({page, server}) => {
|
||||
const windowHandle = await page.evaluateHandle('window');
|
||||
let error = null;
|
||||
await windowHandle.jsonValue().catch(e => error = e);
|
||||
if (WEBKIT)
|
||||
expect(error.message).toContain('Object has too long reference chain');
|
||||
else if (CHROMIUM)
|
||||
expect(error.message).toContain('Object reference chain is too long');
|
||||
else if (FFOX)
|
||||
expect(error.message).toContain('Object is not serializable');
|
||||
});
|
||||
it('should work with tricky values', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => ({a: 1}));
|
||||
const json = await aHandle.jsonValue();
|
||||
expect(json).toEqual({a: 1});
|
||||
});
|
||||
it('should throw for circular objects', async({page, server}) => {
|
||||
const a = { x: 1 };
|
||||
a.y = a;
|
||||
const error = await page.evaluate(x => x, a).catch(e => e);
|
||||
expect(error.message).toBe('Argument is a circular structure');
|
||||
});
|
||||
it('should accept same handle multiple times', async({page, server}) => {
|
||||
const foo = await page.evaluateHandle(() => 1);
|
||||
expect(await page.evaluate(x => x, { foo, bar: [foo], baz: { foo }})).toEqual({ foo: 1, bar: [1], baz: { foo: 1 } });
|
||||
});
|
||||
it('should accept same nested object multiple times', async({page, server}) => {
|
||||
const foo = { x: 1 };
|
||||
expect(await page.evaluate(x => x, { foo, bar: [foo], baz: { foo }})).toEqual({ foo: { x: 1 }, bar: [{ x : 1 }], baz: { foo: { x : 1 } } });
|
||||
});
|
||||
it('should accept object handle to unserializable value', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => Infinity);
|
||||
expect(await page.evaluate(e => Object.is(e, Infinity), aHandle)).toBe(true);
|
||||
});
|
||||
it('should pass configurable args', async({page, server}) => {
|
||||
const result = await page.evaluate(arg => {
|
||||
if (arg.foo !== 42)
|
||||
throw new Error('Not a 42');
|
||||
arg.foo = 17;
|
||||
if (arg.foo !== 17)
|
||||
throw new Error('Not 17');
|
||||
delete arg.foo;
|
||||
if (arg.foo === 17)
|
||||
throw new Error('Still 17');
|
||||
return arg;
|
||||
}, { foo: 42 });
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
it('should use the same JS wrappers', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => {
|
||||
window.FOO = 123;
|
||||
return window;
|
||||
});
|
||||
expect(await page.evaluate(e => e.FOO, aHandle)).toBe(123);
|
||||
});
|
||||
it('should work with primitives', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => {
|
||||
window.FOO = 123;
|
||||
return window;
|
||||
});
|
||||
expect(await page.evaluate(e => e.FOO, aHandle)).toBe(123);
|
||||
});
|
||||
});
|
||||
|
||||
describe('JSHandle.getProperties', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => ({
|
||||
foo: 'bar'
|
||||
}));
|
||||
const properties = await aHandle.getProperties();
|
||||
const foo = properties.get('foo');
|
||||
expect(foo).toBeTruthy();
|
||||
expect(await foo.jsonValue()).toBe('bar');
|
||||
});
|
||||
it('should return empty map for non-objects', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => 123);
|
||||
const properties = await aHandle.getProperties();
|
||||
expect(properties.size).toBe(0);
|
||||
});
|
||||
it('should return even non-own properties', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => {
|
||||
class A {
|
||||
constructor() {
|
||||
this.a = '1';
|
||||
}
|
||||
describe('JSHandle.getProperty', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => ({
|
||||
one: 1,
|
||||
two: 2,
|
||||
three: 3
|
||||
}));
|
||||
const twoHandle = await aHandle.getProperty('two');
|
||||
expect(await twoHandle.jsonValue()).toEqual(2);
|
||||
});
|
||||
it('should work with undefined, null, and empty', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => ({
|
||||
undefined: undefined,
|
||||
null: null,
|
||||
}));
|
||||
const undefinedHandle = await aHandle.getProperty('undefined');
|
||||
expect(String(await undefinedHandle.jsonValue())).toEqual('undefined');
|
||||
const nullHandle = await aHandle.getProperty('null');
|
||||
expect(await nullHandle.jsonValue()).toEqual(null);
|
||||
const emptyhandle = await aHandle.getProperty('empty');
|
||||
expect(String(await emptyhandle.jsonValue())).toEqual('undefined');
|
||||
})
|
||||
});
|
||||
|
||||
describe('JSHandle.jsonValue', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => ({foo: 'bar'}));
|
||||
const json = await aHandle.jsonValue();
|
||||
expect(json).toEqual({foo: 'bar'});
|
||||
});
|
||||
it('should not work with dates', async({page, server}) => {
|
||||
const dateHandle = await page.evaluateHandle(() => new Date('2017-09-26T00:00:00.000Z'));
|
||||
const json = await dateHandle.jsonValue();
|
||||
expect(json).toEqual({});
|
||||
});
|
||||
it('should throw for circular objects', async({page, server}) => {
|
||||
const windowHandle = await page.evaluateHandle('window');
|
||||
let error = null;
|
||||
await windowHandle.jsonValue().catch(e => error = e);
|
||||
if (WEBKIT)
|
||||
expect(error.message).toContain('Object has too long reference chain');
|
||||
else if (CHROMIUM)
|
||||
expect(error.message).toContain('Object reference chain is too long');
|
||||
else if (FFOX)
|
||||
expect(error.message).toContain('Object is not serializable');
|
||||
});
|
||||
it('should work with tricky values', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => ({a: 1}));
|
||||
const json = await aHandle.jsonValue();
|
||||
expect(json).toEqual({a: 1});
|
||||
});
|
||||
});
|
||||
|
||||
describe('JSHandle.getProperties', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => ({
|
||||
foo: 'bar'
|
||||
}));
|
||||
const properties = await aHandle.getProperties();
|
||||
const foo = properties.get('foo');
|
||||
expect(foo).toBeTruthy();
|
||||
expect(await foo.jsonValue()).toBe('bar');
|
||||
});
|
||||
it('should return empty map for non-objects', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => 123);
|
||||
const properties = await aHandle.getProperties();
|
||||
expect(properties.size).toBe(0);
|
||||
});
|
||||
it('should return even non-own properties', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => {
|
||||
class A {
|
||||
constructor() {
|
||||
this.a = '1';
|
||||
}
|
||||
class B extends A {
|
||||
constructor() {
|
||||
super();
|
||||
this.b = '2';
|
||||
}
|
||||
}
|
||||
class B extends A {
|
||||
constructor() {
|
||||
super();
|
||||
this.b = '2';
|
||||
}
|
||||
return new B();
|
||||
});
|
||||
const properties = await aHandle.getProperties();
|
||||
expect(await properties.get('a').jsonValue()).toBe('1');
|
||||
expect(await properties.get('b').jsonValue()).toBe('2');
|
||||
}
|
||||
return new B();
|
||||
});
|
||||
const properties = await aHandle.getProperties();
|
||||
expect(await properties.get('a').jsonValue()).toBe('1');
|
||||
expect(await properties.get('b').jsonValue()).toBe('2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('JSHandle.asElement', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => document.body);
|
||||
const element = aHandle.asElement();
|
||||
expect(element).toBeTruthy();
|
||||
});
|
||||
it('should return null for non-elements', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => 2);
|
||||
const element = aHandle.asElement();
|
||||
expect(element).toBeFalsy();
|
||||
});
|
||||
it('should return ElementHandle for TextNodes', async({page, server}) => {
|
||||
await page.setContent('<div>ee!</div>');
|
||||
const aHandle = await page.evaluateHandle(() => document.querySelector('div').firstChild);
|
||||
const element = aHandle.asElement();
|
||||
expect(element).toBeTruthy();
|
||||
expect(await page.evaluate(e => e.nodeType === HTMLElement.TEXT_NODE, element)).toBeTruthy();
|
||||
});
|
||||
it('should work with nullified Node', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
await page.evaluate(() => delete Node);
|
||||
const handle = await page.evaluateHandle(() => document.querySelector('section'));
|
||||
const element = handle.asElement();
|
||||
expect(element).not.toBe(null);
|
||||
});
|
||||
describe('JSHandle.asElement', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => document.body);
|
||||
const element = aHandle.asElement();
|
||||
expect(element).toBeTruthy();
|
||||
});
|
||||
it('should return null for non-elements', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => 2);
|
||||
const element = aHandle.asElement();
|
||||
expect(element).toBeFalsy();
|
||||
});
|
||||
it('should return ElementHandle for TextNodes', async({page, server}) => {
|
||||
await page.setContent('<div>ee!</div>');
|
||||
const aHandle = await page.evaluateHandle(() => document.querySelector('div').firstChild);
|
||||
const element = aHandle.asElement();
|
||||
expect(element).toBeTruthy();
|
||||
expect(await page.evaluate(e => e.nodeType === HTMLElement.TEXT_NODE, element)).toBeTruthy();
|
||||
});
|
||||
it('should work with nullified Node', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
await page.evaluate(() => delete Node);
|
||||
const handle = await page.evaluateHandle(() => document.querySelector('section'));
|
||||
const element = handle.asElement();
|
||||
expect(element).not.toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('JSHandle.toString', function() {
|
||||
it('should work for primitives', async({page, server}) => {
|
||||
const numberHandle = await page.evaluateHandle(() => 2);
|
||||
expect(numberHandle.toString()).toBe('JSHandle:2');
|
||||
const stringHandle = await page.evaluateHandle(() => 'a');
|
||||
expect(stringHandle.toString()).toBe('JSHandle:a');
|
||||
});
|
||||
it('should work for complicated objects', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => window);
|
||||
expect(aHandle.toString()).toBe('JSHandle@object');
|
||||
});
|
||||
it('should work for promises', async({page, server}) => {
|
||||
// wrap the promise in an object, otherwise we will await.
|
||||
const wrapperHandle = await page.evaluateHandle(() => ({b: Promise.resolve(123)}));
|
||||
const bHandle = await wrapperHandle.getProperty('b');
|
||||
expect(bHandle.toString()).toBe('JSHandle@promise');
|
||||
});
|
||||
it('should work with different subtypes', async({page, server}) => {
|
||||
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');
|
||||
expect((await page.evaluateHandle('undefined')).toString()).toBe('JSHandle:undefined');
|
||||
expect((await page.evaluateHandle('"foo"')).toString()).toBe('JSHandle:foo');
|
||||
expect((await page.evaluateHandle('Symbol()')).toString()).toBe('JSHandle@symbol');
|
||||
expect((await page.evaluateHandle('new Map()')).toString()).toBe('JSHandle@map');
|
||||
expect((await page.evaluateHandle('new Set()')).toString()).toBe('JSHandle@set');
|
||||
expect((await page.evaluateHandle('[]')).toString()).toBe('JSHandle@array');
|
||||
expect((await page.evaluateHandle('null')).toString()).toBe('JSHandle:null');
|
||||
expect((await page.evaluateHandle('/foo/')).toString()).toBe('JSHandle@regexp');
|
||||
expect((await page.evaluateHandle('document.body')).toString()).toBe('JSHandle@node');
|
||||
expect((await page.evaluateHandle('new Date()')).toString()).toBe('JSHandle@date');
|
||||
expect((await page.evaluateHandle('new WeakMap()')).toString()).toBe('JSHandle@weakmap');
|
||||
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(WEBKIT ? 'JSHandle@array' : 'JSHandle@typedarray');
|
||||
expect((await page.evaluateHandle('new Proxy({}, {})')).toString()).toBe('JSHandle@proxy');
|
||||
});
|
||||
describe('JSHandle.toString', function() {
|
||||
it('should work for primitives', async({page, server}) => {
|
||||
const numberHandle = await page.evaluateHandle(() => 2);
|
||||
expect(numberHandle.toString()).toBe('JSHandle:2');
|
||||
const stringHandle = await page.evaluateHandle(() => 'a');
|
||||
expect(stringHandle.toString()).toBe('JSHandle:a');
|
||||
});
|
||||
};
|
||||
it('should work for complicated objects', async({page, server}) => {
|
||||
const aHandle = await page.evaluateHandle(() => window);
|
||||
expect(aHandle.toString()).toBe('JSHandle@object');
|
||||
});
|
||||
it('should work for promises', async({page, server}) => {
|
||||
// wrap the promise in an object, otherwise we will await.
|
||||
const wrapperHandle = await page.evaluateHandle(() => ({b: Promise.resolve(123)}));
|
||||
const bHandle = await wrapperHandle.getProperty('b');
|
||||
expect(bHandle.toString()).toBe('JSHandle@promise');
|
||||
});
|
||||
it('should work with different subtypes', async({page, server}) => {
|
||||
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');
|
||||
expect((await page.evaluateHandle('undefined')).toString()).toBe('JSHandle:undefined');
|
||||
expect((await page.evaluateHandle('"foo"')).toString()).toBe('JSHandle:foo');
|
||||
expect((await page.evaluateHandle('Symbol()')).toString()).toBe('JSHandle@symbol');
|
||||
expect((await page.evaluateHandle('new Map()')).toString()).toBe('JSHandle@map');
|
||||
expect((await page.evaluateHandle('new Set()')).toString()).toBe('JSHandle@set');
|
||||
expect((await page.evaluateHandle('[]')).toString()).toBe('JSHandle@array');
|
||||
expect((await page.evaluateHandle('null')).toString()).toBe('JSHandle:null');
|
||||
expect((await page.evaluateHandle('/foo/')).toString()).toBe('JSHandle@regexp');
|
||||
expect((await page.evaluateHandle('document.body')).toString()).toBe('JSHandle@node');
|
||||
expect((await page.evaluateHandle('new Date()')).toString()).toBe('JSHandle@date');
|
||||
expect((await page.evaluateHandle('new WeakMap()')).toString()).toBe('JSHandle@weakmap');
|
||||
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(WEBKIT ? 'JSHandle@array' : 'JSHandle@typedarray');
|
||||
expect((await page.evaluateHandle('new Proxy({}, {})')).toString()).toBe('JSHandle@proxy');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,297 +16,291 @@
|
|||
*/
|
||||
|
||||
const utils = require('./utils');
|
||||
const os = require('os');
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC} = require('./utils').testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({FFOX, CHROMIUM, WEBKIT, MAC}) {
|
||||
|
||||
describe('Keyboard', function() {
|
||||
it('should type into a textarea', async({page, server}) => {
|
||||
await page.evaluate(() => {
|
||||
const textarea = document.createElement('textarea');
|
||||
document.body.appendChild(textarea);
|
||||
textarea.focus();
|
||||
});
|
||||
const text = 'Hello world. I am the text that was typed!';
|
||||
await page.keyboard.type(text);
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe(text);
|
||||
describe('Keyboard', function() {
|
||||
it('should type into a textarea', async({page, server}) => {
|
||||
await page.evaluate(() => {
|
||||
const textarea = document.createElement('textarea');
|
||||
document.body.appendChild(textarea);
|
||||
textarea.focus();
|
||||
});
|
||||
it('should move with the arrow keys', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.type('textarea', 'Hello World!');
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
|
||||
for (let i = 0; i < 'World!'.length; i++)
|
||||
page.keyboard.press('ArrowLeft');
|
||||
await page.keyboard.type('inserted ');
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello inserted World!');
|
||||
page.keyboard.down('Shift');
|
||||
for (let i = 0; i < 'inserted '.length; i++)
|
||||
page.keyboard.press('ArrowLeft');
|
||||
page.keyboard.up('Shift');
|
||||
await page.keyboard.press('Backspace');
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
|
||||
});
|
||||
it('should send a character with ElementHandle.press', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
const textarea = await page.$('textarea');
|
||||
await textarea.press('a');
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('a');
|
||||
|
||||
await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true));
|
||||
|
||||
await textarea.press('b');
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('a');
|
||||
});
|
||||
it('should send a character with sendCharacter', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.focus('textarea');
|
||||
await page.keyboard.insertText('嗨');
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨');
|
||||
await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true));
|
||||
await page.keyboard.insertText('a');
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨a');
|
||||
});
|
||||
it('insertText should only emit input event', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.focus('textarea');
|
||||
page.on('console', m => console.log(m.text()));
|
||||
await page.evaluate(() => {
|
||||
window.events = [];
|
||||
document.addEventListener('keydown', e => events.push(e.type));
|
||||
document.addEventListener('keyup', e => events.push(e.type));
|
||||
document.addEventListener('keypress', e => events.push(e.type));
|
||||
document.addEventListener('input', e => events.push(e.type));
|
||||
});
|
||||
await page.keyboard.insertText('hello world');
|
||||
expect(await page.evaluate('window.events')).toEqual(['input']);
|
||||
});
|
||||
it.fail(FFOX)('should report shiftKey', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/keyboard.html');
|
||||
const keyboard = page.keyboard;
|
||||
const codeForKey = {'Shift': 16, 'Alt': 18, 'Control': 17};
|
||||
for (const modifierKey in codeForKey) {
|
||||
await keyboard.down(modifierKey);
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keydown: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' [' + modifierKey + ']');
|
||||
await keyboard.down('!');
|
||||
// Shift+! will generate a keypress
|
||||
if (modifierKey === 'Shift')
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']\nKeypress: ! Digit1 33 33 [' + modifierKey + ']');
|
||||
else
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']');
|
||||
|
||||
await keyboard.up('!');
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keyup: ! Digit1 49 [' + modifierKey + ']');
|
||||
await keyboard.up(modifierKey);
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keyup: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' []');
|
||||
}
|
||||
});
|
||||
it('should report multiple modifiers', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/keyboard.html');
|
||||
const keyboard = page.keyboard;
|
||||
await keyboard.down('Control');
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keydown: Control ControlLeft 17 [Control]');
|
||||
await keyboard.down('Alt');
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keydown: Alt AltLeft 18 [Alt Control]');
|
||||
await keyboard.down(';');
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keydown: ; Semicolon 186 [Alt Control]');
|
||||
await keyboard.up(';');
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keyup: ; Semicolon 186 [Alt Control]');
|
||||
await keyboard.up('Control');
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keyup: Control ControlLeft 17 [Alt]');
|
||||
await keyboard.up('Alt');
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keyup: Alt AltLeft 18 []');
|
||||
});
|
||||
it('should send proper codes while typing', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/keyboard.html');
|
||||
await page.keyboard.type('!');
|
||||
expect(await page.evaluate(() => getResult())).toBe(
|
||||
[ 'Keydown: ! Digit1 49 []',
|
||||
'Keypress: ! Digit1 33 33 []',
|
||||
'Keyup: ! Digit1 49 []'].join('\n'));
|
||||
await page.keyboard.type('^');
|
||||
expect(await page.evaluate(() => getResult())).toBe(
|
||||
[ 'Keydown: ^ Digit6 54 []',
|
||||
'Keypress: ^ Digit6 94 94 []',
|
||||
'Keyup: ^ Digit6 54 []'].join('\n'));
|
||||
});
|
||||
it('should send proper codes while typing with shift', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/keyboard.html');
|
||||
const keyboard = page.keyboard;
|
||||
await keyboard.down('Shift');
|
||||
await page.keyboard.type('~');
|
||||
expect(await page.evaluate(() => getResult())).toBe(
|
||||
[ 'Keydown: Shift ShiftLeft 16 [Shift]',
|
||||
'Keydown: ~ Backquote 192 [Shift]', // 192 is ` keyCode
|
||||
'Keypress: ~ Backquote 126 126 [Shift]', // 126 is ~ charCode
|
||||
'Keyup: ~ Backquote 192 [Shift]'].join('\n'));
|
||||
await keyboard.up('Shift');
|
||||
});
|
||||
it('should not type canceled events', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.focus('textarea');
|
||||
await page.evaluate(() => {
|
||||
window.addEventListener('keydown', event => {
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
if (event.key === 'l')
|
||||
event.preventDefault();
|
||||
if (event.key === 'o')
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
});
|
||||
await page.keyboard.type('Hello World!');
|
||||
expect(await page.evaluate(() => textarea.value)).toBe('He Wrd!');
|
||||
});
|
||||
it('should specify repeat property', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.focus('textarea');
|
||||
await page.evaluate(() => document.querySelector('textarea').addEventListener('keydown', e => window.lastEvent = e, true));
|
||||
await page.keyboard.down('a');
|
||||
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
|
||||
await page.keyboard.press('a');
|
||||
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true);
|
||||
|
||||
await page.keyboard.down('b');
|
||||
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
|
||||
await page.keyboard.down('b');
|
||||
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true);
|
||||
|
||||
await page.keyboard.up('a');
|
||||
await page.keyboard.down('a');
|
||||
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
|
||||
});
|
||||
it('should type all kinds of characters', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.focus('textarea');
|
||||
const text = 'This text goes onto two lines.\nThis character is 嗨.';
|
||||
await page.keyboard.type(text);
|
||||
expect(await page.evaluate('result')).toBe(text);
|
||||
});
|
||||
it('should specify location', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.evaluate(() => {
|
||||
window.addEventListener('keydown', event => window.keyLocation = event.location, true);
|
||||
});
|
||||
const textarea = await page.$('textarea');
|
||||
|
||||
await textarea.press('Digit5');
|
||||
expect(await page.evaluate('keyLocation')).toBe(0);
|
||||
|
||||
await textarea.press('ControlLeft');
|
||||
expect(await page.evaluate('keyLocation')).toBe(1);
|
||||
|
||||
await textarea.press('ControlRight');
|
||||
expect(await page.evaluate('keyLocation')).toBe(2);
|
||||
|
||||
await textarea.press('NumpadSubtract');
|
||||
expect(await page.evaluate('keyLocation')).toBe(3);
|
||||
});
|
||||
it('should press Enter', async({page, server}) => {
|
||||
await page.setContent('<textarea></textarea>');
|
||||
await page.focus('textarea');
|
||||
await page.evaluate(() => window.addEventListener('keydown', e => window.lastEvent = {key: e.key, code:e.code}));
|
||||
await testEnterKey('Enter', 'Enter', 'Enter');
|
||||
await testEnterKey('NumpadEnter', 'Enter', 'NumpadEnter');
|
||||
await testEnterKey('\n', 'Enter', 'Enter');
|
||||
await testEnterKey('\r', 'Enter', 'Enter');
|
||||
|
||||
async function testEnterKey(key, expectedKey, expectedCode) {
|
||||
await page.keyboard.press(key);
|
||||
const lastEvent = await page.evaluate('lastEvent');
|
||||
expect(lastEvent.key).toBe(expectedKey, `${JSON.stringify(key)} had the wrong key: ${lastEvent.key}`);
|
||||
expect(lastEvent.code).toBe(expectedCode, `${JSON.stringify(key)} had the wrong code: ${lastEvent.code}`);
|
||||
const value = await page.$eval('textarea', t => t.value);
|
||||
expect(value).toBe('\n', `${JSON.stringify(key)} failed to create a newline: ${JSON.stringify(value)}`);
|
||||
await page.$eval('textarea', t => t.value = '');
|
||||
}
|
||||
});
|
||||
it('should throw on unknown keys', async({page, server}) => {
|
||||
let error = await page.keyboard.press('NotARealKey').catch(e => e);
|
||||
expect(error.message).toBe('Unknown key: "NotARealKey"');
|
||||
|
||||
error = await page.keyboard.press('ё').catch(e => e);
|
||||
expect(error && error.message).toBe('Unknown key: "ё"');
|
||||
|
||||
error = await page.keyboard.press('😊').catch(e => e);
|
||||
expect(error && error.message).toBe('Unknown key: "😊"');
|
||||
});
|
||||
it('should type emoji', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.type('textarea', '👹 Tokyo street Japan 🇯🇵');
|
||||
expect(await page.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵');
|
||||
});
|
||||
it('should type emoji into an iframe', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'emoji-test', server.PREFIX + '/input/textarea.html');
|
||||
const frame = page.frames()[1];
|
||||
const textarea = await frame.$('textarea');
|
||||
await textarea.type('👹 Tokyo street Japan 🇯🇵');
|
||||
expect(await frame.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵');
|
||||
});
|
||||
it.fail(CHROMIUM && MAC)('should handle selectAll', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
const textarea = await page.$('textarea');
|
||||
await textarea.type('some text');
|
||||
const modifier = MAC ? 'Meta' : 'Control';
|
||||
await page.keyboard.down(modifier);
|
||||
await page.keyboard.press('a');
|
||||
await page.keyboard.up(modifier);
|
||||
await page.keyboard.press('Backspace');
|
||||
expect(await page.$eval('textarea', textarea => textarea.value)).toBe('');
|
||||
});
|
||||
it.fail(CHROMIUM && MAC)('should be able to prevent selectAll', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
const textarea = await page.$('textarea');
|
||||
await textarea.type('some text');
|
||||
await page.$eval('textarea', textarea => {
|
||||
textarea.addEventListener('keydown', event => {
|
||||
if (event.key === 'a' && (event.metaKey || event.ctrlKey))
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
});
|
||||
const modifier = MAC ? 'Meta' : 'Control';
|
||||
await page.keyboard.down(modifier);
|
||||
await page.keyboard.press('a');
|
||||
await page.keyboard.up(modifier);
|
||||
await page.keyboard.press('Backspace');
|
||||
expect(await page.$eval('textarea', textarea => textarea.value)).toBe('some tex');
|
||||
});
|
||||
it('should press the meta key', async({page}) => {
|
||||
await page.evaluate(() => {
|
||||
window.result = null;
|
||||
document.addEventListener('keydown', event => {
|
||||
window.result = [event.key, event.code, event.metaKey];
|
||||
});
|
||||
});
|
||||
await page.keyboard.press('Meta');
|
||||
const [key, code, metaKey] = await page.evaluate('result');
|
||||
if (FFOX && !MAC)
|
||||
expect(key).toBe('OS');
|
||||
else
|
||||
expect(key).toBe('Meta');
|
||||
|
||||
if (FFOX)
|
||||
expect(code).toBe('OSLeft');
|
||||
else
|
||||
expect(code).toBe('MetaLeft');
|
||||
|
||||
if (FFOX && !MAC)
|
||||
expect(metaKey).toBe(false);
|
||||
else
|
||||
expect(metaKey).toBe(true);
|
||||
|
||||
});
|
||||
it('should work after a cross origin navigation', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
await page.evaluate(() => {
|
||||
document.addEventListener('keydown', event => window.lastKey = event);
|
||||
})
|
||||
await page.keyboard.press('a');
|
||||
expect(await page.evaluate('lastKey.key')).toBe('a');
|
||||
})
|
||||
const text = 'Hello world. I am the text that was typed!';
|
||||
await page.keyboard.type(text);
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe(text);
|
||||
});
|
||||
};
|
||||
it('should move with the arrow keys', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.type('textarea', 'Hello World!');
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
|
||||
for (let i = 0; i < 'World!'.length; i++)
|
||||
page.keyboard.press('ArrowLeft');
|
||||
await page.keyboard.type('inserted ');
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello inserted World!');
|
||||
page.keyboard.down('Shift');
|
||||
for (let i = 0; i < 'inserted '.length; i++)
|
||||
page.keyboard.press('ArrowLeft');
|
||||
page.keyboard.up('Shift');
|
||||
await page.keyboard.press('Backspace');
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
|
||||
});
|
||||
it('should send a character with ElementHandle.press', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
const textarea = await page.$('textarea');
|
||||
await textarea.press('a');
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('a');
|
||||
|
||||
await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true));
|
||||
|
||||
await textarea.press('b');
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('a');
|
||||
});
|
||||
it('should send a character with sendCharacter', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.focus('textarea');
|
||||
await page.keyboard.insertText('嗨');
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨');
|
||||
await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true));
|
||||
await page.keyboard.insertText('a');
|
||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨a');
|
||||
});
|
||||
it('insertText should only emit input event', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.focus('textarea');
|
||||
page.on('console', m => console.log(m.text()));
|
||||
await page.evaluate(() => {
|
||||
window.events = [];
|
||||
document.addEventListener('keydown', e => events.push(e.type));
|
||||
document.addEventListener('keyup', e => events.push(e.type));
|
||||
document.addEventListener('keypress', e => events.push(e.type));
|
||||
document.addEventListener('input', e => events.push(e.type));
|
||||
});
|
||||
await page.keyboard.insertText('hello world');
|
||||
expect(await page.evaluate('window.events')).toEqual(['input']);
|
||||
});
|
||||
it.fail(FFOX)('should report shiftKey', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/keyboard.html');
|
||||
const keyboard = page.keyboard;
|
||||
const codeForKey = {'Shift': 16, 'Alt': 18, 'Control': 17};
|
||||
for (const modifierKey in codeForKey) {
|
||||
await keyboard.down(modifierKey);
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keydown: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' [' + modifierKey + ']');
|
||||
await keyboard.down('!');
|
||||
// Shift+! will generate a keypress
|
||||
if (modifierKey === 'Shift')
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']\nKeypress: ! Digit1 33 33 [' + modifierKey + ']');
|
||||
else
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']');
|
||||
|
||||
await keyboard.up('!');
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keyup: ! Digit1 49 [' + modifierKey + ']');
|
||||
await keyboard.up(modifierKey);
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keyup: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' []');
|
||||
}
|
||||
});
|
||||
it('should report multiple modifiers', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/keyboard.html');
|
||||
const keyboard = page.keyboard;
|
||||
await keyboard.down('Control');
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keydown: Control ControlLeft 17 [Control]');
|
||||
await keyboard.down('Alt');
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keydown: Alt AltLeft 18 [Alt Control]');
|
||||
await keyboard.down(';');
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keydown: ; Semicolon 186 [Alt Control]');
|
||||
await keyboard.up(';');
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keyup: ; Semicolon 186 [Alt Control]');
|
||||
await keyboard.up('Control');
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keyup: Control ControlLeft 17 [Alt]');
|
||||
await keyboard.up('Alt');
|
||||
expect(await page.evaluate(() => getResult())).toBe('Keyup: Alt AltLeft 18 []');
|
||||
});
|
||||
it('should send proper codes while typing', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/keyboard.html');
|
||||
await page.keyboard.type('!');
|
||||
expect(await page.evaluate(() => getResult())).toBe(
|
||||
[ 'Keydown: ! Digit1 49 []',
|
||||
'Keypress: ! Digit1 33 33 []',
|
||||
'Keyup: ! Digit1 49 []'].join('\n'));
|
||||
await page.keyboard.type('^');
|
||||
expect(await page.evaluate(() => getResult())).toBe(
|
||||
[ 'Keydown: ^ Digit6 54 []',
|
||||
'Keypress: ^ Digit6 94 94 []',
|
||||
'Keyup: ^ Digit6 54 []'].join('\n'));
|
||||
});
|
||||
it('should send proper codes while typing with shift', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/keyboard.html');
|
||||
const keyboard = page.keyboard;
|
||||
await keyboard.down('Shift');
|
||||
await page.keyboard.type('~');
|
||||
expect(await page.evaluate(() => getResult())).toBe(
|
||||
[ 'Keydown: Shift ShiftLeft 16 [Shift]',
|
||||
'Keydown: ~ Backquote 192 [Shift]', // 192 is ` keyCode
|
||||
'Keypress: ~ Backquote 126 126 [Shift]', // 126 is ~ charCode
|
||||
'Keyup: ~ Backquote 192 [Shift]'].join('\n'));
|
||||
await keyboard.up('Shift');
|
||||
});
|
||||
it('should not type canceled events', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.focus('textarea');
|
||||
await page.evaluate(() => {
|
||||
window.addEventListener('keydown', event => {
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
if (event.key === 'l')
|
||||
event.preventDefault();
|
||||
if (event.key === 'o')
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
});
|
||||
await page.keyboard.type('Hello World!');
|
||||
expect(await page.evaluate(() => textarea.value)).toBe('He Wrd!');
|
||||
});
|
||||
it('should specify repeat property', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.focus('textarea');
|
||||
await page.evaluate(() => document.querySelector('textarea').addEventListener('keydown', e => window.lastEvent = e, true));
|
||||
await page.keyboard.down('a');
|
||||
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
|
||||
await page.keyboard.press('a');
|
||||
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true);
|
||||
|
||||
await page.keyboard.down('b');
|
||||
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
|
||||
await page.keyboard.down('b');
|
||||
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true);
|
||||
|
||||
await page.keyboard.up('a');
|
||||
await page.keyboard.down('a');
|
||||
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
|
||||
});
|
||||
it('should type all kinds of characters', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.focus('textarea');
|
||||
const text = 'This text goes onto two lines.\nThis character is 嗨.';
|
||||
await page.keyboard.type(text);
|
||||
expect(await page.evaluate('result')).toBe(text);
|
||||
});
|
||||
it('should specify location', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.evaluate(() => {
|
||||
window.addEventListener('keydown', event => window.keyLocation = event.location, true);
|
||||
});
|
||||
const textarea = await page.$('textarea');
|
||||
|
||||
await textarea.press('Digit5');
|
||||
expect(await page.evaluate('keyLocation')).toBe(0);
|
||||
|
||||
await textarea.press('ControlLeft');
|
||||
expect(await page.evaluate('keyLocation')).toBe(1);
|
||||
|
||||
await textarea.press('ControlRight');
|
||||
expect(await page.evaluate('keyLocation')).toBe(2);
|
||||
|
||||
await textarea.press('NumpadSubtract');
|
||||
expect(await page.evaluate('keyLocation')).toBe(3);
|
||||
});
|
||||
it('should press Enter', async({page, server}) => {
|
||||
await page.setContent('<textarea></textarea>');
|
||||
await page.focus('textarea');
|
||||
await page.evaluate(() => window.addEventListener('keydown', e => window.lastEvent = {key: e.key, code:e.code}));
|
||||
await testEnterKey('Enter', 'Enter', 'Enter');
|
||||
await testEnterKey('NumpadEnter', 'Enter', 'NumpadEnter');
|
||||
await testEnterKey('\n', 'Enter', 'Enter');
|
||||
await testEnterKey('\r', 'Enter', 'Enter');
|
||||
|
||||
async function testEnterKey(key, expectedKey, expectedCode) {
|
||||
await page.keyboard.press(key);
|
||||
const lastEvent = await page.evaluate('lastEvent');
|
||||
expect(lastEvent.key).toBe(expectedKey, `${JSON.stringify(key)} had the wrong key: ${lastEvent.key}`);
|
||||
expect(lastEvent.code).toBe(expectedCode, `${JSON.stringify(key)} had the wrong code: ${lastEvent.code}`);
|
||||
const value = await page.$eval('textarea', t => t.value);
|
||||
expect(value).toBe('\n', `${JSON.stringify(key)} failed to create a newline: ${JSON.stringify(value)}`);
|
||||
await page.$eval('textarea', t => t.value = '');
|
||||
}
|
||||
});
|
||||
it('should throw on unknown keys', async({page, server}) => {
|
||||
let error = await page.keyboard.press('NotARealKey').catch(e => e);
|
||||
expect(error.message).toBe('Unknown key: "NotARealKey"');
|
||||
|
||||
error = await page.keyboard.press('ё').catch(e => e);
|
||||
expect(error && error.message).toBe('Unknown key: "ё"');
|
||||
|
||||
error = await page.keyboard.press('😊').catch(e => e);
|
||||
expect(error && error.message).toBe('Unknown key: "😊"');
|
||||
});
|
||||
it('should type emoji', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.type('textarea', '👹 Tokyo street Japan 🇯🇵');
|
||||
expect(await page.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵');
|
||||
});
|
||||
it('should type emoji into an iframe', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'emoji-test', server.PREFIX + '/input/textarea.html');
|
||||
const frame = page.frames()[1];
|
||||
const textarea = await frame.$('textarea');
|
||||
await textarea.type('👹 Tokyo street Japan 🇯🇵');
|
||||
expect(await frame.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵');
|
||||
});
|
||||
it.fail(CHROMIUM && MAC)('should handle selectAll', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
const textarea = await page.$('textarea');
|
||||
await textarea.type('some text');
|
||||
const modifier = MAC ? 'Meta' : 'Control';
|
||||
await page.keyboard.down(modifier);
|
||||
await page.keyboard.press('a');
|
||||
await page.keyboard.up(modifier);
|
||||
await page.keyboard.press('Backspace');
|
||||
expect(await page.$eval('textarea', textarea => textarea.value)).toBe('');
|
||||
});
|
||||
it.fail(CHROMIUM && MAC)('should be able to prevent selectAll', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
const textarea = await page.$('textarea');
|
||||
await textarea.type('some text');
|
||||
await page.$eval('textarea', textarea => {
|
||||
textarea.addEventListener('keydown', event => {
|
||||
if (event.key === 'a' && (event.metaKey || event.ctrlKey))
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
});
|
||||
const modifier = MAC ? 'Meta' : 'Control';
|
||||
await page.keyboard.down(modifier);
|
||||
await page.keyboard.press('a');
|
||||
await page.keyboard.up(modifier);
|
||||
await page.keyboard.press('Backspace');
|
||||
expect(await page.$eval('textarea', textarea => textarea.value)).toBe('some tex');
|
||||
});
|
||||
it('should press the meta key', async({page}) => {
|
||||
await page.evaluate(() => {
|
||||
window.result = null;
|
||||
document.addEventListener('keydown', event => {
|
||||
window.result = [event.key, event.code, event.metaKey];
|
||||
});
|
||||
});
|
||||
await page.keyboard.press('Meta');
|
||||
const [key, code, metaKey] = await page.evaluate('result');
|
||||
if (FFOX && !MAC)
|
||||
expect(key).toBe('OS');
|
||||
else
|
||||
expect(key).toBe('Meta');
|
||||
|
||||
if (FFOX)
|
||||
expect(code).toBe('OSLeft');
|
||||
else
|
||||
expect(code).toBe('MetaLeft');
|
||||
|
||||
if (FFOX && !MAC)
|
||||
expect(metaKey).toBe(false);
|
||||
else
|
||||
expect(metaKey).toBe(true);
|
||||
|
||||
});
|
||||
it('should work after a cross origin navigation', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
await page.evaluate(() => {
|
||||
document.addEventListener('keydown', event => window.lastKey = event);
|
||||
})
|
||||
await page.keyboard.press('a');
|
||||
expect(await page.evaluate('lastKey.key')).toBe('a');
|
||||
})
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,333 +18,328 @@
|
|||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const utils = require('./utils');
|
||||
const { makeUserDataDir, removeUserDataDir } = require('./utils');
|
||||
const { makeUserDataDir, removeUserDataDir } = utils;
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN, defaultBrowserOptions, playwrightPath} = utils.testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {TestSuite}
|
||||
*/
|
||||
module.exports.describe = function({defaultBrowserOptions, playwright, browserType, playwrightPath, CHROMIUM, FFOX, WEBKIT, WIN}) {
|
||||
|
||||
describe('Playwright', function() {
|
||||
describe('browserType.launch', function() {
|
||||
it('should reject all promises when browser is closed', async() => {
|
||||
const browser = await browserType.launch(defaultBrowserOptions);
|
||||
const page = await (await browser.newContext()).newPage();
|
||||
let error = null;
|
||||
const neverResolves = page.evaluate(() => new Promise(r => {})).catch(e => error = e);
|
||||
await browser.close();
|
||||
await neverResolves;
|
||||
expect(error.message).toContain('Protocol error');
|
||||
});
|
||||
it('should throw if userDataDir option is passed', async() => {
|
||||
let waitError = null;
|
||||
const options = Object.assign({}, defaultBrowserOptions, {userDataDir: 'random-path'});
|
||||
await browserType.launch(options).catch(e => waitError = e);
|
||||
expect(waitError.message).toContain('launchPersistentContext');
|
||||
});
|
||||
it('should throw if page argument is passed', async() => {
|
||||
let waitError = null;
|
||||
const options = Object.assign({}, defaultBrowserOptions, { args: ['http://example.com'] });
|
||||
await browserType.launch(options).catch(e => waitError = e);
|
||||
expect(waitError.message).toContain('can not specify page');
|
||||
});
|
||||
it('should reject if executable path is invalid', async({server}) => {
|
||||
let waitError = null;
|
||||
const options = Object.assign({}, defaultBrowserOptions, {executablePath: 'random-invalid-path'});
|
||||
await browserType.launch(options).catch(e => waitError = e);
|
||||
expect(waitError.message).toContain('Failed to launch');
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.launchPersistentContext', function() {
|
||||
it('should have default URL when launching browser', async function() {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const urls = browserContext.pages().map(page => page.url());
|
||||
expect(urls).toEqual(['about:blank']);
|
||||
await browserContext.close();
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it('should have custom URL when launching browser', async function({server}) {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const options = Object.assign({}, defaultBrowserOptions);
|
||||
options.args = [server.EMPTY_PAGE].concat(options.args || []);
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, options);
|
||||
const pages = browserContext.pages();
|
||||
expect(pages.length).toBe(1);
|
||||
const page = pages[0];
|
||||
if (page.url() !== server.EMPTY_PAGE) {
|
||||
await page.waitForNavigation();
|
||||
}
|
||||
expect(page.url()).toBe(server.EMPTY_PAGE);
|
||||
await browserContext.close();
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.launchServer', function() {
|
||||
it('should return child_process instance', async () => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
expect(browserServer.process().pid).toBeGreaterThan(0);
|
||||
await browserServer.close();
|
||||
});
|
||||
it('should fire close event', async () => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
await Promise.all([
|
||||
new Promise(f => browserServer.on('close', f)),
|
||||
browserServer.close(),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.executablePath', function() {
|
||||
it('should work', async({server}) => {
|
||||
const executablePath = browserType.executablePath();
|
||||
expect(fs.existsSync(executablePath)).toBe(true);
|
||||
expect(fs.realpathSync(executablePath)).toBe(executablePath);
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.name', function() {
|
||||
it('should work', async({server}) => {
|
||||
if (WEBKIT)
|
||||
expect(browserType.name()).toBe('webkit');
|
||||
else if (FFOX)
|
||||
expect(browserType.name()).toBe('firefox');
|
||||
else if (CHROMIUM)
|
||||
expect(browserType.name()).toBe('chromium');
|
||||
else
|
||||
throw new Error('Unknown browser');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Top-level requires', function() {
|
||||
it('should require top-level Errors', async() => {
|
||||
const Errors = require(path.join(utils.projectRoot(), '/lib/errors.js'));
|
||||
expect(Errors.TimeoutError).toBe(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should require top-level DeviceDescriptors', async() => {
|
||||
const Devices = require(path.join(utils.projectRoot(), '/lib/deviceDescriptors.js')).DeviceDescriptors;
|
||||
expect(Devices['iPhone 6']).toBeTruthy();
|
||||
expect(Devices['iPhone 6']).toBe(playwright.devices['iPhone 6']);
|
||||
expect(Devices['iPhone 6']).toBe(require(playwrightPath).devices['iPhone 6']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Browser.isConnected', () => {
|
||||
it('should set the browser connected state', async () => {
|
||||
const browserServer = await browserType.launchServer({...defaultBrowserOptions });
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
expect(remote.isConnected()).toBe(true);
|
||||
await remote.close();
|
||||
expect(remote.isConnected()).toBe(false);
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
it('should throw when used after isConnected returns false', async({server}) => {
|
||||
const browserServer = await browserType.launchServer({...defaultBrowserOptions });
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const page = await remote.newPage();
|
||||
await Promise.all([
|
||||
browserServer.close(),
|
||||
new Promise(f => remote.once('disconnected', f)),
|
||||
]);
|
||||
expect(remote.isConnected()).toBe(false);
|
||||
const error = await page.evaluate('1 + 1').catch(e => e);
|
||||
expect(error.message).toContain('has been closed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Browser.disconnect', function() {
|
||||
it('should reject navigation when browser closes', async({server}) => {
|
||||
server.setRoute('/one-style.css', () => {});
|
||||
const browserServer = await browserType.launchServer({...defaultBrowserOptions });
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const page = await remote.newPage();
|
||||
const navigationPromise = page.goto(server.PREFIX + '/one-style.html', {timeout: 60000}).catch(e => e);
|
||||
await server.waitForRequest('/one-style.css');
|
||||
await remote.close();
|
||||
const error = await navigationPromise;
|
||||
expect(error.message).toContain('Navigation failed because browser has disconnected!');
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
it('should reject waitForSelector when browser closes', async({server}) => {
|
||||
server.setRoute('/empty.html', () => {});
|
||||
const browserServer = await browserType.launchServer({...defaultBrowserOptions });
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const page = await remote.newPage();
|
||||
const watchdog = page.waitForSelector('div', { timeout: 60000 }).catch(e => e);
|
||||
|
||||
// Make sure the previous waitForSelector has time to make it to the browser before we disconnect.
|
||||
await page.waitForSelector('body');
|
||||
|
||||
await remote.close();
|
||||
const error = await watchdog;
|
||||
expect(error.message).toContain('Protocol error');
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
it('should throw if used after disconnect', async({server}) => {
|
||||
const browserServer = await browserType.launchServer({...defaultBrowserOptions });
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const page = await remote.newPage();
|
||||
await remote.close();
|
||||
const error = await page.evaluate('1 + 1').catch(e => e);
|
||||
expect(error.message).toContain('has been closed');
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
it('should emit close events on pages and contexts', async({server}) => {
|
||||
const browserServer = await browserType.launchServer({...defaultBrowserOptions });
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const context = await remote.newContext();
|
||||
const page = await context.newPage();
|
||||
let pageClosed = false;
|
||||
page.on('close', e => pageClosed = true);
|
||||
await Promise.all([
|
||||
new Promise(f => context.on('close', f)),
|
||||
browserServer.close()
|
||||
]);
|
||||
expect(pageClosed).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Browser.close', function() {
|
||||
it('should terminate network waiters', async({server}) => {
|
||||
const browserServer = await browserType.launchServer({...defaultBrowserOptions });
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const newPage = await remote.newPage();
|
||||
const results = await Promise.all([
|
||||
newPage.waitForRequest(server.EMPTY_PAGE).catch(e => e),
|
||||
newPage.waitForResponse(server.EMPTY_PAGE).catch(e => e),
|
||||
browserServer.close()
|
||||
]);
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const message = results[i].message;
|
||||
expect(message).toContain('Target closed');
|
||||
expect(message).not.toContain('Timeout');
|
||||
}
|
||||
});
|
||||
it('should fire close event for all contexts', async(state, test) => {
|
||||
describe('Playwright', function() {
|
||||
describe('browserType.launch', function() {
|
||||
it('should reject all promises when browser is closed', async({browserType}) => {
|
||||
const browser = await browserType.launch(defaultBrowserOptions);
|
||||
const context = await browser.newContext();
|
||||
let closed = false;
|
||||
context.on('close', () => closed = true);
|
||||
const page = await (await browser.newContext()).newPage();
|
||||
let error = null;
|
||||
const neverResolves = page.evaluate(() => new Promise(r => {})).catch(e => error = e);
|
||||
await browser.close();
|
||||
expect(closed).toBe(true);
|
||||
await neverResolves;
|
||||
expect(error.message).toContain('Protocol error');
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.launch |webSocket| option', function() {
|
||||
it('should support the webSocket option', async() => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const browserContext = await browser.newContext();
|
||||
expect(browserContext.pages().length).toBe(0);
|
||||
expect(browserServer.wsEndpoint()).not.toBe(null);
|
||||
const page = await browserContext.newPage();
|
||||
expect(await page.evaluate('11 * 11')).toBe(121);
|
||||
await page.close();
|
||||
await browser.close();
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
it('should throw if userDataDir option is passed', async({browserType}) => {
|
||||
let waitError = null;
|
||||
const options = Object.assign({}, defaultBrowserOptions, {userDataDir: 'random-path'});
|
||||
await browserType.launch(options).catch(e => waitError = e);
|
||||
expect(waitError.message).toContain('launchPersistentContext');
|
||||
});
|
||||
it('should fire "disconnected" when closing with webSocket', async() => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
|
||||
browserServer.kill();
|
||||
await disconnectedEventPromise;
|
||||
it('should throw if page argument is passed', async({browserType}) => {
|
||||
let waitError = null;
|
||||
const options = Object.assign({}, defaultBrowserOptions, { args: ['http://example.com'] });
|
||||
await browserType.launch(options).catch(e => waitError = e);
|
||||
expect(waitError.message).toContain('can not specify page');
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.connect', function() {
|
||||
it.slow()('should be able to reconnect to a browser', async({server}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
{
|
||||
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const browserContext = await browser.newContext();
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await browser.close();
|
||||
}
|
||||
{
|
||||
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const browserContext = await browser.newContext();
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await browser.close();
|
||||
}
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
it('should reject if executable path is invalid', async({browserType}) => {
|
||||
let waitError = null;
|
||||
const options = Object.assign({}, defaultBrowserOptions, {executablePath: 'random-invalid-path'});
|
||||
await browserType.launch(options).catch(e => waitError = e);
|
||||
expect(waitError.message).toContain('Failed to launch');
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.launchPersistentContext', function() {
|
||||
it('userDataDir option', async({server}) => {
|
||||
it('should have default URL when launching browser', async ({browserType}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const options = Object.assign(defaultBrowserOptions);
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const urls = browserContext.pages().map(page => page.url());
|
||||
expect(urls).toEqual(['about:blank']);
|
||||
await browserContext.close();
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it('should have custom URL when launching browser', async ({browserType, server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const options = Object.assign({}, defaultBrowserOptions);
|
||||
options.args = [server.EMPTY_PAGE].concat(options.args || []);
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, options);
|
||||
// Open a page to make sure its functional.
|
||||
await browserContext.newPage();
|
||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||
const pages = browserContext.pages();
|
||||
expect(pages.length).toBe(1);
|
||||
const page = pages[0];
|
||||
if (page.url() !== server.EMPTY_PAGE) {
|
||||
await page.waitForNavigation();
|
||||
}
|
||||
expect(page.url()).toBe(server.EMPTY_PAGE);
|
||||
await browserContext.close();
|
||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it.slow()('userDataDir option should restore state', async({server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => localStorage.hey = 'hello');
|
||||
await browserContext.close();
|
||||
|
||||
const browserContext2 = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page2 = await browserContext2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
expect(await page2.evaluate(() => localStorage.hey)).toBe('hello');
|
||||
await browserContext2.close();
|
||||
|
||||
const userDataDir2 = await makeUserDataDir();
|
||||
const browserContext3 = await browserType.launchPersistentContext(userDataDir2, defaultBrowserOptions);
|
||||
const page3 = await browserContext3.newPage();
|
||||
await page3.goto(server.EMPTY_PAGE);
|
||||
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('hello');
|
||||
await browserContext3.close();
|
||||
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
// See https://github.com/microsoft/playwright/issues/717
|
||||
it.slow().fail(WIN && CHROMIUM)('userDataDir option should restore cookies', async({server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => document.cookie = 'doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT');
|
||||
await browserContext.close();
|
||||
|
||||
const browserContext2 = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page2 = await browserContext2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
expect(await page2.evaluate(() => document.cookie)).toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext2.close();
|
||||
|
||||
const userDataDir2 = await makeUserDataDir();
|
||||
const browserContext3 = await browserType.launchPersistentContext(userDataDir2, defaultBrowserOptions);
|
||||
const page3 = await browserContext3.newPage();
|
||||
await page3.goto(server.EMPTY_PAGE);
|
||||
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext3.close();
|
||||
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
describe('browserType.launchServer', function() {
|
||||
it('should return child_process instance', async ({browserType}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
expect(browserServer.process().pid).toBeGreaterThan(0);
|
||||
await browserServer.close();
|
||||
});
|
||||
it('should fire close event', async ({browserType}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
await Promise.all([
|
||||
new Promise(f => browserServer.on('close', f)),
|
||||
browserServer.close(),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.executablePath', function() {
|
||||
it('should work', async({browserType}) => {
|
||||
const executablePath = browserType.executablePath();
|
||||
expect(fs.existsSync(executablePath)).toBe(true);
|
||||
expect(fs.realpathSync(executablePath)).toBe(executablePath);
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.name', function() {
|
||||
it('should work', async({browserType}) => {
|
||||
if (WEBKIT)
|
||||
expect(browserType.name()).toBe('webkit');
|
||||
else if (FFOX)
|
||||
expect(browserType.name()).toBe('firefox');
|
||||
else if (CHROMIUM)
|
||||
expect(browserType.name()).toBe('chromium');
|
||||
else
|
||||
throw new Error('Unknown browser');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Top-level requires', function() {
|
||||
it('should require top-level Errors', async() => {
|
||||
const Errors = require(path.join(utils.projectRoot(), '/lib/errors.js'));
|
||||
expect(Errors.TimeoutError).toBe(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should require top-level DeviceDescriptors', async() => {
|
||||
const Devices = require(path.join(utils.projectRoot(), '/lib/deviceDescriptors.js')).DeviceDescriptors;
|
||||
expect(Devices['iPhone 6']).toBeTruthy();
|
||||
expect(Devices['iPhone 6']).toBe(playwright.devices['iPhone 6']);
|
||||
expect(Devices['iPhone 6']).toBe(require(playwrightPath).devices['iPhone 6']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Browser.isConnected', () => {
|
||||
it('should set the browser connected state', async ({browserType}) => {
|
||||
const browserServer = await browserType.launchServer({...defaultBrowserOptions });
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
expect(remote.isConnected()).toBe(true);
|
||||
await remote.close();
|
||||
expect(remote.isConnected()).toBe(false);
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
it('should throw when used after isConnected returns false', async({browserType}) => {
|
||||
const browserServer = await browserType.launchServer({...defaultBrowserOptions });
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const page = await remote.newPage();
|
||||
await Promise.all([
|
||||
browserServer.close(),
|
||||
new Promise(f => remote.once('disconnected', f)),
|
||||
]);
|
||||
expect(remote.isConnected()).toBe(false);
|
||||
const error = await page.evaluate('1 + 1').catch(e => e);
|
||||
expect(error.message).toContain('has been closed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Browser.disconnect', function() {
|
||||
it('should reject navigation when browser closes', async({browserType, server}) => {
|
||||
server.setRoute('/one-style.css', () => {});
|
||||
const browserServer = await browserType.launchServer({...defaultBrowserOptions });
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const page = await remote.newPage();
|
||||
const navigationPromise = page.goto(server.PREFIX + '/one-style.html', {timeout: 60000}).catch(e => e);
|
||||
await server.waitForRequest('/one-style.css');
|
||||
await remote.close();
|
||||
const error = await navigationPromise;
|
||||
expect(error.message).toContain('Navigation failed because browser has disconnected!');
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
it('should reject waitForSelector when browser closes', async({browserType, server}) => {
|
||||
server.setRoute('/empty.html', () => {});
|
||||
const browserServer = await browserType.launchServer({...defaultBrowserOptions });
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const page = await remote.newPage();
|
||||
const watchdog = page.waitForSelector('div', { timeout: 60000 }).catch(e => e);
|
||||
|
||||
// Make sure the previous waitForSelector has time to make it to the browser before we disconnect.
|
||||
await page.waitForSelector('body');
|
||||
|
||||
await remote.close();
|
||||
const error = await watchdog;
|
||||
expect(error.message).toContain('Protocol error');
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
it('should throw if used after disconnect', async({browserType}) => {
|
||||
const browserServer = await browserType.launchServer({...defaultBrowserOptions });
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const page = await remote.newPage();
|
||||
await remote.close();
|
||||
const error = await page.evaluate('1 + 1').catch(e => e);
|
||||
expect(error.message).toContain('has been closed');
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
it('should emit close events on pages and contexts', async({browserType}) => {
|
||||
const browserServer = await browserType.launchServer({...defaultBrowserOptions });
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const context = await remote.newContext();
|
||||
const page = await context.newPage();
|
||||
let pageClosed = false;
|
||||
page.on('close', e => pageClosed = true);
|
||||
await Promise.all([
|
||||
new Promise(f => context.on('close', f)),
|
||||
browserServer.close()
|
||||
]);
|
||||
expect(pageClosed).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Browser.close', function() {
|
||||
it('should terminate network waiters', async({browserType, server}) => {
|
||||
const browserServer = await browserType.launchServer({...defaultBrowserOptions });
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const newPage = await remote.newPage();
|
||||
const results = await Promise.all([
|
||||
newPage.waitForRequest(server.EMPTY_PAGE).catch(e => e),
|
||||
newPage.waitForResponse(server.EMPTY_PAGE).catch(e => e),
|
||||
browserServer.close()
|
||||
]);
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const message = results[i].message;
|
||||
expect(message).toContain('Target closed');
|
||||
expect(message).not.toContain('Timeout');
|
||||
}
|
||||
});
|
||||
it('should fire close event for all contexts', async({browserType}) => {
|
||||
const browser = await browserType.launch(defaultBrowserOptions);
|
||||
const context = await browser.newContext();
|
||||
let closed = false;
|
||||
context.on('close', () => closed = true);
|
||||
await browser.close();
|
||||
expect(closed).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.launch |webSocket| option', function() {
|
||||
it('should support the webSocket option', async({browserType}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const browserContext = await browser.newContext();
|
||||
expect(browserContext.pages().length).toBe(0);
|
||||
expect(browserServer.wsEndpoint()).not.toBe(null);
|
||||
const page = await browserContext.newPage();
|
||||
expect(await page.evaluate('11 * 11')).toBe(121);
|
||||
await page.close();
|
||||
await browser.close();
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
it('should fire "disconnected" when closing with webSocket', async({browserType}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
|
||||
browserServer.kill();
|
||||
await disconnectedEventPromise;
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.connect', function() {
|
||||
it.slow()('should be able to reconnect to a browser', async({browserType, server}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
{
|
||||
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const browserContext = await browser.newContext();
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await browser.close();
|
||||
}
|
||||
{
|
||||
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const browserContext = await browser.newContext();
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await browser.close();
|
||||
}
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.launchPersistentContext', function() {
|
||||
it('userDataDir option', async({browserType}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const options = Object.assign(defaultBrowserOptions);
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, options);
|
||||
// Open a page to make sure its functional.
|
||||
await browserContext.newPage();
|
||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||
await browserContext.close();
|
||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it.slow()('userDataDir option should restore state', async({browserType, server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => localStorage.hey = 'hello');
|
||||
await browserContext.close();
|
||||
|
||||
const browserContext2 = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page2 = await browserContext2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
expect(await page2.evaluate(() => localStorage.hey)).toBe('hello');
|
||||
await browserContext2.close();
|
||||
|
||||
const userDataDir2 = await makeUserDataDir();
|
||||
const browserContext3 = await browserType.launchPersistentContext(userDataDir2, defaultBrowserOptions);
|
||||
const page3 = await browserContext3.newPage();
|
||||
await page3.goto(server.EMPTY_PAGE);
|
||||
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('hello');
|
||||
await browserContext3.close();
|
||||
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
// See https://github.com/microsoft/playwright/issues/717
|
||||
it.slow().fail(WIN && CHROMIUM)('userDataDir option should restore cookies', async({browserType, server}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => document.cookie = 'doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT');
|
||||
await browserContext.close();
|
||||
|
||||
const browserContext2 = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page2 = await browserContext2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
expect(await page2.evaluate(() => document.cookie)).toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext2.close();
|
||||
|
||||
const userDataDir2 = await makeUserDataDir();
|
||||
const browserContext3 = await browserType.launchPersistentContext(userDataDir2, defaultBrowserOptions);
|
||||
const page3 = await browserContext3.newPage();
|
||||
await page3.goto(server.EMPTY_PAGE);
|
||||
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext3.close();
|
||||
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC} = require('./utils').testOptions(browserType);
|
||||
|
||||
function dimensions() {
|
||||
const rect = document.querySelector('textarea').getBoundingClientRect();
|
||||
return {
|
||||
|
|
@ -25,135 +27,129 @@ function dimensions() {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({FFOX, CHROMIUM, WEBKIT, MAC}) {
|
||||
|
||||
describe('Mouse', function() {
|
||||
it('should click the document', async({page, server}) => {
|
||||
await page.evaluate(() => {
|
||||
window.clickPromise = new Promise(resolve => {
|
||||
document.addEventListener('click', event => {
|
||||
resolve({
|
||||
type: event.type,
|
||||
detail: event.detail,
|
||||
clientX: event.clientX,
|
||||
clientY: event.clientY,
|
||||
isTrusted: event.isTrusted,
|
||||
button: event.button
|
||||
});
|
||||
describe('Mouse', function() {
|
||||
it('should click the document', async({page, server}) => {
|
||||
await page.evaluate(() => {
|
||||
window.clickPromise = new Promise(resolve => {
|
||||
document.addEventListener('click', event => {
|
||||
resolve({
|
||||
type: event.type,
|
||||
detail: event.detail,
|
||||
clientX: event.clientX,
|
||||
clientY: event.clientY,
|
||||
isTrusted: event.isTrusted,
|
||||
button: event.button
|
||||
});
|
||||
});
|
||||
});
|
||||
await page.mouse.click(50, 60);
|
||||
const event = await page.evaluate(() => window.clickPromise);
|
||||
expect(event.type).toBe('click');
|
||||
expect(event.detail).toBe(1);
|
||||
expect(event.clientX).toBe(50);
|
||||
expect(event.clientY).toBe(60);
|
||||
expect(event.isTrusted).toBe(true);
|
||||
expect(event.button).toBe(0);
|
||||
});
|
||||
it('should select the text with mouse', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.focus('textarea');
|
||||
const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
|
||||
await page.keyboard.type(text);
|
||||
// Firefox needs an extra frame here after typing or it will fail to set the scrollTop
|
||||
await page.evaluate(() => new Promise(requestAnimationFrame));
|
||||
await page.evaluate(() => document.querySelector('textarea').scrollTop = 0);
|
||||
const {x, y} = await page.evaluate(dimensions);
|
||||
await page.mouse.move(x + 2,y + 2);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(200,200);
|
||||
await page.mouse.up();
|
||||
expect(await page.evaluate(() => {
|
||||
const textarea = document.querySelector('textarea');
|
||||
return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
|
||||
})).toBe(text);
|
||||
});
|
||||
it('should trigger hover state', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||
await page.hover('#button-6');
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
||||
await page.hover('#button-2');
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-2');
|
||||
await page.hover('#button-91');
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-91');
|
||||
});
|
||||
it('should trigger hover state with removed window.Node', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||
await page.evaluate(() => delete window.Node);
|
||||
await page.hover('#button-6');
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
||||
});
|
||||
it('should set modifier keys on click', async({page, server}) => {
|
||||
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 (FFOX && !MAC)
|
||||
delete modifiers['Meta'];
|
||||
for (const modifier in modifiers) {
|
||||
await page.keyboard.down(modifier);
|
||||
await page.click('#button-3');
|
||||
if (!(await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier])))
|
||||
throw new Error(modifiers[modifier] + ' should be true');
|
||||
await page.keyboard.up(modifier);
|
||||
}
|
||||
await page.click('#button-3');
|
||||
for (const modifier in modifiers) {
|
||||
if ((await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier])))
|
||||
throw new Error(modifiers[modifier] + ' should be false');
|
||||
}
|
||||
});
|
||||
it('should tween mouse movement', async({page, server}) => {
|
||||
// The test becomes flaky on WebKit without next line.
|
||||
if (WEBKIT)
|
||||
await page.evaluate(() => new Promise(requestAnimationFrame));
|
||||
await page.mouse.move(100, 100);
|
||||
await page.evaluate(() => {
|
||||
window.result = [];
|
||||
document.addEventListener('mousemove', event => {
|
||||
window.result.push([event.clientX, event.clientY]);
|
||||
});
|
||||
});
|
||||
await page.mouse.move(200, 300, {steps: 5});
|
||||
expect(await page.evaluate('result')).toEqual([
|
||||
[120, 140],
|
||||
[140, 180],
|
||||
[160, 220],
|
||||
[180, 260],
|
||||
[200, 300]
|
||||
]);
|
||||
});
|
||||
it.skip(FFOX)('should work with mobile viewports and cross process navigations', async({browser, server}) => {
|
||||
// @see https://crbug.com/929806
|
||||
const context = await browser.newContext({ viewport: {width: 360, height: 640, isMobile: true} });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/mobile.html');
|
||||
await page.evaluate(() => {
|
||||
document.addEventListener('click', event => {
|
||||
window.result = {x: event.clientX, y: event.clientY};
|
||||
});
|
||||
});
|
||||
|
||||
await page.mouse.click(30, 40);
|
||||
|
||||
expect(await page.evaluate('result')).toEqual({x: 30, y: 40});
|
||||
await context.close();
|
||||
});
|
||||
xdescribe('Drag and Drop', function() {
|
||||
it('should work', async({server, page}) => {
|
||||
await page.goto(server.PREFIX + '/drag-n-drop.html');
|
||||
await page.hover('#source');
|
||||
await page.mouse.down();
|
||||
await page.hover('#target');
|
||||
await page.mouse.up();
|
||||
expect(await page.$eval('#target', target => target.contains(document.querySelector('#source')))).toBe(true, 'could not find source in target');
|
||||
})
|
||||
});
|
||||
await page.mouse.click(50, 60);
|
||||
const event = await page.evaluate(() => window.clickPromise);
|
||||
expect(event.type).toBe('click');
|
||||
expect(event.detail).toBe(1);
|
||||
expect(event.clientX).toBe(50);
|
||||
expect(event.clientY).toBe(60);
|
||||
expect(event.isTrusted).toBe(true);
|
||||
expect(event.button).toBe(0);
|
||||
});
|
||||
};
|
||||
it('should select the text with mouse', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.focus('textarea');
|
||||
const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
|
||||
await page.keyboard.type(text);
|
||||
// Firefox needs an extra frame here after typing or it will fail to set the scrollTop
|
||||
await page.evaluate(() => new Promise(requestAnimationFrame));
|
||||
await page.evaluate(() => document.querySelector('textarea').scrollTop = 0);
|
||||
const {x, y} = await page.evaluate(dimensions);
|
||||
await page.mouse.move(x + 2,y + 2);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(200,200);
|
||||
await page.mouse.up();
|
||||
expect(await page.evaluate(() => {
|
||||
const textarea = document.querySelector('textarea');
|
||||
return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
|
||||
})).toBe(text);
|
||||
});
|
||||
it('should trigger hover state', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||
await page.hover('#button-6');
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
||||
await page.hover('#button-2');
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-2');
|
||||
await page.hover('#button-91');
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-91');
|
||||
});
|
||||
it('should trigger hover state with removed window.Node', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||
await page.evaluate(() => delete window.Node);
|
||||
await page.hover('#button-6');
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
||||
});
|
||||
it('should set modifier keys on click', async({page, server}) => {
|
||||
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 (FFOX && !MAC)
|
||||
delete modifiers['Meta'];
|
||||
for (const modifier in modifiers) {
|
||||
await page.keyboard.down(modifier);
|
||||
await page.click('#button-3');
|
||||
if (!(await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier])))
|
||||
throw new Error(modifiers[modifier] + ' should be true');
|
||||
await page.keyboard.up(modifier);
|
||||
}
|
||||
await page.click('#button-3');
|
||||
for (const modifier in modifiers) {
|
||||
if ((await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier])))
|
||||
throw new Error(modifiers[modifier] + ' should be false');
|
||||
}
|
||||
});
|
||||
it('should tween mouse movement', async({page, server}) => {
|
||||
// The test becomes flaky on WebKit without next line.
|
||||
if (WEBKIT)
|
||||
await page.evaluate(() => new Promise(requestAnimationFrame));
|
||||
await page.mouse.move(100, 100);
|
||||
await page.evaluate(() => {
|
||||
window.result = [];
|
||||
document.addEventListener('mousemove', event => {
|
||||
window.result.push([event.clientX, event.clientY]);
|
||||
});
|
||||
});
|
||||
await page.mouse.move(200, 300, {steps: 5});
|
||||
expect(await page.evaluate('result')).toEqual([
|
||||
[120, 140],
|
||||
[140, 180],
|
||||
[160, 220],
|
||||
[180, 260],
|
||||
[200, 300]
|
||||
]);
|
||||
});
|
||||
it.skip(FFOX)('should work with mobile viewports and cross process navigations', async({browser, server}) => {
|
||||
// @see https://crbug.com/929806
|
||||
const context = await browser.newContext({ viewport: {width: 360, height: 640, isMobile: true} });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/mobile.html');
|
||||
await page.evaluate(() => {
|
||||
document.addEventListener('click', event => {
|
||||
window.result = {x: event.clientX, y: event.clientY};
|
||||
});
|
||||
});
|
||||
|
||||
await page.mouse.click(30, 40);
|
||||
|
||||
expect(await page.evaluate('result')).toEqual({x: 30, y: 40});
|
||||
await context.close();
|
||||
});
|
||||
xdescribe('Drag and Drop', function() {
|
||||
it('should work', async({server, page}) => {
|
||||
await page.goto(server.PREFIX + '/drag-n-drop.html');
|
||||
await page.hover('#source');
|
||||
await page.mouse.down();
|
||||
await page.hover('#target');
|
||||
await page.mouse.up();
|
||||
expect(await page.$eval('#target', target => target.contains(document.querySelector('#source')))).toBe(true, 'could not find source in target');
|
||||
})
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,101 +15,95 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, defaultBrowserOptions} = require('./utils').testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {TestSuite}
|
||||
*/
|
||||
module.exports.describe = function({defaultBrowserOptions, browserType, FFOX, CHROMIUM, WEBKIT}) {
|
||||
describe('BrowserContext', function() {
|
||||
it('should work across sessions', async ({browserType}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const browser1 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
expect(browser1.contexts().length).toBe(0);
|
||||
await browser1.newContext();
|
||||
expect(browser1.contexts().length).toBe(1);
|
||||
|
||||
describe('BrowserContext', function() {
|
||||
it('should work across sessions', async () => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const browser1 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
expect(browser1.contexts().length).toBe(0);
|
||||
await browser1.newContext();
|
||||
expect(browser1.contexts().length).toBe(1);
|
||||
const browser2 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
expect(browser2.contexts().length).toBe(0);
|
||||
await browser2.newContext();
|
||||
expect(browser2.contexts().length).toBe(1);
|
||||
|
||||
const browser2 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
expect(browser2.contexts().length).toBe(0);
|
||||
await browser2.newContext();
|
||||
expect(browser2.contexts().length).toBe(1);
|
||||
expect(browser1.contexts().length).toBe(1);
|
||||
|
||||
expect(browser1.contexts().length).toBe(1);
|
||||
await browser1.close();
|
||||
await browser2.close();
|
||||
|
||||
await browser1.close();
|
||||
await browser2.close();
|
||||
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Browser.Events.disconnected', function() {
|
||||
it.slow()('should be emitted when: browser gets closed, disconnected or underlying websocket gets closed', async () => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const originalBrowser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const wsEndpoint = browserServer.wsEndpoint();
|
||||
const remoteBrowser1 = await browserType.connect({ wsEndpoint });
|
||||
const remoteBrowser2 = await browserType.connect({ wsEndpoint });
|
||||
describe('Browser.Events.disconnected', function() {
|
||||
it.slow()('should be emitted when: browser gets closed, disconnected or underlying websocket gets closed', async ({browserType}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const originalBrowser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const wsEndpoint = browserServer.wsEndpoint();
|
||||
const remoteBrowser1 = await browserType.connect({ wsEndpoint });
|
||||
const remoteBrowser2 = await browserType.connect({ wsEndpoint });
|
||||
|
||||
let disconnectedOriginal = 0;
|
||||
let disconnectedRemote1 = 0;
|
||||
let disconnectedRemote2 = 0;
|
||||
originalBrowser.on('disconnected', () => ++disconnectedOriginal);
|
||||
remoteBrowser1.on('disconnected', () => ++disconnectedRemote1);
|
||||
remoteBrowser2.on('disconnected', () => ++disconnectedRemote2);
|
||||
let disconnectedOriginal = 0;
|
||||
let disconnectedRemote1 = 0;
|
||||
let disconnectedRemote2 = 0;
|
||||
originalBrowser.on('disconnected', () => ++disconnectedOriginal);
|
||||
remoteBrowser1.on('disconnected', () => ++disconnectedRemote1);
|
||||
remoteBrowser2.on('disconnected', () => ++disconnectedRemote2);
|
||||
|
||||
await Promise.all([
|
||||
new Promise(f => remoteBrowser2.on('disconnected', f)),
|
||||
remoteBrowser2.close(),
|
||||
]);
|
||||
await Promise.all([
|
||||
new Promise(f => remoteBrowser2.on('disconnected', f)),
|
||||
remoteBrowser2.close(),
|
||||
]);
|
||||
|
||||
expect(disconnectedOriginal).toBe(0);
|
||||
expect(disconnectedRemote1).toBe(0);
|
||||
expect(disconnectedRemote2).toBe(1);
|
||||
expect(disconnectedOriginal).toBe(0);
|
||||
expect(disconnectedRemote1).toBe(0);
|
||||
expect(disconnectedRemote2).toBe(1);
|
||||
|
||||
await Promise.all([
|
||||
new Promise(f => remoteBrowser1.on('disconnected', f)),
|
||||
new Promise(f => originalBrowser.on('disconnected', f)),
|
||||
browserServer.close(),
|
||||
]);
|
||||
await Promise.all([
|
||||
new Promise(f => remoteBrowser1.on('disconnected', f)),
|
||||
new Promise(f => originalBrowser.on('disconnected', f)),
|
||||
browserServer.close(),
|
||||
]);
|
||||
|
||||
expect(disconnectedOriginal).toBe(1);
|
||||
expect(disconnectedRemote1).toBe(1);
|
||||
expect(disconnectedRemote2).toBe(1);
|
||||
});
|
||||
expect(disconnectedOriginal).toBe(1);
|
||||
expect(disconnectedRemote1).toBe(1);
|
||||
expect(disconnectedRemote2).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.connect', function() {
|
||||
it('should be able to connect multiple times to the same browser', async({server}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const browser1 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const browser2 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const page1 = await browser1.newPage();
|
||||
expect(await page1.evaluate(() => 7 * 8)).toBe(56);
|
||||
browser1.close();
|
||||
describe('browserType.connect', function() {
|
||||
it('should be able to connect multiple times to the same browser', async({browserType}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
const browser1 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const browser2 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
const page1 = await browser1.newPage();
|
||||
expect(await page1.evaluate(() => 7 * 8)).toBe(56);
|
||||
browser1.close();
|
||||
|
||||
const page2 = await browser2.newPage();
|
||||
expect(await page2.evaluate(() => 7 * 6)).toBe(42, 'original browser should still work');
|
||||
await browser2.close();
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
it('should not be able to close remote browser', async() => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
{
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
await remote.newContext();
|
||||
await remote.close();
|
||||
}
|
||||
{
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
await remote.newContext();
|
||||
await remote.close();
|
||||
}
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
const page2 = await browser2.newPage();
|
||||
expect(await page2.evaluate(() => 7 * 6)).toBe(42, 'original browser should still work');
|
||||
await browser2.close();
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
};
|
||||
it('should not be able to close remote browser', async({browserType}) => {
|
||||
const browserServer = await browserType.launchServer(defaultBrowserOptions);
|
||||
{
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
await remote.newContext();
|
||||
await remote.close();
|
||||
}
|
||||
{
|
||||
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||
await remote.newContext();
|
||||
await remote.close();
|
||||
}
|
||||
await browserServer._checkLeaks();
|
||||
await browserServer.close();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -18,355 +18,350 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC, WIN} = utils.testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({MAC, WIN, FFOX, CHROMIUM, WEBKIT}) {
|
||||
describe('Page.Events.Request', function() {
|
||||
it('should fire for navigation requests', async({page, server}) => {
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(requests.length).toBe(1);
|
||||
});
|
||||
it('should fire for iframes', async({page, server}) => {
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
expect(requests.length).toBe(2);
|
||||
});
|
||||
it('should fire for fetches', async({page, server}) => {
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => fetch('/empty.html'));
|
||||
expect(requests.length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.Events.Request', function() {
|
||||
it('should fire for navigation requests', async({page, server}) => {
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(requests.length).toBe(1);
|
||||
describe('Request.frame', function() {
|
||||
it('should work for main frame navigation request', async({page, server}) => {
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(requests.length).toBe(1);
|
||||
expect(requests[0].frame()).toBe(page.mainFrame());
|
||||
});
|
||||
it('should work for subframe navigation request', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
expect(requests.length).toBe(1);
|
||||
expect(requests[0].frame()).toBe(page.frames()[1]);
|
||||
});
|
||||
it('should work for fetch requests', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.evaluate(() => fetch('/digits/1.png'));
|
||||
requests = requests.filter(request => !request.url().includes('favicon'));
|
||||
expect(requests.length).toBe(1);
|
||||
expect(requests[0].frame()).toBe(page.mainFrame());
|
||||
});
|
||||
});
|
||||
|
||||
describe('Request.headers', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
if (CHROMIUM)
|
||||
expect(response.request().headers()['user-agent']).toContain('Chrome');
|
||||
else if (FFOX)
|
||||
expect(response.request().headers()['user-agent']).toContain('Firefox');
|
||||
else if (WEBKIT)
|
||||
expect(response.request().headers()['user-agent']).toContain('WebKit');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Response.headers', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('foo', 'bar');
|
||||
res.end();
|
||||
});
|
||||
it('should fire for iframes', async({page, server}) => {
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
expect(requests.length).toBe(2);
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.headers()['foo']).toBe('bar');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Request.postData', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
server.setRoute('/post', (req, res) => res.end());
|
||||
let request = null;
|
||||
page.on('request', r => request = r);
|
||||
await page.evaluate(() => fetch('./post', { method: 'POST', body: JSON.stringify({foo: 'bar'})}));
|
||||
expect(request).toBeTruthy();
|
||||
expect(request.postData()).toBe('{"foo":"bar"}');
|
||||
});
|
||||
it('should be |undefined| when there is no post data', async({page, server}) => {
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.request().postData()).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Response.text', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const response = await page.goto(server.PREFIX + '/simple.json');
|
||||
expect(await response.text()).toBe('{"foo": "bar"}\n');
|
||||
});
|
||||
it('should return uncompressed text', async({page, server}) => {
|
||||
server.enableGzip('/simple.json');
|
||||
const response = await page.goto(server.PREFIX + '/simple.json');
|
||||
expect(response.headers()['content-encoding']).toBe('gzip');
|
||||
expect(await response.text()).toBe('{"foo": "bar"}\n');
|
||||
});
|
||||
it('should throw when requesting body of redirected response', async({page, server}) => {
|
||||
server.setRedirect('/foo.html', '/empty.html');
|
||||
const response = await page.goto(server.PREFIX + '/foo.html');
|
||||
const redirectedFrom = response.request().redirectedFrom();
|
||||
expect(redirectedFrom).toBeTruthy();
|
||||
const redirected = await redirectedFrom.response();
|
||||
expect(redirected.status()).toBe(302);
|
||||
let error = null;
|
||||
await redirected.text().catch(e => error = e);
|
||||
expect(error.message).toContain('Response body is unavailable for redirect responses');
|
||||
});
|
||||
it('should wait until response completes', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
// Setup server to trap request.
|
||||
let serverResponse = null;
|
||||
server.setRoute('/get', (req, res) => {
|
||||
serverResponse = res;
|
||||
// In Firefox, |fetch| will be hanging until it receives |Content-Type| header
|
||||
// from server.
|
||||
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
||||
res.write('hello ');
|
||||
});
|
||||
it('should fire for fetches', async({page, server}) => {
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => fetch('/empty.html'));
|
||||
expect(requests.length).toBe(2);
|
||||
// Setup page to trap response.
|
||||
let requestFinished = false;
|
||||
page.on('requestfinished', r => requestFinished = requestFinished || r.url().includes('/get'));
|
||||
// send request and wait for server response
|
||||
const [pageResponse] = await Promise.all([
|
||||
page.waitForEvent('response'),
|
||||
page.evaluate(() => fetch('./get', { method: 'GET'})),
|
||||
server.waitForRequest('/get'),
|
||||
]);
|
||||
|
||||
expect(serverResponse).toBeTruthy();
|
||||
expect(pageResponse).toBeTruthy();
|
||||
expect(pageResponse.status()).toBe(200);
|
||||
expect(requestFinished).toBe(false);
|
||||
|
||||
const responseText = pageResponse.text();
|
||||
// Write part of the response and wait for it to be flushed.
|
||||
await new Promise(x => serverResponse.write('wor', x));
|
||||
// Finish response.
|
||||
await new Promise(x => serverResponse.end('ld!', x));
|
||||
expect(await responseText).toBe('hello world!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Response.json', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const response = await page.goto(server.PREFIX + '/simple.json');
|
||||
expect(await response.json()).toEqual({foo: 'bar'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Response.body', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const response = await page.goto(server.PREFIX + '/pptr.png');
|
||||
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
|
||||
const responseBuffer = await response.body();
|
||||
expect(responseBuffer.equals(imageBuffer)).toBe(true);
|
||||
});
|
||||
it('should work with compression', async({page, server}) => {
|
||||
server.enableGzip('/pptr.png');
|
||||
const response = await page.goto(server.PREFIX + '/pptr.png');
|
||||
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
|
||||
const responseBuffer = await response.body();
|
||||
expect(responseBuffer.equals(imageBuffer)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Response.statusText', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
server.setRoute('/cool', (req, res) => {
|
||||
res.writeHead(200, 'cool!');
|
||||
res.end();
|
||||
});
|
||||
const response = await page.goto(server.PREFIX + '/cool');
|
||||
expect(response.statusText()).toBe('cool!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Network Events', function() {
|
||||
it('Page.Events.Request', async({page, server}) => {
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(requests.length).toBe(1);
|
||||
expect(requests[0].url()).toBe(server.EMPTY_PAGE);
|
||||
expect(requests[0].resourceType()).toBe('document');
|
||||
expect(requests[0].method()).toBe('GET');
|
||||
expect(await requests[0].response()).toBeTruthy();
|
||||
expect(requests[0].frame() === page.mainFrame()).toBe(true);
|
||||
expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE);
|
||||
});
|
||||
it('Page.Events.Response', async({page, server}) => {
|
||||
const responses = [];
|
||||
page.on('response', response => responses.push(response));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(responses.length).toBe(1);
|
||||
expect(responses[0].url()).toBe(server.EMPTY_PAGE);
|
||||
expect(responses[0].status()).toBe(200);
|
||||
expect(responses[0].ok()).toBe(true);
|
||||
expect(responses[0].request()).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('Request.frame', function() {
|
||||
it('should work for main frame navigation request', async({page, server}) => {
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(requests.length).toBe(1);
|
||||
expect(requests[0].frame()).toBe(page.mainFrame());
|
||||
});
|
||||
it('should work for subframe navigation request', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
expect(requests.length).toBe(1);
|
||||
expect(requests[0].frame()).toBe(page.frames()[1]);
|
||||
});
|
||||
it('should work for fetch requests', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.evaluate(() => fetch('/digits/1.png'));
|
||||
requests = requests.filter(request => !request.url().includes('favicon'));
|
||||
expect(requests.length).toBe(1);
|
||||
expect(requests[0].frame()).toBe(page.mainFrame());
|
||||
it.fail(FFOX)('Page.Events.RequestFailed', async({page, server}) => {
|
||||
server.setRoute('/one-style.css', (req, res) => {
|
||||
req.socket.write('deadbeef');
|
||||
req.socket.end();
|
||||
});
|
||||
const failedRequests = [];
|
||||
page.on('requestfailed', request => failedRequests.push(request));
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
expect(failedRequests.length).toBe(1);
|
||||
expect(failedRequests[0].url()).toContain('one-style.css');
|
||||
expect(await failedRequests[0].response()).toBe(null);
|
||||
expect(failedRequests[0].resourceType()).toBe('stylesheet');
|
||||
if (CHROMIUM) {
|
||||
expect(failedRequests[0].failure().errorText).toBe('net::ERR_INVALID_HTTP_RESPONSE');
|
||||
} else if (WEBKIT) {
|
||||
if (MAC)
|
||||
expect(failedRequests[0].failure().errorText).toBe('The network connection was lost.');
|
||||
else if (WIN)
|
||||
expect(failedRequests[0].failure().errorText).toBe('Unsupported protocol');
|
||||
else
|
||||
expect(failedRequests[0].failure().errorText).toBe('Message Corrupt');
|
||||
} else {
|
||||
expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE');
|
||||
}
|
||||
expect(failedRequests[0].frame()).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('Request.headers', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
if (CHROMIUM)
|
||||
expect(response.request().headers()['user-agent']).toContain('Chrome');
|
||||
else if (FFOX)
|
||||
expect(response.request().headers()['user-agent']).toContain('Firefox');
|
||||
else if (WEBKIT)
|
||||
expect(response.request().headers()['user-agent']).toContain('WebKit');
|
||||
});
|
||||
it('Page.Events.RequestFinished', async({page, server}) => {
|
||||
const [response] = await Promise.all([
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
page.waitForEvent('requestfinished')
|
||||
]);
|
||||
const request = response.request();
|
||||
expect(request.url()).toBe(server.EMPTY_PAGE);
|
||||
expect(await request.response()).toBeTruthy();
|
||||
expect(request.frame() === page.mainFrame()).toBe(true);
|
||||
expect(request.frame().url()).toBe(server.EMPTY_PAGE);
|
||||
});
|
||||
|
||||
describe('Response.headers', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('foo', 'bar');
|
||||
res.end();
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.headers()['foo']).toBe('bar');
|
||||
});
|
||||
it('should fire events in proper order', async({page, server}) => {
|
||||
const events = [];
|
||||
page.on('request', request => events.push('request'));
|
||||
page.on('response', response => events.push('response'));
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
await response.finished();
|
||||
events.push('requestfinished')
|
||||
expect(events).toEqual(['request', 'response', 'requestfinished']);
|
||||
});
|
||||
|
||||
describe('Request.postData', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
server.setRoute('/post', (req, res) => res.end());
|
||||
let request = null;
|
||||
page.on('request', r => request = r);
|
||||
await page.evaluate(() => fetch('./post', { method: 'POST', body: JSON.stringify({foo: 'bar'})}));
|
||||
expect(request).toBeTruthy();
|
||||
expect(request.postData()).toBe('{"foo":"bar"}');
|
||||
});
|
||||
it('should be |undefined| when there is no post data', async({page, server}) => {
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.request().postData()).toBe(null);
|
||||
});
|
||||
it('should support redirects', async({page, server}) => {
|
||||
const events = [];
|
||||
page.on('request', request => events.push(`${request.method()} ${request.url()}`));
|
||||
page.on('response', response => events.push(`${response.status()} ${response.url()}`));
|
||||
page.on('requestfinished', request => events.push(`DONE ${request.url()}`));
|
||||
page.on('requestfailed', request => events.push(`FAIL ${request.url()}`));
|
||||
server.setRedirect('/foo.html', '/empty.html');
|
||||
const FOO_URL = server.PREFIX + '/foo.html';
|
||||
const response = await page.goto(FOO_URL);
|
||||
await response.finished();
|
||||
expect(events).toEqual([
|
||||
`GET ${FOO_URL}`,
|
||||
`302 ${FOO_URL}`,
|
||||
`DONE ${FOO_URL}`,
|
||||
`GET ${server.EMPTY_PAGE}`,
|
||||
`200 ${server.EMPTY_PAGE}`,
|
||||
`DONE ${server.EMPTY_PAGE}`
|
||||
]);
|
||||
const redirectedFrom = response.request().redirectedFrom();
|
||||
expect(redirectedFrom.url()).toContain('/foo.html');
|
||||
expect(redirectedFrom.redirectedFrom()).toBe(null);
|
||||
expect(redirectedFrom.redirectedTo()).toBe(response.request());
|
||||
});
|
||||
});
|
||||
|
||||
describe('Response.text', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const response = await page.goto(server.PREFIX + '/simple.json');
|
||||
expect(await response.text()).toBe('{"foo": "bar"}\n');
|
||||
});
|
||||
it('should return uncompressed text', async({page, server}) => {
|
||||
server.enableGzip('/simple.json');
|
||||
const response = await page.goto(server.PREFIX + '/simple.json');
|
||||
expect(response.headers()['content-encoding']).toBe('gzip');
|
||||
expect(await response.text()).toBe('{"foo": "bar"}\n');
|
||||
});
|
||||
it('should throw when requesting body of redirected response', async({page, server}) => {
|
||||
server.setRedirect('/foo.html', '/empty.html');
|
||||
const response = await page.goto(server.PREFIX + '/foo.html');
|
||||
const redirectedFrom = response.request().redirectedFrom();
|
||||
expect(redirectedFrom).toBeTruthy();
|
||||
const redirected = await redirectedFrom.response();
|
||||
expect(redirected.status()).toBe(302);
|
||||
let error = null;
|
||||
await redirected.text().catch(e => error = e);
|
||||
expect(error.message).toContain('Response body is unavailable for redirect responses');
|
||||
});
|
||||
it('should wait until response completes', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
// Setup server to trap request.
|
||||
let serverResponse = null;
|
||||
server.setRoute('/get', (req, res) => {
|
||||
serverResponse = res;
|
||||
// In Firefox, |fetch| will be hanging until it receives |Content-Type| header
|
||||
// from server.
|
||||
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
||||
res.write('hello ');
|
||||
});
|
||||
// Setup page to trap response.
|
||||
let requestFinished = false;
|
||||
page.on('requestfinished', r => requestFinished = requestFinished || r.url().includes('/get'));
|
||||
// send request and wait for server response
|
||||
const [pageResponse] = await Promise.all([
|
||||
page.waitForEvent('response'),
|
||||
page.evaluate(() => fetch('./get', { method: 'GET'})),
|
||||
server.waitForRequest('/get'),
|
||||
]);
|
||||
|
||||
expect(serverResponse).toBeTruthy();
|
||||
expect(pageResponse).toBeTruthy();
|
||||
expect(pageResponse.status()).toBe(200);
|
||||
expect(requestFinished).toBe(false);
|
||||
|
||||
const responseText = pageResponse.text();
|
||||
// Write part of the response and wait for it to be flushed.
|
||||
await new Promise(x => serverResponse.write('wor', x));
|
||||
// Finish response.
|
||||
await new Promise(x => serverResponse.end('ld!', x));
|
||||
expect(await responseText).toBe('hello world!');
|
||||
});
|
||||
describe('Request.isNavigationRequest', () => {
|
||||
it('should work', async({page, server}) => {
|
||||
const requests = new Map();
|
||||
page.on('request', request => requests.set(request.url().split('/').pop(), request));
|
||||
server.setRedirect('/rrredirect', '/frames/one-frame.html');
|
||||
await page.goto(server.PREFIX + '/rrredirect');
|
||||
expect(requests.get('rrredirect').isNavigationRequest()).toBe(true);
|
||||
expect(requests.get('one-frame.html').isNavigationRequest()).toBe(true);
|
||||
expect(requests.get('frame.html').isNavigationRequest()).toBe(true);
|
||||
expect(requests.get('script.js').isNavigationRequest()).toBe(false);
|
||||
expect(requests.get('style.css').isNavigationRequest()).toBe(false);
|
||||
});
|
||||
|
||||
describe('Response.json', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const response = await page.goto(server.PREFIX + '/simple.json');
|
||||
expect(await response.json()).toEqual({foo: 'bar'});
|
||||
});
|
||||
it('should work when navigating to image', async({page, server}) => {
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.goto(server.PREFIX + '/pptr.png');
|
||||
expect(requests[0].isNavigationRequest()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Response.body', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
const response = await page.goto(server.PREFIX + '/pptr.png');
|
||||
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
|
||||
const responseBuffer = await response.body();
|
||||
expect(responseBuffer.equals(imageBuffer)).toBe(true);
|
||||
});
|
||||
it('should work with compression', async({page, server}) => {
|
||||
server.enableGzip('/pptr.png');
|
||||
const response = await page.goto(server.PREFIX + '/pptr.png');
|
||||
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
|
||||
const responseBuffer = await response.body();
|
||||
expect(responseBuffer.equals(imageBuffer)).toBe(true);
|
||||
describe('Page.setExtraHTTPHeaders', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.setExtraHTTPHeaders({
|
||||
foo: 'bar'
|
||||
});
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['foo']).toBe('bar');
|
||||
});
|
||||
|
||||
describe('Response.statusText', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
server.setRoute('/cool', (req, res) => {
|
||||
res.writeHead(200, 'cool!');
|
||||
res.end();
|
||||
});
|
||||
const response = await page.goto(server.PREFIX + '/cool');
|
||||
expect(response.statusText()).toBe('cool!');
|
||||
it('should work with extra headers from browser context', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.setExtraHTTPHeaders({
|
||||
'foo': 'bar',
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
await context.close();
|
||||
expect(request.headers['foo']).toBe('bar');
|
||||
});
|
||||
|
||||
describe('Network Events', function() {
|
||||
it('Page.Events.Request', async({page, server}) => {
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(requests.length).toBe(1);
|
||||
expect(requests[0].url()).toBe(server.EMPTY_PAGE);
|
||||
expect(requests[0].resourceType()).toBe('document');
|
||||
expect(requests[0].method()).toBe('GET');
|
||||
expect(await requests[0].response()).toBeTruthy();
|
||||
expect(requests[0].frame() === page.mainFrame()).toBe(true);
|
||||
expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE);
|
||||
it('should override extra headers from browser context', async({browser, server}) => {
|
||||
const context = await browser.newContext({
|
||||
extraHTTPHeaders: { 'fOo': 'bAr', 'baR': 'foO' },
|
||||
});
|
||||
it('Page.Events.Response', async({page, server}) => {
|
||||
const responses = [];
|
||||
page.on('response', response => responses.push(response));
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(responses.length).toBe(1);
|
||||
expect(responses[0].url()).toBe(server.EMPTY_PAGE);
|
||||
expect(responses[0].status()).toBe(200);
|
||||
expect(responses[0].ok()).toBe(true);
|
||||
expect(responses[0].request()).toBeTruthy();
|
||||
});
|
||||
|
||||
it.fail(FFOX)('Page.Events.RequestFailed', async({page, server}) => {
|
||||
server.setRoute('/one-style.css', (req, res) => {
|
||||
req.socket.write('deadbeef');
|
||||
req.socket.end();
|
||||
});
|
||||
const failedRequests = [];
|
||||
page.on('requestfailed', request => failedRequests.push(request));
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
expect(failedRequests.length).toBe(1);
|
||||
expect(failedRequests[0].url()).toContain('one-style.css');
|
||||
expect(await failedRequests[0].response()).toBe(null);
|
||||
expect(failedRequests[0].resourceType()).toBe('stylesheet');
|
||||
if (CHROMIUM) {
|
||||
expect(failedRequests[0].failure().errorText).toBe('net::ERR_INVALID_HTTP_RESPONSE');
|
||||
} else if (WEBKIT) {
|
||||
if (MAC)
|
||||
expect(failedRequests[0].failure().errorText).toBe('The network connection was lost.');
|
||||
else if (WIN)
|
||||
expect(failedRequests[0].failure().errorText).toBe('Unsupported protocol');
|
||||
else
|
||||
expect(failedRequests[0].failure().errorText).toBe('Message Corrupt');
|
||||
} else {
|
||||
expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE');
|
||||
}
|
||||
expect(failedRequests[0].frame()).toBeTruthy();
|
||||
});
|
||||
it('Page.Events.RequestFinished', async({page, server}) => {
|
||||
const [response] = await Promise.all([
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
page.waitForEvent('requestfinished')
|
||||
]);
|
||||
const request = response.request();
|
||||
expect(request.url()).toBe(server.EMPTY_PAGE);
|
||||
expect(await request.response()).toBeTruthy();
|
||||
expect(request.frame() === page.mainFrame()).toBe(true);
|
||||
expect(request.frame().url()).toBe(server.EMPTY_PAGE);
|
||||
});
|
||||
it('should fire events in proper order', async({page, server}) => {
|
||||
const events = [];
|
||||
page.on('request', request => events.push('request'));
|
||||
page.on('response', response => events.push('response'));
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
await response.finished();
|
||||
events.push('requestfinished')
|
||||
expect(events).toEqual(['request', 'response', 'requestfinished']);
|
||||
});
|
||||
it('should support redirects', async({page, server}) => {
|
||||
const events = [];
|
||||
page.on('request', request => events.push(`${request.method()} ${request.url()}`));
|
||||
page.on('response', response => events.push(`${response.status()} ${response.url()}`));
|
||||
page.on('requestfinished', request => events.push(`DONE ${request.url()}`));
|
||||
page.on('requestfailed', request => events.push(`FAIL ${request.url()}`));
|
||||
server.setRedirect('/foo.html', '/empty.html');
|
||||
const FOO_URL = server.PREFIX + '/foo.html';
|
||||
const response = await page.goto(FOO_URL);
|
||||
await response.finished();
|
||||
expect(events).toEqual([
|
||||
`GET ${FOO_URL}`,
|
||||
`302 ${FOO_URL}`,
|
||||
`DONE ${FOO_URL}`,
|
||||
`GET ${server.EMPTY_PAGE}`,
|
||||
`200 ${server.EMPTY_PAGE}`,
|
||||
`DONE ${server.EMPTY_PAGE}`
|
||||
]);
|
||||
const redirectedFrom = response.request().redirectedFrom();
|
||||
expect(redirectedFrom.url()).toContain('/foo.html');
|
||||
expect(redirectedFrom.redirectedFrom()).toBe(null);
|
||||
expect(redirectedFrom.redirectedTo()).toBe(response.request());
|
||||
const page = await context.newPage();
|
||||
await page.setExtraHTTPHeaders({
|
||||
'Foo': 'Bar'
|
||||
});
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
await context.close();
|
||||
expect(request.headers['foo']).toBe('Bar');
|
||||
expect(request.headers['bar']).toBe('foO');
|
||||
});
|
||||
|
||||
describe('Request.isNavigationRequest', () => {
|
||||
it('should work', async({page, server}) => {
|
||||
const requests = new Map();
|
||||
page.on('request', request => requests.set(request.url().split('/').pop(), request));
|
||||
server.setRedirect('/rrredirect', '/frames/one-frame.html');
|
||||
await page.goto(server.PREFIX + '/rrredirect');
|
||||
expect(requests.get('rrredirect').isNavigationRequest()).toBe(true);
|
||||
expect(requests.get('one-frame.html').isNavigationRequest()).toBe(true);
|
||||
expect(requests.get('frame.html').isNavigationRequest()).toBe(true);
|
||||
expect(requests.get('script.js').isNavigationRequest()).toBe(false);
|
||||
expect(requests.get('style.css').isNavigationRequest()).toBe(false);
|
||||
});
|
||||
it('should work when navigating to image', async({page, server}) => {
|
||||
const requests = [];
|
||||
page.on('request', request => requests.push(request));
|
||||
await page.goto(server.PREFIX + '/pptr.png');
|
||||
expect(requests[0].isNavigationRequest()).toBe(true);
|
||||
});
|
||||
it('should throw for non-string header values', async({page, server}) => {
|
||||
let error = null;
|
||||
try {
|
||||
await page.setExtraHTTPHeaders({ 'foo': 1 });
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toBe('Expected value of header "foo" to be String, but "number" is found.');
|
||||
});
|
||||
|
||||
describe('Page.setExtraHTTPHeaders', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.setExtraHTTPHeaders({
|
||||
foo: 'bar'
|
||||
});
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['foo']).toBe('bar');
|
||||
});
|
||||
it('should work with extra headers from browser context', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.setExtraHTTPHeaders({
|
||||
'foo': 'bar',
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
await context.close();
|
||||
expect(request.headers['foo']).toBe('bar');
|
||||
});
|
||||
it('should override extra headers from browser context', async({browser, server}) => {
|
||||
const context = await browser.newContext({
|
||||
extraHTTPHeaders: { 'fOo': 'bAr', 'baR': 'foO' },
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.setExtraHTTPHeaders({
|
||||
'Foo': 'Bar'
|
||||
});
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
await context.close();
|
||||
expect(request.headers['foo']).toBe('Bar');
|
||||
expect(request.headers['bar']).toBe('foO');
|
||||
});
|
||||
it('should throw for non-string header values', async({page, server}) => {
|
||||
let error = null;
|
||||
try {
|
||||
await page.setExtraHTTPHeaders({ 'foo': 1 });
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toBe('Expected value of header "foo" to be String, but "number" is found.');
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
|||
2083
test/page.spec.js
2083
test/page.spec.js
File diff suppressed because it is too large
Load diff
|
|
@ -15,112 +15,108 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({WEBKIT, FFOX}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
|
||||
// Permissions API is not implemented in WebKit (see https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API)
|
||||
describe.skip(WEBKIT)('Permissions', function() {
|
||||
function getPermission(page, name) {
|
||||
return page.evaluate(name => navigator.permissions.query({name}).then(result => result.state), name);
|
||||
}
|
||||
// Permissions API is not implemented in WebKit (see https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API)
|
||||
describe.skip(WEBKIT)('Permissions', function() {
|
||||
function getPermission(page, name) {
|
||||
return page.evaluate(name => navigator.permissions.query({name}).then(result => result.state), name);
|
||||
}
|
||||
|
||||
it('should be prompt by default', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
});
|
||||
it('should deny permission when not listed', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions([], { origin: server.EMPTY_PAGE });
|
||||
expect(await getPermission(page, 'geolocation')).toBe('denied');
|
||||
});
|
||||
it('should fail when bad permission is given', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let error = {};
|
||||
await context.grantPermissions(['foo'], { origin: server.EMPTY_PAGE }).catch(e => error = e);
|
||||
expect(error.message).toBe('Unknown permission: foo');
|
||||
});
|
||||
it('should grant geolocation permission when listed', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation'], { origin: server.EMPTY_PAGE });
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
});
|
||||
it('should grant notifications permission when listed', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['notifications'], { origin: server.EMPTY_PAGE });
|
||||
expect(await getPermission(page, 'notifications')).toBe('granted');
|
||||
});
|
||||
it('should accumulate when adding', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation']);
|
||||
await context.grantPermissions(['notifications']);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
expect(await getPermission(page, 'notifications')).toBe('granted');
|
||||
});
|
||||
it('should clear permissions', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation']);
|
||||
await context.clearPermissions();
|
||||
await context.grantPermissions(['notifications']);
|
||||
expect(await getPermission(page, 'geolocation')).not.toBe('granted');
|
||||
expect(await getPermission(page, 'notifications')).toBe('granted');
|
||||
});
|
||||
it('should grant permission when listed for all domains', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation']);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
});
|
||||
it('should grant permission when creating context', async({server, browser}) => {
|
||||
const context = await browser.newContext({ permissions: ['geolocation'] });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
await context.close();
|
||||
});
|
||||
it('should reset permissions', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation'], { origin: server.EMPTY_PAGE });
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
await context.clearPermissions();
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
});
|
||||
it('should trigger permission onchange', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
window['events'] = [];
|
||||
return navigator.permissions.query({name: 'geolocation'}).then(function(result) {
|
||||
window['events'].push(result.state);
|
||||
result.onchange = function() {
|
||||
window['events'].push(result.state);
|
||||
};
|
||||
});
|
||||
});
|
||||
expect(await page.evaluate(() => window['events'])).toEqual(['prompt']);
|
||||
await context.grantPermissions([], { origin: server.EMPTY_PAGE });
|
||||
expect(await page.evaluate(() => window['events'])).toEqual(['prompt', 'denied']);
|
||||
await context.grantPermissions(['geolocation'], { origin: server.EMPTY_PAGE });
|
||||
expect(await page.evaluate(() => window['events'])).toEqual(['prompt', 'denied', 'granted']);
|
||||
await context.clearPermissions();
|
||||
expect(await page.evaluate(() => window['events'])).toEqual(['prompt', 'denied', 'granted', 'prompt']);
|
||||
});
|
||||
it('should isolate permissions between browser contexs', async({page, server, context, browser}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const otherContext = await browser.newContext();
|
||||
const otherPage = await otherContext.newPage();
|
||||
await otherPage.goto(server.EMPTY_PAGE);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
expect(await getPermission(otherPage, 'geolocation')).toBe('prompt');
|
||||
|
||||
await context.grantPermissions([], { origin: server.EMPTY_PAGE });
|
||||
await otherContext.grantPermissions(['geolocation'], { origin: server.EMPTY_PAGE });
|
||||
expect(await getPermission(page, 'geolocation')).toBe('denied');
|
||||
expect(await getPermission(otherPage, 'geolocation')).toBe('granted');
|
||||
|
||||
await context.clearPermissions();
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
expect(await getPermission(otherPage, 'geolocation')).toBe('granted');
|
||||
await otherContext.close();
|
||||
});
|
||||
it('should be prompt by default', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
});
|
||||
};
|
||||
it('should deny permission when not listed', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions([], { origin: server.EMPTY_PAGE });
|
||||
expect(await getPermission(page, 'geolocation')).toBe('denied');
|
||||
});
|
||||
it('should fail when bad permission is given', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let error = {};
|
||||
await context.grantPermissions(['foo'], { origin: server.EMPTY_PAGE }).catch(e => error = e);
|
||||
expect(error.message).toBe('Unknown permission: foo');
|
||||
});
|
||||
it('should grant geolocation permission when listed', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation'], { origin: server.EMPTY_PAGE });
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
});
|
||||
it('should grant notifications permission when listed', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['notifications'], { origin: server.EMPTY_PAGE });
|
||||
expect(await getPermission(page, 'notifications')).toBe('granted');
|
||||
});
|
||||
it('should accumulate when adding', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation']);
|
||||
await context.grantPermissions(['notifications']);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
expect(await getPermission(page, 'notifications')).toBe('granted');
|
||||
});
|
||||
it('should clear permissions', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation']);
|
||||
await context.clearPermissions();
|
||||
await context.grantPermissions(['notifications']);
|
||||
expect(await getPermission(page, 'geolocation')).not.toBe('granted');
|
||||
expect(await getPermission(page, 'notifications')).toBe('granted');
|
||||
});
|
||||
it('should grant permission when listed for all domains', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation']);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
});
|
||||
it('should grant permission when creating context', async({server, browser}) => {
|
||||
const context = await browser.newContext({ permissions: ['geolocation'] });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
await context.close();
|
||||
});
|
||||
it('should reset permissions', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation'], { origin: server.EMPTY_PAGE });
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
await context.clearPermissions();
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
});
|
||||
it('should trigger permission onchange', async({page, server, context}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
window['events'] = [];
|
||||
return navigator.permissions.query({name: 'geolocation'}).then(function(result) {
|
||||
window['events'].push(result.state);
|
||||
result.onchange = function() {
|
||||
window['events'].push(result.state);
|
||||
};
|
||||
});
|
||||
});
|
||||
expect(await page.evaluate(() => window['events'])).toEqual(['prompt']);
|
||||
await context.grantPermissions([], { origin: server.EMPTY_PAGE });
|
||||
expect(await page.evaluate(() => window['events'])).toEqual(['prompt', 'denied']);
|
||||
await context.grantPermissions(['geolocation'], { origin: server.EMPTY_PAGE });
|
||||
expect(await page.evaluate(() => window['events'])).toEqual(['prompt', 'denied', 'granted']);
|
||||
await context.clearPermissions();
|
||||
expect(await page.evaluate(() => window['events'])).toEqual(['prompt', 'denied', 'granted', 'prompt']);
|
||||
});
|
||||
it('should isolate permissions between browser contexs', async({page, server, context, browser}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const otherContext = await browser.newContext();
|
||||
const otherPage = await otherContext.newPage();
|
||||
await otherPage.goto(server.EMPTY_PAGE);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
expect(await getPermission(otherPage, 'geolocation')).toBe('prompt');
|
||||
|
||||
await context.grantPermissions([], { origin: server.EMPTY_PAGE });
|
||||
await otherContext.grantPermissions(['geolocation'], { origin: server.EMPTY_PAGE });
|
||||
expect(await getPermission(page, 'geolocation')).toBe('denied');
|
||||
expect(await getPermission(otherPage, 'geolocation')).toBe('granted');
|
||||
|
||||
await context.clearPermissions();
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
expect(await getPermission(otherPage, 'geolocation')).toBe('granted');
|
||||
await otherContext.close();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -80,9 +80,11 @@ module.exports.addPlaywrightTests = ({testRunner, products}) => {
|
|||
const playwrightEnvironment = new Environment('Playwright');
|
||||
playwrightEnvironment.beforeAll(async state => {
|
||||
state.playwright = playwright;
|
||||
global.playwright = playwright;
|
||||
});
|
||||
playwrightEnvironment.afterAll(async state => {
|
||||
delete state.playwright;
|
||||
delete global.playwright;
|
||||
});
|
||||
|
||||
for (const product of products) {
|
||||
|
|
@ -143,21 +145,6 @@ module.exports.addPlaywrightTests = ({testRunner, products}) => {
|
|||
state.page = null;
|
||||
});
|
||||
|
||||
function loadTests(modulePath) {
|
||||
const testOptions = {
|
||||
...require('./utils').testOptions(global.browserType),
|
||||
playwright: global.playwright,
|
||||
browserType: global.browserType,
|
||||
};
|
||||
const module = require(modulePath);
|
||||
if (typeof module.describe === 'function')
|
||||
describe('', module.describe, testOptions);
|
||||
if (typeof module.fdescribe === 'function')
|
||||
fdescribe('', module.fdescribe, testOptions);
|
||||
if (typeof module.xdescribe === 'function')
|
||||
xdescribe('', module.xdescribe, testOptions);
|
||||
}
|
||||
|
||||
testRunner.collector().useEnvironment(serverEnvironment); // Custom global environment.
|
||||
testRunner.collector().useEnvironment(playwrightEnvironment);
|
||||
|
||||
|
|
@ -177,71 +164,66 @@ module.exports.addPlaywrightTests = ({testRunner, products}) => {
|
|||
|
||||
// Page-level tests that are given a browser, a context and a page.
|
||||
// Each test is launched in a new browser context.
|
||||
describe('[Accessibility]', () => loadTests('./accessibility.spec.js'));
|
||||
describe('[Driver]', () => {
|
||||
loadTests('./autowaiting.spec.js');
|
||||
loadTests('./click.spec.js');
|
||||
loadTests('./cookies.spec.js');
|
||||
loadTests('./dialog.spec.js');
|
||||
loadTests('./download.spec.js');
|
||||
loadTests('./elementhandle.spec.js');
|
||||
loadTests('./emulation.spec.js');
|
||||
loadTests('./evaluation.spec.js');
|
||||
loadTests('./frame.spec.js');
|
||||
loadTests('./focus.spec.js');
|
||||
loadTests('./input.spec.js');
|
||||
loadTests('./jshandle.spec.js');
|
||||
loadTests('./keyboard.spec.js');
|
||||
loadTests('./mouse.spec.js');
|
||||
loadTests('./navigation.spec.js');
|
||||
loadTests('./network.spec.js');
|
||||
loadTests('./page.spec.js');
|
||||
loadTests('./queryselector.spec.js');
|
||||
loadTests('./screenshot.spec.js');
|
||||
loadTests('./waittask.spec.js');
|
||||
loadTests('./interception.spec.js');
|
||||
loadTests('./geolocation.spec.js');
|
||||
loadTests('./workers.spec.js');
|
||||
loadTests('./capabilities.spec.js');
|
||||
});
|
||||
describe('[Permissions]', () => {
|
||||
loadTests('./permissions.spec.js');
|
||||
});
|
||||
require('./accessibility.spec.js');
|
||||
require('./autowaiting.spec.js');
|
||||
require('./click.spec.js');
|
||||
require('./cookies.spec.js');
|
||||
require('./dialog.spec.js');
|
||||
require('./download.spec.js');
|
||||
require('./elementhandle.spec.js');
|
||||
require('./emulation.spec.js');
|
||||
require('./evaluation.spec.js');
|
||||
require('./frame.spec.js');
|
||||
require('./focus.spec.js');
|
||||
require('./input.spec.js');
|
||||
require('./jshandle.spec.js');
|
||||
require('./keyboard.spec.js');
|
||||
require('./mouse.spec.js');
|
||||
require('./navigation.spec.js');
|
||||
require('./network.spec.js');
|
||||
require('./page.spec.js');
|
||||
require('./queryselector.spec.js');
|
||||
require('./screenshot.spec.js');
|
||||
require('./waittask.spec.js');
|
||||
require('./interception.spec.js');
|
||||
require('./geolocation.spec.js');
|
||||
require('./workers.spec.js');
|
||||
require('./capabilities.spec.js');
|
||||
require('./permissions.spec.js');
|
||||
|
||||
describe.skip(product !== 'Chromium')('[Chromium]', () => {
|
||||
loadTests('./chromium/chromium.spec.js');
|
||||
loadTests('./chromium/coverage.spec.js');
|
||||
loadTests('./chromium/pdf.spec.js');
|
||||
loadTests('./chromium/session.spec.js');
|
||||
require('./chromium/chromium.spec.js');
|
||||
require('./chromium/coverage.spec.js');
|
||||
require('./chromium/pdf.spec.js');
|
||||
require('./chromium/session.spec.js');
|
||||
});
|
||||
});
|
||||
|
||||
// Browser-level tests that are given a browser.
|
||||
describe('[Driver]', () => {
|
||||
loadTests('./browser.spec.js');
|
||||
loadTests('./browsercontext.spec.js');
|
||||
loadTests('./ignorehttpserrors.spec.js');
|
||||
loadTests('./popup.spec.js');
|
||||
require('./browser.spec.js');
|
||||
require('./browsercontext.spec.js');
|
||||
require('./ignorehttpserrors.spec.js');
|
||||
require('./popup.spec.js');
|
||||
});
|
||||
});
|
||||
|
||||
// Top-level tests that launch Browser themselves.
|
||||
describe('[Driver]', () => {
|
||||
loadTests('./defaultbrowsercontext.spec.js');
|
||||
loadTests('./fixtures.spec.js');
|
||||
loadTests('./launcher.spec.js');
|
||||
loadTests('./headful.spec.js');
|
||||
loadTests('./multiclient.spec.js');
|
||||
require('./defaultbrowsercontext.spec.js');
|
||||
require('./fixtures.spec.js');
|
||||
require('./launcher.spec.js');
|
||||
require('./headful.spec.js');
|
||||
require('./multiclient.spec.js');
|
||||
});
|
||||
|
||||
describe.skip(product !== 'Chromium')('[Chromium]', () => {
|
||||
loadTests('./chromium/launcher.spec.js');
|
||||
loadTests('./chromium/oopif.spec.js');
|
||||
loadTests('./chromium/tracing.spec.js');
|
||||
require('./chromium/launcher.spec.js');
|
||||
require('./chromium/oopif.spec.js');
|
||||
require('./chromium/tracing.spec.js');
|
||||
});
|
||||
|
||||
if (process.env.COVERAGE)
|
||||
loadTests('./apicoverage.spec.js');
|
||||
require('./apicoverage.spec.js');
|
||||
|
||||
delete global.browserType;
|
||||
delete global.playwright;
|
||||
|
|
|
|||
|
|
@ -14,324 +14,322 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
module.exports.describe = function({playwright, CHROMIUM, WEBKIT, FFOX}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
|
||||
describe('Link navigation', function() {
|
||||
it('should inherit user agent from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext({
|
||||
userAgent: 'hey'
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a target=_blank rel=noopener href="/popup/popup.html">link</a>');
|
||||
const requestPromise = server.waitForRequest('/popup/popup.html');
|
||||
const [popup] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.click('a'),
|
||||
]);
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
const userAgent = await popup.evaluate(() => window.initialUserAgent);
|
||||
const request = await requestPromise;
|
||||
await context.close();
|
||||
expect(userAgent).toBe('hey');
|
||||
expect(request.headers['user-agent']).toBe('hey');
|
||||
});
|
||||
it('should respect routes from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a target=_blank rel=noopener href="empty.html">link</a>');
|
||||
let intercepted = false;
|
||||
await context.route('**/empty.html', route => {
|
||||
route.continue();
|
||||
intercepted = true;
|
||||
});
|
||||
await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.click('a'),
|
||||
]);
|
||||
await context.close();
|
||||
expect(intercepted).toBe(true);
|
||||
describe('Link navigation', function() {
|
||||
it('should inherit user agent from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext({
|
||||
userAgent: 'hey'
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a target=_blank rel=noopener href="/popup/popup.html">link</a>');
|
||||
const requestPromise = server.waitForRequest('/popup/popup.html');
|
||||
const [popup] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.click('a'),
|
||||
]);
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
const userAgent = await popup.evaluate(() => window.initialUserAgent);
|
||||
const request = await requestPromise;
|
||||
await context.close();
|
||||
expect(userAgent).toBe('hey');
|
||||
expect(request.headers['user-agent']).toBe('hey');
|
||||
});
|
||||
|
||||
describe('window.open', function() {
|
||||
it('should inherit user agent from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext({
|
||||
userAgent: 'hey'
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const requestPromise = server.waitForRequest('/dummy.html');
|
||||
const userAgent = await page.evaluate(url => {
|
||||
const win = window.open(url);
|
||||
return win.navigator.userAgent;
|
||||
}, server.PREFIX + '/dummy.html');
|
||||
const request = await requestPromise;
|
||||
await context.close();
|
||||
expect(userAgent).toBe('hey');
|
||||
expect(request.headers['user-agent']).toBe('hey');
|
||||
});
|
||||
it('should inherit extra headers from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext({
|
||||
extraHTTPHeaders: { 'foo': 'bar' },
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const requestPromise = server.waitForRequest('/dummy.html');
|
||||
await page.evaluate(url => window._popup = window.open(url), server.PREFIX + '/dummy.html');
|
||||
const request = await requestPromise;
|
||||
await context.close();
|
||||
expect(request.headers['foo']).toBe('bar');
|
||||
});
|
||||
it('should inherit offline from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.setOffline(true);
|
||||
const online = await page.evaluate(url => {
|
||||
const win = window.open(url);
|
||||
return win.navigator.onLine;
|
||||
}, server.PREFIX + '/dummy.html');
|
||||
await context.close();
|
||||
expect(online).toBe(false);
|
||||
});
|
||||
it('should inherit http credentials from browser context', async function({browser, server}) {
|
||||
server.setAuth('/title.html', 'user', 'pass');
|
||||
const context = await browser.newContext({
|
||||
httpCredentials: { username: 'user', password: 'pass' }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window._popup = window.open(url), server.PREFIX + '/title.html'),
|
||||
]);
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
expect(await popup.title()).toBe('Woof-Woof');
|
||||
await context.close();
|
||||
});
|
||||
it('should inherit touch support from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext({
|
||||
viewport: { width: 400, height: 500 },
|
||||
hasTouch: true
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const hasTouch = await page.evaluate(() => {
|
||||
const win = window.open('');
|
||||
return 'ontouchstart' in win;
|
||||
});
|
||||
await context.close();
|
||||
expect(hasTouch).toBe(true);
|
||||
});
|
||||
it('should inherit viewport size from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext({
|
||||
viewport: { width: 400, height: 500 }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const size = await page.evaluate(() => {
|
||||
const win = window.open('about:blank');
|
||||
return { width: win.innerWidth, height: win.innerHeight };
|
||||
});
|
||||
await context.close();
|
||||
expect(size).toEqual({width: 400, height: 500});
|
||||
});
|
||||
it('should respect routes from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let intercepted = false;
|
||||
await context.route('**/empty.html', route => {
|
||||
route.continue();
|
||||
intercepted = true;
|
||||
});
|
||||
await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window.__popup = window.open(url), server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(intercepted).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should apply addInitScript from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
await context.addInitScript(() => window.injected = 123);
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const injected = await page.evaluate(() => {
|
||||
const win = window.open('about:blank');
|
||||
return win.injected;
|
||||
});
|
||||
await context.close();
|
||||
expect(injected).toBe(123);
|
||||
});
|
||||
it('should expose function from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
await context.exposeFunction('add', (a, b) => a + b);
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const added = await page.evaluate(async () => {
|
||||
const win = window.open('about:blank');
|
||||
return win.add(9, 4);
|
||||
});
|
||||
await context.close();
|
||||
expect(added).toBe(13);
|
||||
it('should respect routes from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a target=_blank rel=noopener href="empty.html">link</a>');
|
||||
let intercepted = false;
|
||||
await context.route('**/empty.html', route => {
|
||||
route.continue();
|
||||
intercepted = true;
|
||||
});
|
||||
await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.click('a'),
|
||||
]);
|
||||
await context.close();
|
||||
expect(intercepted).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.Events.Popup', function() {
|
||||
it('should work', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => window.__popup = window.open('about:blank')),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with window features', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => window.__popup = window.open(window.location.href, 'Title', 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=780,height=200,top=0,left=0')),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should emit for immediately closed popups', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => {
|
||||
const win = window.open('about:blank');
|
||||
win.close();
|
||||
}),
|
||||
]);
|
||||
expect(popup).toBeTruthy();
|
||||
await context.close();
|
||||
});
|
||||
it('should be able to capture alert', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const evaluatePromise = page.evaluate(() => {
|
||||
const win = window.open('');
|
||||
win.alert('hello');
|
||||
});
|
||||
const popup = await page.waitForEvent('popup');
|
||||
const dialog = await popup.waitForEvent('dialog');
|
||||
expect(dialog.message()).toBe('hello');
|
||||
await dialog.dismiss();
|
||||
await evaluatePromise;
|
||||
await context.close();
|
||||
});
|
||||
it('should work with empty url', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => window.__popup = window.open('')),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with noopener and no url', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => window.__popup = window.open(undefined, null, 'noopener')),
|
||||
]);
|
||||
expect(popup.url()).toBe('about:blank');
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with noopener and about:blank', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => window.__popup = window.open('about:blank', null, 'noopener')),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with noopener and url', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window.__popup = window.open(url, null, 'noopener'), server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with clicking target=_blank', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a target=_blank rel="opener" href="/one-style.html">yo</a>');
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.click('a'),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with fake-clicking target=_blank and rel=noopener', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
// TODO: FFOX sends events for "one-style.html" request to both pages.
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a target=_blank rel=noopener href="/one-style.html">yo</a>');
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.$eval('a', a => a.click()),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with clicking target=_blank and rel=noopener', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a target=_blank rel=noopener href="/one-style.html">yo</a>');
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.click('a'),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
|
||||
await context.close();
|
||||
});
|
||||
it('should not treat navigations as new popups', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a target=_blank rel=noopener href="/one-style.html">yo</a>');
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.click('a'),
|
||||
]);
|
||||
let badSecondPopup = false;
|
||||
page.on('popup', () => badSecondPopup = true);
|
||||
await popup.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
await context.close();
|
||||
expect(badSecondPopup).toBe(false);
|
||||
describe('window.open', function() {
|
||||
it('should inherit user agent from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext({
|
||||
userAgent: 'hey'
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const requestPromise = server.waitForRequest('/dummy.html');
|
||||
const userAgent = await page.evaluate(url => {
|
||||
const win = window.open(url);
|
||||
return win.navigator.userAgent;
|
||||
}, server.PREFIX + '/dummy.html');
|
||||
const request = await requestPromise;
|
||||
await context.close();
|
||||
expect(userAgent).toBe('hey');
|
||||
expect(request.headers['user-agent']).toBe('hey');
|
||||
});
|
||||
it('should inherit extra headers from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext({
|
||||
extraHTTPHeaders: { 'foo': 'bar' },
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const requestPromise = server.waitForRequest('/dummy.html');
|
||||
await page.evaluate(url => window._popup = window.open(url), server.PREFIX + '/dummy.html');
|
||||
const request = await requestPromise;
|
||||
await context.close();
|
||||
expect(request.headers['foo']).toBe('bar');
|
||||
});
|
||||
it('should inherit offline from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.setOffline(true);
|
||||
const online = await page.evaluate(url => {
|
||||
const win = window.open(url);
|
||||
return win.navigator.onLine;
|
||||
}, server.PREFIX + '/dummy.html');
|
||||
await context.close();
|
||||
expect(online).toBe(false);
|
||||
});
|
||||
it('should inherit http credentials from browser context', async function({browser, server}) {
|
||||
server.setAuth('/title.html', 'user', 'pass');
|
||||
const context = await browser.newContext({
|
||||
httpCredentials: { username: 'user', password: 'pass' }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window._popup = window.open(url), server.PREFIX + '/title.html'),
|
||||
]);
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
expect(await popup.title()).toBe('Woof-Woof');
|
||||
await context.close();
|
||||
});
|
||||
it('should inherit touch support from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext({
|
||||
viewport: { width: 400, height: 500 },
|
||||
hasTouch: true
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const hasTouch = await page.evaluate(() => {
|
||||
const win = window.open('');
|
||||
return 'ontouchstart' in win;
|
||||
});
|
||||
await context.close();
|
||||
expect(hasTouch).toBe(true);
|
||||
});
|
||||
it('should inherit viewport size from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext({
|
||||
viewport: { width: 400, height: 500 }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const size = await page.evaluate(() => {
|
||||
const win = window.open('about:blank');
|
||||
return { width: win.innerWidth, height: win.innerHeight };
|
||||
});
|
||||
await context.close();
|
||||
expect(size).toEqual({width: 400, height: 500});
|
||||
});
|
||||
it('should respect routes from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let intercepted = false;
|
||||
await context.route('**/empty.html', route => {
|
||||
route.continue();
|
||||
intercepted = true;
|
||||
});
|
||||
await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window.__popup = window.open(url), server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(intercepted).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should apply addInitScript from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
await context.addInitScript(() => window.injected = 123);
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const injected = await page.evaluate(() => {
|
||||
const win = window.open('about:blank');
|
||||
return win.injected;
|
||||
});
|
||||
await context.close();
|
||||
expect(injected).toBe(123);
|
||||
});
|
||||
it('should expose function from browser context', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
await context.exposeFunction('add', (a, b) => a + b);
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const added = await page.evaluate(async () => {
|
||||
const win = window.open('about:blank');
|
||||
return win.add(9, 4);
|
||||
});
|
||||
await context.close();
|
||||
expect(added).toBe(13);
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
describe('Page.Events.Popup', function() {
|
||||
it('should work', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => window.__popup = window.open('about:blank')),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with window features', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => window.__popup = window.open(window.location.href, 'Title', 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=780,height=200,top=0,left=0')),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should emit for immediately closed popups', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => {
|
||||
const win = window.open('about:blank');
|
||||
win.close();
|
||||
}),
|
||||
]);
|
||||
expect(popup).toBeTruthy();
|
||||
await context.close();
|
||||
});
|
||||
it('should be able to capture alert', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const evaluatePromise = page.evaluate(() => {
|
||||
const win = window.open('');
|
||||
win.alert('hello');
|
||||
});
|
||||
const popup = await page.waitForEvent('popup');
|
||||
const dialog = await popup.waitForEvent('dialog');
|
||||
expect(dialog.message()).toBe('hello');
|
||||
await dialog.dismiss();
|
||||
await evaluatePromise;
|
||||
await context.close();
|
||||
});
|
||||
it('should work with empty url', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => window.__popup = window.open('')),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with noopener and no url', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => window.__popup = window.open(undefined, null, 'noopener')),
|
||||
]);
|
||||
expect(popup.url()).toBe('about:blank');
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with noopener and about:blank', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => window.__popup = window.open('about:blank', null, 'noopener')),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with noopener and url', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window.__popup = window.open(url, null, 'noopener'), server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with clicking target=_blank', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a target=_blank rel="opener" href="/one-style.html">yo</a>');
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.click('a'),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with fake-clicking target=_blank and rel=noopener', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
// TODO: FFOX sends events for "one-style.html" request to both pages.
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a target=_blank rel=noopener href="/one-style.html">yo</a>');
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.$eval('a', a => a.click()),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with clicking target=_blank and rel=noopener', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a target=_blank rel=noopener href="/one-style.html">yo</a>');
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.click('a'),
|
||||
]);
|
||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
|
||||
await context.close();
|
||||
});
|
||||
it('should not treat navigations as new popups', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a target=_blank rel=noopener href="/one-style.html">yo</a>');
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.click('a'),
|
||||
]);
|
||||
let badSecondPopup = false;
|
||||
page.on('popup', () => badSecondPopup = true);
|
||||
await popup.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
await context.close();
|
||||
expect(badSecondPopup).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -15,469 +15,465 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({FFOX, CHROMIUM, WEBKIT, LINUX }) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
|
||||
describe('Page.screenshot', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshot = await page.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-sanity.png');
|
||||
});
|
||||
it('should clip rect', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshot = await page.screenshot({
|
||||
clip: {
|
||||
x: 50,
|
||||
y: 100,
|
||||
width: 150,
|
||||
height: 100
|
||||
}
|
||||
});
|
||||
expect(screenshot).toBeGolden('screenshot-clip-rect.png');
|
||||
});
|
||||
it('should clip rect with fullPage', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await page.evaluate(() => window.scrollBy(150, 200));
|
||||
const screenshot = await page.screenshot({
|
||||
fullPage: true,
|
||||
clip: {
|
||||
x: 50,
|
||||
y: 100,
|
||||
width: 150,
|
||||
height: 100,
|
||||
},
|
||||
});
|
||||
expect(screenshot).toBeGolden('screenshot-clip-rect.png');
|
||||
});
|
||||
it('should clip elements to the viewport', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshot = await page.screenshot({
|
||||
clip: {
|
||||
x: 50,
|
||||
y: 450,
|
||||
width: 1000,
|
||||
height: 100
|
||||
}
|
||||
});
|
||||
expect(screenshot).toBeGolden('screenshot-offscreen-clip.png');
|
||||
});
|
||||
it('should throw on clip outside the viewport', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshotError = await page.screenshot({
|
||||
clip: {
|
||||
x: 50,
|
||||
y: 650,
|
||||
width: 100,
|
||||
height: 100
|
||||
}
|
||||
}).catch(error => error);
|
||||
expect(screenshotError.message).toBe('Clipped area is either empty or outside the resulting image');
|
||||
});
|
||||
it('should run in parallel', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const promises = [];
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
promises.push(page.screenshot({
|
||||
clip: {
|
||||
x: 50 * i,
|
||||
y: 0,
|
||||
width: 50,
|
||||
height: 50
|
||||
}
|
||||
}));
|
||||
describe('Page.screenshot', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshot = await page.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-sanity.png');
|
||||
});
|
||||
it('should clip rect', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshot = await page.screenshot({
|
||||
clip: {
|
||||
x: 50,
|
||||
y: 100,
|
||||
width: 150,
|
||||
height: 100
|
||||
}
|
||||
const screenshots = await Promise.all(promises);
|
||||
expect(screenshots[1]).toBeGolden('grid-cell-1.png');
|
||||
});
|
||||
it('should take fullPage screenshots', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshot = await page.screenshot({
|
||||
fullPage: true
|
||||
});
|
||||
expect(screenshot).toBeGolden('screenshot-grid-fullpage.png');
|
||||
expect(screenshot).toBeGolden('screenshot-clip-rect.png');
|
||||
});
|
||||
it('should clip rect with fullPage', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await page.evaluate(() => window.scrollBy(150, 200));
|
||||
const screenshot = await page.screenshot({
|
||||
fullPage: true,
|
||||
clip: {
|
||||
x: 50,
|
||||
y: 100,
|
||||
width: 150,
|
||||
height: 100,
|
||||
},
|
||||
});
|
||||
it('should restore viewport after fullPage screenshot', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshot = await page.screenshot({ fullPage: true });
|
||||
expect(screenshot).toBeInstanceOf(Buffer);
|
||||
expect(page.viewportSize().width).toBe(500);
|
||||
expect(page.viewportSize().height).toBe(500);
|
||||
expect(screenshot).toBeGolden('screenshot-clip-rect.png');
|
||||
});
|
||||
it('should clip elements to the viewport', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshot = await page.screenshot({
|
||||
clip: {
|
||||
x: 50,
|
||||
y: 450,
|
||||
width: 1000,
|
||||
height: 100
|
||||
}
|
||||
});
|
||||
it('should run in parallel in multiple pages', async({page, server, context}) => {
|
||||
const N = 2;
|
||||
const pages = await Promise.all(Array(N).fill(0).map(async() => {
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
return page;
|
||||
}));
|
||||
const promises = [];
|
||||
for (let i = 0; i < N; ++i)
|
||||
promises.push(pages[i].screenshot({ clip: { x: 50 * i, y: 0, width: 50, height: 50 } }));
|
||||
const screenshots = await Promise.all(promises);
|
||||
for (let i = 0; i < N; ++i)
|
||||
expect(screenshots[i]).toBeGolden(`grid-cell-${i}.png`);
|
||||
await Promise.all(pages.map(page => page.close()));
|
||||
});
|
||||
it.fail(FFOX)('should allow transparency', async({page, server}) => {
|
||||
await page.setViewportSize({ width: 50, height: 150 });
|
||||
await page.setContent(`
|
||||
<style>
|
||||
body { margin: 0 }
|
||||
div { width: 50px; height: 50px; }
|
||||
</style>
|
||||
<div style="background:black"></div>
|
||||
<div style="background:white"></div>
|
||||
<div style="background:transparent"></div>
|
||||
`);
|
||||
const screenshot = await page.screenshot({omitBackground: true});
|
||||
expect(screenshot).toBeGolden('transparent.png');
|
||||
});
|
||||
it('should render white background on jpeg file', async({page, server}) => {
|
||||
await page.setViewportSize({ width: 100, height: 100 });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const screenshot = await page.screenshot({omitBackground: true, type: 'jpeg'});
|
||||
expect(screenshot).toBeGolden('white.jpg');
|
||||
});
|
||||
it('should work with odd clip size on Retina displays', async({page, server}) => {
|
||||
const screenshot = await page.screenshot({
|
||||
expect(screenshot).toBeGolden('screenshot-offscreen-clip.png');
|
||||
});
|
||||
it('should throw on clip outside the viewport', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshotError = await page.screenshot({
|
||||
clip: {
|
||||
x: 50,
|
||||
y: 650,
|
||||
width: 100,
|
||||
height: 100
|
||||
}
|
||||
}).catch(error => error);
|
||||
expect(screenshotError.message).toBe('Clipped area is either empty or outside the resulting image');
|
||||
});
|
||||
it('should run in parallel', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const promises = [];
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
promises.push(page.screenshot({
|
||||
clip: {
|
||||
x: 0,
|
||||
x: 50 * i,
|
||||
y: 0,
|
||||
width: 11,
|
||||
height: 11,
|
||||
width: 50,
|
||||
height: 50
|
||||
}
|
||||
});
|
||||
expect(screenshot).toBeGolden('screenshot-clip-odd-size.png');
|
||||
}));
|
||||
}
|
||||
const screenshots = await Promise.all(promises);
|
||||
expect(screenshots[1]).toBeGolden('grid-cell-1.png');
|
||||
});
|
||||
it('should take fullPage screenshots', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshot = await page.screenshot({
|
||||
fullPage: true
|
||||
});
|
||||
it.skip(FFOX)('should work with a mobile viewport', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 320, height: 480 }, isMobile: true });
|
||||
expect(screenshot).toBeGolden('screenshot-grid-fullpage.png');
|
||||
});
|
||||
it('should restore viewport after fullPage screenshot', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshot = await page.screenshot({ fullPage: true });
|
||||
expect(screenshot).toBeInstanceOf(Buffer);
|
||||
expect(page.viewportSize().width).toBe(500);
|
||||
expect(page.viewportSize().height).toBe(500);
|
||||
});
|
||||
it('should run in parallel in multiple pages', async({page, server, context}) => {
|
||||
const N = 2;
|
||||
const pages = await Promise.all(Array(N).fill(0).map(async() => {
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/overflow.html');
|
||||
const screenshot = await page.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-mobile.png');
|
||||
await context.close();
|
||||
});
|
||||
it.skip(FFOX)('should work with a mobile viewport and clip', async({browser, server}) => {
|
||||
const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/overflow.html');
|
||||
const screenshot = await page.screenshot({ clip: { x: 10, y: 10, width: 100, height: 150 } });
|
||||
expect(screenshot).toBeGolden('screenshot-mobile-clip.png');
|
||||
await context.close();
|
||||
});
|
||||
it.skip(FFOX)('should work with a mobile viewport and fullPage', async({browser, server}) => {
|
||||
const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/overflow-large.html');
|
||||
const screenshot = await page.screenshot({ fullPage: true });
|
||||
expect(screenshot).toBeGolden('screenshot-mobile-fullpage.png');
|
||||
await context.close();
|
||||
});
|
||||
it('should work for canvas', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/screenshots/canvas.html');
|
||||
const screenshot = await page.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-canvas.png');
|
||||
});
|
||||
it('should work for translateZ', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/screenshots/translateZ.html');
|
||||
const screenshot = await page.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-translateZ.png');
|
||||
});
|
||||
it.fail(FFOX || WEBKIT)('should work for webgl', async({page, server}) => {
|
||||
await page.setViewportSize({width: 640, height: 480});
|
||||
await page.goto(server.PREFIX + '/screenshots/webgl.html');
|
||||
const screenshot = await page.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-webgl.png');
|
||||
});
|
||||
it('should work while navigating', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/redirectloop1.html');
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const screenshot = await page.screenshot({ fullPage: true }).catch(e => {
|
||||
if (e.message.includes('Cannot take a screenshot while page is navigating'))
|
||||
return Buffer.from('');
|
||||
throw e;
|
||||
});
|
||||
expect(screenshot).toBeInstanceOf(Buffer);
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
return page;
|
||||
}));
|
||||
const promises = [];
|
||||
for (let i = 0; i < N; ++i)
|
||||
promises.push(pages[i].screenshot({ clip: { x: 50 * i, y: 0, width: 50, height: 50 } }));
|
||||
const screenshots = await Promise.all(promises);
|
||||
for (let i = 0; i < N; ++i)
|
||||
expect(screenshots[i]).toBeGolden(`grid-cell-${i}.png`);
|
||||
await Promise.all(pages.map(page => page.close()));
|
||||
});
|
||||
it.fail(FFOX)('should allow transparency', async({page, server}) => {
|
||||
await page.setViewportSize({ width: 50, height: 150 });
|
||||
await page.setContent(`
|
||||
<style>
|
||||
body { margin: 0 }
|
||||
div { width: 50px; height: 50px; }
|
||||
</style>
|
||||
<div style="background:black"></div>
|
||||
<div style="background:white"></div>
|
||||
<div style="background:transparent"></div>
|
||||
`);
|
||||
const screenshot = await page.screenshot({omitBackground: true});
|
||||
expect(screenshot).toBeGolden('transparent.png');
|
||||
});
|
||||
it('should render white background on jpeg file', async({page, server}) => {
|
||||
await page.setViewportSize({ width: 100, height: 100 });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const screenshot = await page.screenshot({omitBackground: true, type: 'jpeg'});
|
||||
expect(screenshot).toBeGolden('white.jpg');
|
||||
});
|
||||
it('should work with odd clip size on Retina displays', async({page, server}) => {
|
||||
const screenshot = await page.screenshot({
|
||||
clip: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 11,
|
||||
height: 11,
|
||||
}
|
||||
});
|
||||
it('should work with device scale factor', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 320, height: 480 }, deviceScaleFactor: 2 });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshot = await page.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-device-scale-factor.png');
|
||||
await context.close();
|
||||
});
|
||||
expect(screenshot).toBeGolden('screenshot-clip-odd-size.png');
|
||||
});
|
||||
|
||||
describe('ElementHandle.screenshot', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await page.evaluate(() => window.scrollBy(50, 100));
|
||||
const elementHandle = await page.$('.box:nth-of-type(3)');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-bounding-box.png');
|
||||
});
|
||||
it('should take into account padding and border', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.setContent(`
|
||||
<div style="height: 14px">oooo</div>
|
||||
<style>div {
|
||||
border: 2px solid blue;
|
||||
background: green;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
</style>
|
||||
<div id="d"></div>
|
||||
`);
|
||||
const elementHandle = await page.$('div#d');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-padding-border.png');
|
||||
});
|
||||
it('should capture full element when larger than viewport in parallel', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
|
||||
await page.setContent(`
|
||||
<div style="height: 14px">oooo</div>
|
||||
<style>
|
||||
div.to-screenshot {
|
||||
border: 1px solid blue;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
margin-left: 50px;
|
||||
}
|
||||
::-webkit-scrollbar{
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<div class="to-screenshot"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
`);
|
||||
const elementHandles = await page.$$('div.to-screenshot');
|
||||
const promises = elementHandles.map(handle => handle.screenshot());
|
||||
const screenshots = await Promise.all(promises);
|
||||
expect(screenshots[2]).toBeGolden('screenshot-element-larger-than-viewport.png');
|
||||
|
||||
expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
|
||||
});
|
||||
it('should capture full element when larger than viewport', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
|
||||
await page.setContent(`
|
||||
<div style="height: 14px">oooo</div>
|
||||
<style>
|
||||
div.to-screenshot {
|
||||
border: 1px solid blue;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
margin-left: 50px;
|
||||
}
|
||||
::-webkit-scrollbar{
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<div class="to-screenshot"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
`);
|
||||
const elementHandle = await page.$('div.to-screenshot');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-larger-than-viewport.png');
|
||||
|
||||
expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
|
||||
});
|
||||
it('should scroll element into view', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.setContent(`
|
||||
<div style="height: 14px">oooo</div>
|
||||
<style>div.above {
|
||||
border: 2px solid blue;
|
||||
background: red;
|
||||
height: 1500px;
|
||||
}
|
||||
div.to-screenshot {
|
||||
border: 2px solid blue;
|
||||
background: green;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
</style>
|
||||
<div class="above"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
`);
|
||||
const elementHandle = await page.$('div.to-screenshot');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png');
|
||||
});
|
||||
it('should scroll 15000px into view', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.setContent(`
|
||||
<div style="height: 14px">oooo</div>
|
||||
<style>div.above {
|
||||
border: 2px solid blue;
|
||||
background: red;
|
||||
height: 15000px;
|
||||
}
|
||||
div.to-screenshot {
|
||||
border: 2px solid blue;
|
||||
background: green;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
</style>
|
||||
<div class="above"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
`);
|
||||
const elementHandle = await page.$('div.to-screenshot');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png');
|
||||
});
|
||||
it('should work with a rotated element', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.setContent(`<div style="position:absolute;
|
||||
top: 100px;
|
||||
left: 100px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: green;
|
||||
transform: rotateZ(200deg);"> </div>`);
|
||||
const elementHandle = await page.$('div');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-rotate.png');
|
||||
});
|
||||
it('should fail to screenshot a detached element', async({page, server}) => {
|
||||
await page.setContent('<h1>remove this</h1>');
|
||||
const elementHandle = await page.$('h1');
|
||||
await page.evaluate(element => element.remove(), elementHandle);
|
||||
const screenshotError = await elementHandle.screenshot().catch(error => error);
|
||||
expect(screenshotError.message).toContain('Node is detached');
|
||||
});
|
||||
it('should not hang with zero width/height element', async({page, server}) => {
|
||||
await page.setContent('<div style="width: 50px; height: 0"></div>');
|
||||
const div = await page.$('div');
|
||||
const error = await div.screenshot().catch(e => e);
|
||||
expect(error.message).toBe('Node has 0 height.');
|
||||
});
|
||||
it('should work for an element with fractional dimensions', async({page}) => {
|
||||
await page.setContent('<div style="width:48.51px;height:19.8px;border:1px solid black;"></div>');
|
||||
const elementHandle = await page.$('div');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-fractional.png');
|
||||
});
|
||||
it.skip(FFOX)('should work with a mobile viewport', async({browser, server}) => {
|
||||
const context = await browser.newContext({viewport: { width: 320, height: 480, isMobile: true }});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await page.evaluate(() => window.scrollBy(50, 100));
|
||||
const elementHandle = await page.$('.box:nth-of-type(3)');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-mobile.png');
|
||||
await context.close();
|
||||
});
|
||||
it.skip(FFOX)('should work with device scale factor', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 320, height: 480 }, deviceScaleFactor: 2 });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await page.evaluate(() => window.scrollBy(50, 100));
|
||||
const elementHandle = await page.$('.box:nth-of-type(3)');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-mobile-dsf.png');
|
||||
await context.close();
|
||||
});
|
||||
it('should work for an element with an offset', async({page}) => {
|
||||
await page.setContent('<div style="position:absolute; top: 10.3px; left: 20.4px;width:50.3px;height:20.2px;border:1px solid black;"></div>');
|
||||
const elementHandle = await page.$('div');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-fractional-offset.png');
|
||||
});
|
||||
it('should take screenshots when default viewport is null', async({server, browser}) => {
|
||||
const context = await browser.newContext({ viewport: null });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
||||
const screenshot = await page.screenshot();
|
||||
expect(screenshot).toBeInstanceOf(Buffer);
|
||||
|
||||
const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
||||
expect(sizeBefore.width).toBe(sizeAfter.width);
|
||||
expect(sizeBefore.height).toBe(sizeAfter.height);
|
||||
await context.close();
|
||||
});
|
||||
it('should take fullPage screenshots when default viewport is null', async({server, browser}) => {
|
||||
const context = await browser.newContext({ viewport: null });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
||||
const screenshot = await page.screenshot({
|
||||
fullPage: true
|
||||
it.skip(FFOX)('should work with a mobile viewport', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 320, height: 480 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/overflow.html');
|
||||
const screenshot = await page.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-mobile.png');
|
||||
await context.close();
|
||||
});
|
||||
it.skip(FFOX)('should work with a mobile viewport and clip', async({browser, server}) => {
|
||||
const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/overflow.html');
|
||||
const screenshot = await page.screenshot({ clip: { x: 10, y: 10, width: 100, height: 150 } });
|
||||
expect(screenshot).toBeGolden('screenshot-mobile-clip.png');
|
||||
await context.close();
|
||||
});
|
||||
it.skip(FFOX)('should work with a mobile viewport and fullPage', async({browser, server}) => {
|
||||
const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/overflow-large.html');
|
||||
const screenshot = await page.screenshot({ fullPage: true });
|
||||
expect(screenshot).toBeGolden('screenshot-mobile-fullpage.png');
|
||||
await context.close();
|
||||
});
|
||||
it('should work for canvas', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/screenshots/canvas.html');
|
||||
const screenshot = await page.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-canvas.png');
|
||||
});
|
||||
it('should work for translateZ', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/screenshots/translateZ.html');
|
||||
const screenshot = await page.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-translateZ.png');
|
||||
});
|
||||
it.fail(FFOX || WEBKIT)('should work for webgl', async({page, server}) => {
|
||||
await page.setViewportSize({width: 640, height: 480});
|
||||
await page.goto(server.PREFIX + '/screenshots/webgl.html');
|
||||
const screenshot = await page.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-webgl.png');
|
||||
});
|
||||
it('should work while navigating', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/redirectloop1.html');
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const screenshot = await page.screenshot({ fullPage: true }).catch(e => {
|
||||
if (e.message.includes('Cannot take a screenshot while page is navigating'))
|
||||
return Buffer.from('');
|
||||
throw e;
|
||||
});
|
||||
expect(screenshot).toBeInstanceOf(Buffer);
|
||||
|
||||
const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
||||
expect(sizeBefore.width).toBe(sizeAfter.width);
|
||||
expect(sizeBefore.height).toBe(sizeAfter.height);
|
||||
await context.close();
|
||||
});
|
||||
it('should restore default viewport after fullPage screenshot', async({ browser }) => {
|
||||
const context = await browser.newContext({ viewport: { width: 456, height: 789 } });
|
||||
const page = await context.newPage();
|
||||
expect(page.viewportSize().width).toBe(456);
|
||||
expect(page.viewportSize().height).toBe(789);
|
||||
expect(await page.evaluate('window.innerWidth')).toBe(456);
|
||||
expect(await page.evaluate('window.innerHeight')).toBe(789);
|
||||
const screenshot = await page.screenshot({ fullPage: true });
|
||||
expect(screenshot).toBeInstanceOf(Buffer);
|
||||
expect(page.viewportSize().width).toBe(456);
|
||||
expect(page.viewportSize().height).toBe(789);
|
||||
expect(await page.evaluate('window.innerWidth')).toBe(456);
|
||||
expect(await page.evaluate('window.innerHeight')).toBe(789);
|
||||
await context.close();
|
||||
});
|
||||
it('should take element screenshot when default viewport is null and restore back', async({server, browser}) => {
|
||||
const context = await browser.newContext({viewport: null});
|
||||
const page = await context.newPage({ viewport: null });
|
||||
await page.setContent(`
|
||||
<div style="height: 14px">oooo</div>
|
||||
<style>
|
||||
div.to-screenshot {
|
||||
border: 1px solid blue;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
margin-left: 50px;
|
||||
}
|
||||
::-webkit-scrollbar{
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<div class="to-screenshot"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
`);
|
||||
const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
||||
const elementHandle = await page.$('div.to-screenshot');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeInstanceOf(Buffer);
|
||||
const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
||||
expect(sizeBefore.width).toBe(sizeAfter.width);
|
||||
expect(sizeBefore.height).toBe(sizeAfter.height);
|
||||
await context.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
it('should work with device scale factor', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 320, height: 480 }, deviceScaleFactor: 2 });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const screenshot = await page.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-device-scale-factor.png');
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.screenshot', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await page.evaluate(() => window.scrollBy(50, 100));
|
||||
const elementHandle = await page.$('.box:nth-of-type(3)');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-bounding-box.png');
|
||||
});
|
||||
it('should take into account padding and border', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.setContent(`
|
||||
<div style="height: 14px">oooo</div>
|
||||
<style>div {
|
||||
border: 2px solid blue;
|
||||
background: green;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
</style>
|
||||
<div id="d"></div>
|
||||
`);
|
||||
const elementHandle = await page.$('div#d');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-padding-border.png');
|
||||
});
|
||||
it('should capture full element when larger than viewport in parallel', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
|
||||
await page.setContent(`
|
||||
<div style="height: 14px">oooo</div>
|
||||
<style>
|
||||
div.to-screenshot {
|
||||
border: 1px solid blue;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
margin-left: 50px;
|
||||
}
|
||||
::-webkit-scrollbar{
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<div class="to-screenshot"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
`);
|
||||
const elementHandles = await page.$$('div.to-screenshot');
|
||||
const promises = elementHandles.map(handle => handle.screenshot());
|
||||
const screenshots = await Promise.all(promises);
|
||||
expect(screenshots[2]).toBeGolden('screenshot-element-larger-than-viewport.png');
|
||||
|
||||
expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
|
||||
});
|
||||
it('should capture full element when larger than viewport', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
|
||||
await page.setContent(`
|
||||
<div style="height: 14px">oooo</div>
|
||||
<style>
|
||||
div.to-screenshot {
|
||||
border: 1px solid blue;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
margin-left: 50px;
|
||||
}
|
||||
::-webkit-scrollbar{
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<div class="to-screenshot"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
`);
|
||||
const elementHandle = await page.$('div.to-screenshot');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-larger-than-viewport.png');
|
||||
|
||||
expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
|
||||
});
|
||||
it('should scroll element into view', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.setContent(`
|
||||
<div style="height: 14px">oooo</div>
|
||||
<style>div.above {
|
||||
border: 2px solid blue;
|
||||
background: red;
|
||||
height: 1500px;
|
||||
}
|
||||
div.to-screenshot {
|
||||
border: 2px solid blue;
|
||||
background: green;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
</style>
|
||||
<div class="above"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
`);
|
||||
const elementHandle = await page.$('div.to-screenshot');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png');
|
||||
});
|
||||
it('should scroll 15000px into view', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.setContent(`
|
||||
<div style="height: 14px">oooo</div>
|
||||
<style>div.above {
|
||||
border: 2px solid blue;
|
||||
background: red;
|
||||
height: 15000px;
|
||||
}
|
||||
div.to-screenshot {
|
||||
border: 2px solid blue;
|
||||
background: green;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
</style>
|
||||
<div class="above"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
`);
|
||||
const elementHandle = await page.$('div.to-screenshot');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png');
|
||||
});
|
||||
it('should work with a rotated element', async({page, server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.setContent(`<div style="position:absolute;
|
||||
top: 100px;
|
||||
left: 100px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: green;
|
||||
transform: rotateZ(200deg);"> </div>`);
|
||||
const elementHandle = await page.$('div');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-rotate.png');
|
||||
});
|
||||
it('should fail to screenshot a detached element', async({page, server}) => {
|
||||
await page.setContent('<h1>remove this</h1>');
|
||||
const elementHandle = await page.$('h1');
|
||||
await page.evaluate(element => element.remove(), elementHandle);
|
||||
const screenshotError = await elementHandle.screenshot().catch(error => error);
|
||||
expect(screenshotError.message).toContain('Node is detached');
|
||||
});
|
||||
it('should not hang with zero width/height element', async({page, server}) => {
|
||||
await page.setContent('<div style="width: 50px; height: 0"></div>');
|
||||
const div = await page.$('div');
|
||||
const error = await div.screenshot().catch(e => e);
|
||||
expect(error.message).toBe('Node has 0 height.');
|
||||
});
|
||||
it('should work for an element with fractional dimensions', async({page}) => {
|
||||
await page.setContent('<div style="width:48.51px;height:19.8px;border:1px solid black;"></div>');
|
||||
const elementHandle = await page.$('div');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-fractional.png');
|
||||
});
|
||||
it.skip(FFOX)('should work with a mobile viewport', async({browser, server}) => {
|
||||
const context = await browser.newContext({viewport: { width: 320, height: 480, isMobile: true }});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await page.evaluate(() => window.scrollBy(50, 100));
|
||||
const elementHandle = await page.$('.box:nth-of-type(3)');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-mobile.png');
|
||||
await context.close();
|
||||
});
|
||||
it.skip(FFOX)('should work with device scale factor', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 320, height: 480 }, deviceScaleFactor: 2 });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await page.evaluate(() => window.scrollBy(50, 100));
|
||||
const elementHandle = await page.$('.box:nth-of-type(3)');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-mobile-dsf.png');
|
||||
await context.close();
|
||||
});
|
||||
it('should work for an element with an offset', async({page}) => {
|
||||
await page.setContent('<div style="position:absolute; top: 10.3px; left: 20.4px;width:50.3px;height:20.2px;border:1px solid black;"></div>');
|
||||
const elementHandle = await page.$('div');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeGolden('screenshot-element-fractional-offset.png');
|
||||
});
|
||||
it('should take screenshots when default viewport is null', async({server, browser}) => {
|
||||
const context = await browser.newContext({ viewport: null });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
||||
const screenshot = await page.screenshot();
|
||||
expect(screenshot).toBeInstanceOf(Buffer);
|
||||
|
||||
const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
||||
expect(sizeBefore.width).toBe(sizeAfter.width);
|
||||
expect(sizeBefore.height).toBe(sizeAfter.height);
|
||||
await context.close();
|
||||
});
|
||||
it('should take fullPage screenshots when default viewport is null', async({server, browser}) => {
|
||||
const context = await browser.newContext({ viewport: null });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
||||
const screenshot = await page.screenshot({
|
||||
fullPage: true
|
||||
});
|
||||
expect(screenshot).toBeInstanceOf(Buffer);
|
||||
|
||||
const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
||||
expect(sizeBefore.width).toBe(sizeAfter.width);
|
||||
expect(sizeBefore.height).toBe(sizeAfter.height);
|
||||
await context.close();
|
||||
});
|
||||
it('should restore default viewport after fullPage screenshot', async({ browser }) => {
|
||||
const context = await browser.newContext({ viewport: { width: 456, height: 789 } });
|
||||
const page = await context.newPage();
|
||||
expect(page.viewportSize().width).toBe(456);
|
||||
expect(page.viewportSize().height).toBe(789);
|
||||
expect(await page.evaluate('window.innerWidth')).toBe(456);
|
||||
expect(await page.evaluate('window.innerHeight')).toBe(789);
|
||||
const screenshot = await page.screenshot({ fullPage: true });
|
||||
expect(screenshot).toBeInstanceOf(Buffer);
|
||||
expect(page.viewportSize().width).toBe(456);
|
||||
expect(page.viewportSize().height).toBe(789);
|
||||
expect(await page.evaluate('window.innerWidth')).toBe(456);
|
||||
expect(await page.evaluate('window.innerHeight')).toBe(789);
|
||||
await context.close();
|
||||
});
|
||||
it('should take element screenshot when default viewport is null and restore back', async({server, browser}) => {
|
||||
const context = await browser.newContext({viewport: null});
|
||||
const page = await context.newPage({ viewport: null });
|
||||
await page.setContent(`
|
||||
<div style="height: 14px">oooo</div>
|
||||
<style>
|
||||
div.to-screenshot {
|
||||
border: 1px solid blue;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
margin-left: 50px;
|
||||
}
|
||||
::-webkit-scrollbar{
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<div class="to-screenshot"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
<div class="to-screenshot"></div>
|
||||
`);
|
||||
const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
||||
const elementHandle = await page.$('div.to-screenshot');
|
||||
const screenshot = await elementHandle.screenshot();
|
||||
expect(screenshot).toBeInstanceOf(Buffer);
|
||||
const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
||||
expect(sizeBefore.width).toBe(sizeAfter.width);
|
||||
expect(sizeBefore.height).toBe(sizeAfter.height);
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,452 +16,446 @@
|
|||
*/
|
||||
|
||||
const utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT} = utils.testOptions(browserType);
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({playwright, FFOX, CHROMIUM, WEBKIT}) {
|
||||
describe('Page.waitFor', function() {
|
||||
it('should wait for selector', async({page, server}) => {
|
||||
let found = false;
|
||||
const waitFor = page.waitFor('div').then(() => found = true);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(found).toBe(false);
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await waitFor;
|
||||
expect(found).toBe(true);
|
||||
});
|
||||
it('should wait for an xpath', async({page, server}) => {
|
||||
let found = false;
|
||||
const waitFor = page.waitFor('//div').then(() => found = true);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(found).toBe(false);
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await waitFor;
|
||||
expect(found).toBe(true);
|
||||
});
|
||||
it('should not allow you to select an element with single slash xpath', async({page, server}) => {
|
||||
await page.setContent(`<div>some text</div>`);
|
||||
let error = null;
|
||||
await page.waitFor('/html/body/div').catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
});
|
||||
it('should timeout', async({page, server}) => {
|
||||
const startTime = Date.now();
|
||||
const timeout = 42;
|
||||
await page.waitFor(timeout);
|
||||
expect(Date.now() - startTime).not.toBeLessThan(timeout / 2);
|
||||
});
|
||||
it('should work with multiline body', async({page, server}) => {
|
||||
const result = await page.waitForFunction(`
|
||||
(() => true)()
|
||||
`);
|
||||
expect(await result.jsonValue()).toBe(true);
|
||||
});
|
||||
it('should wait for predicate', async({page, server}) => {
|
||||
await Promise.all([
|
||||
page.waitFor(() => window.innerWidth < 130), // Windows doesn't like windows below 120px wide
|
||||
page.setViewportSize({width: 10, height: 10}),
|
||||
]);
|
||||
});
|
||||
it('should throw when unknown type', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.waitFor({foo: 'bar'}).catch(e => error = e);
|
||||
expect(error.message).toContain('Unsupported target type');
|
||||
});
|
||||
it('should wait for predicate with arguments', async({page, server}) => {
|
||||
await page.waitFor(({arg1, arg2}) => arg1 + arg2 === 3, {}, { arg1: 1, arg2: 2});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.waitFor', function() {
|
||||
it('should wait for selector', async({page, server}) => {
|
||||
let found = false;
|
||||
const waitFor = page.waitFor('div').then(() => found = true);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(found).toBe(false);
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await waitFor;
|
||||
expect(found).toBe(true);
|
||||
});
|
||||
it('should wait for an xpath', async({page, server}) => {
|
||||
let found = false;
|
||||
const waitFor = page.waitFor('//div').then(() => found = true);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(found).toBe(false);
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await waitFor;
|
||||
expect(found).toBe(true);
|
||||
});
|
||||
it('should not allow you to select an element with single slash xpath', async({page, server}) => {
|
||||
await page.setContent(`<div>some text</div>`);
|
||||
let error = null;
|
||||
await page.waitFor('/html/body/div').catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
});
|
||||
it('should timeout', async({page, server}) => {
|
||||
const startTime = Date.now();
|
||||
const timeout = 42;
|
||||
await page.waitFor(timeout);
|
||||
expect(Date.now() - startTime).not.toBeLessThan(timeout / 2);
|
||||
});
|
||||
it('should work with multiline body', async({page, server}) => {
|
||||
const result = await page.waitForFunction(`
|
||||
(() => true)()
|
||||
`);
|
||||
expect(await result.jsonValue()).toBe(true);
|
||||
});
|
||||
it('should wait for predicate', async({page, server}) => {
|
||||
await Promise.all([
|
||||
page.waitFor(() => window.innerWidth < 130), // Windows doesn't like windows below 120px wide
|
||||
page.setViewportSize({width: 10, height: 10}),
|
||||
]);
|
||||
});
|
||||
it('should throw when unknown type', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.waitFor({foo: 'bar'}).catch(e => error = e);
|
||||
expect(error.message).toContain('Unsupported target type');
|
||||
});
|
||||
it('should wait for predicate with arguments', async({page, server}) => {
|
||||
await page.waitFor(({arg1, arg2}) => arg1 + arg2 === 3, {}, { arg1: 1, arg2: 2});
|
||||
describe('Frame.waitForFunction', function() {
|
||||
it('should accept a string', async({page, server}) => {
|
||||
const watchdog = page.waitForFunction('window.__FOO === 1');
|
||||
await page.evaluate(() => window.__FOO = 1);
|
||||
await watchdog;
|
||||
});
|
||||
it('should work when resolved right before execution context disposal', async({page, server}) => {
|
||||
await page.addInitScript(() => window.__RELOADED = true);
|
||||
await page.waitForFunction(() => {
|
||||
if (!window.__RELOADED)
|
||||
window.location.reload();
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Frame.waitForFunction', function() {
|
||||
it('should accept a string', async({page, server}) => {
|
||||
const watchdog = page.waitForFunction('window.__FOO === 1');
|
||||
await page.evaluate(() => window.__FOO = 1);
|
||||
await watchdog;
|
||||
});
|
||||
it('should work when resolved right before execution context disposal', async({page, server}) => {
|
||||
await page.addInitScript(() => window.__RELOADED = true);
|
||||
await page.waitForFunction(() => {
|
||||
if (!window.__RELOADED)
|
||||
window.location.reload();
|
||||
return true;
|
||||
});
|
||||
});
|
||||
it('should poll on interval', async({page, server}) => {
|
||||
const polling = 100;
|
||||
const timeDelta = await page.waitForFunction(() => {
|
||||
if (!window.__startTime) {
|
||||
window.__startTime = Date.now();
|
||||
return false;
|
||||
}
|
||||
return Date.now() - window.__startTime;
|
||||
}, {}, {polling});
|
||||
expect(timeDelta).not.toBeLessThan(polling);
|
||||
});
|
||||
it('should poll on mutation', async({page, server}) => {
|
||||
let success = false;
|
||||
const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'mutation'})
|
||||
.then(() => success = true);
|
||||
await page.evaluate(() => window.__FOO = 'hit');
|
||||
expect(success).toBe(false);
|
||||
await page.evaluate(() => document.body.appendChild(document.createElement('div')));
|
||||
await watchdog;
|
||||
});
|
||||
it('should poll on raf', async({page, server}) => {
|
||||
const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'raf'});
|
||||
await page.evaluate(() => window.__FOO = 'hit');
|
||||
await watchdog;
|
||||
});
|
||||
it('should work with strict CSP policy', async({page, server}) => {
|
||||
server.setCSP('/empty.html', 'script-src ' + server.PREFIX);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let error = null;
|
||||
await Promise.all([
|
||||
page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'raf'}).catch(e => error = e),
|
||||
page.evaluate(() => window.__FOO = 'hit')
|
||||
]);
|
||||
expect(error).toBe(null);
|
||||
});
|
||||
it('should throw on bad polling value', async({page, server}) => {
|
||||
let error = null;
|
||||
try {
|
||||
await page.waitForFunction(() => !!document.body, {}, {polling: 'unknown'});
|
||||
} catch (e) {
|
||||
error = e;
|
||||
it('should poll on interval', async({page, server}) => {
|
||||
const polling = 100;
|
||||
const timeDelta = await page.waitForFunction(() => {
|
||||
if (!window.__startTime) {
|
||||
window.__startTime = Date.now();
|
||||
return false;
|
||||
}
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('polling');
|
||||
});
|
||||
it('should throw negative polling interval', async({page, server}) => {
|
||||
let error = null;
|
||||
try {
|
||||
await page.waitForFunction(() => !!document.body, {}, {polling: -10});
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('Cannot poll with non-positive interval');
|
||||
});
|
||||
it('should return the success value as a JSHandle', async({page}) => {
|
||||
expect(await (await page.waitForFunction(() => 5)).jsonValue()).toBe(5);
|
||||
});
|
||||
it('should return the window as a success value', async({ page }) => {
|
||||
expect(await page.waitForFunction(() => window)).toBeTruthy();
|
||||
});
|
||||
it('should accept ElementHandle arguments', async({page}) => {
|
||||
await page.setContent('<div></div>');
|
||||
const div = await page.$('div');
|
||||
let resolved = false;
|
||||
const waitForFunction = page.waitForFunction(element => !element.parentElement, div).then(() => resolved = true);
|
||||
expect(resolved).toBe(false);
|
||||
await page.evaluate(element => element.remove(), div);
|
||||
await waitForFunction;
|
||||
});
|
||||
it('should respect timeout', async({page}) => {
|
||||
let error = null;
|
||||
await page.waitForFunction('false', {}, {timeout: 10}).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('waiting for function failed: timeout');
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should respect default timeout', async({page}) => {
|
||||
page.setDefaultTimeout(1);
|
||||
let error = null;
|
||||
await page.waitForFunction('false').catch(e => error = e);
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
expect(error.message).toContain('waiting for function failed: timeout');
|
||||
});
|
||||
it('should disable timeout when its set to 0', async({page}) => {
|
||||
const watchdog = page.waitForFunction(() => {
|
||||
window.__counter = (window.__counter || 0) + 1;
|
||||
return window.__injected;
|
||||
}, {}, {timeout: 0, polling: 10});
|
||||
await page.waitForFunction(() => window.__counter > 10);
|
||||
await page.evaluate(() => window.__injected = true);
|
||||
await watchdog;
|
||||
});
|
||||
it('should survive cross-process navigation', async({page, server}) => {
|
||||
let fooFound = false;
|
||||
const waitForFunction = page.waitForFunction('window.__FOO === 1').then(() => fooFound = true);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(fooFound).toBe(false);
|
||||
await page.reload();
|
||||
expect(fooFound).toBe(false);
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
expect(fooFound).toBe(false);
|
||||
await page.evaluate(() => window.__FOO = 1);
|
||||
await waitForFunction;
|
||||
expect(fooFound).toBe(true);
|
||||
});
|
||||
it('should survive navigations', async({page, server}) => {
|
||||
const watchdog = page.waitForFunction(() => window.__done);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.goto(server.PREFIX + '/consolelog.html');
|
||||
await page.evaluate(() => window.__done = true);
|
||||
await watchdog;
|
||||
});
|
||||
return Date.now() - window.__startTime;
|
||||
}, {}, {polling});
|
||||
expect(timeDelta).not.toBeLessThan(polling);
|
||||
});
|
||||
|
||||
describe('Frame.waitForSelector', function() {
|
||||
const addElement = tag => document.body.appendChild(document.createElement(tag));
|
||||
it('should immediately resolve promise if node exists', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
await frame.waitForSelector('*');
|
||||
await frame.evaluate(addElement, 'div');
|
||||
await frame.waitForSelector('div');
|
||||
});
|
||||
it('should work with removed MutationObserver', async({page, server}) => {
|
||||
await page.evaluate(() => delete window.MutationObserver);
|
||||
const [handle] = await Promise.all([
|
||||
page.waitForSelector('.zombo'),
|
||||
page.setContent(`<div class='zombo'>anything</div>`),
|
||||
]);
|
||||
expect(await page.evaluate(x => x.textContent, handle)).toBe('anything');
|
||||
});
|
||||
it('should resolve promise when node is added', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
const watchdog = frame.waitForSelector('div');
|
||||
await frame.evaluate(addElement, 'br');
|
||||
await frame.evaluate(addElement, 'div');
|
||||
const eHandle = await watchdog;
|
||||
const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue());
|
||||
expect(tagName).toBe('DIV');
|
||||
});
|
||||
it('should work when node is added through innerHTML', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const watchdog = page.waitForSelector('h3 div');
|
||||
await page.evaluate(addElement, 'span');
|
||||
await page.evaluate(() => document.querySelector('span').innerHTML = '<h3><div></div></h3>');
|
||||
await watchdog;
|
||||
});
|
||||
it('Page.$ waitFor is shortcut for main frame', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const otherFrame = page.frames()[1];
|
||||
const watchdog = page.waitForSelector('div');
|
||||
await otherFrame.evaluate(addElement, 'div');
|
||||
await page.evaluate(addElement, 'div');
|
||||
const eHandle = await watchdog;
|
||||
expect(await eHandle.ownerFrame()).toBe(page.mainFrame());
|
||||
});
|
||||
it('should run in specified frame', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
const frame1 = page.frames()[1];
|
||||
const frame2 = page.frames()[2];
|
||||
const waitForSelectorPromise = frame2.waitForSelector('div');
|
||||
await frame1.evaluate(addElement, 'div');
|
||||
await frame2.evaluate(addElement, 'div');
|
||||
const eHandle = await waitForSelectorPromise;
|
||||
expect(await eHandle.ownerFrame()).toBe(frame2);
|
||||
});
|
||||
it('should throw when frame is detached', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
let waitError = null;
|
||||
const waitPromise = frame.waitForSelector('.box').catch(e => waitError = e);
|
||||
await utils.detachFrame(page, 'frame1');
|
||||
await waitPromise;
|
||||
expect(waitError).toBeTruthy();
|
||||
expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
|
||||
});
|
||||
it('should survive cross-process navigation', async({page, server}) => {
|
||||
let boxFound = false;
|
||||
const waitForSelector = page.waitForSelector('.box').then(() => boxFound = true);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(boxFound).toBe(false);
|
||||
await page.reload();
|
||||
expect(boxFound).toBe(false);
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
await waitForSelector;
|
||||
expect(boxFound).toBe(true);
|
||||
});
|
||||
it('should wait for visible', async({page, server}) => {
|
||||
let divFound = false;
|
||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'visible' }).then(() => divFound = true);
|
||||
await page.setContent(`<div style='display: none; visibility: hidden;'>1</div>`);
|
||||
expect(divFound).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
|
||||
expect(divFound).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility'));
|
||||
expect(await waitForSelector).toBe(true);
|
||||
expect(divFound).toBe(true);
|
||||
});
|
||||
it('should wait for visible recursively', async({page, server}) => {
|
||||
let divVisible = false;
|
||||
const waitForSelector = page.waitForSelector('div#inner', { waitFor: 'visible' }).then(() => divVisible = true);
|
||||
await page.setContent(`<div style='display: none; visibility: hidden;'><div id="inner">hi</div></div>`);
|
||||
expect(divVisible).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
|
||||
expect(divVisible).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility'));
|
||||
expect(await waitForSelector).toBe(true);
|
||||
expect(divVisible).toBe(true);
|
||||
});
|
||||
it('hidden should wait for hidden', async({page, server}) => {
|
||||
let divHidden = false;
|
||||
await page.setContent(`<div style='display: block;'></div>`);
|
||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'hidden' }).then(() => divHidden = true);
|
||||
await page.waitForSelector('div'); // do a round trip
|
||||
expect(divHidden).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.setProperty('visibility', 'hidden'));
|
||||
expect(await waitForSelector).toBe(true);
|
||||
expect(divHidden).toBe(true);
|
||||
});
|
||||
it('hidden should wait for display: none', async({page, server}) => {
|
||||
let divHidden = false;
|
||||
await page.setContent(`<div style='display: block;'></div>`);
|
||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'hidden' }).then(() => divHidden = true);
|
||||
await page.waitForSelector('div'); // do a round trip
|
||||
expect(divHidden).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none'));
|
||||
expect(await waitForSelector).toBe(true);
|
||||
expect(divHidden).toBe(true);
|
||||
});
|
||||
it('hidden should wait for removal', async({page, server}) => {
|
||||
await page.setContent(`<div></div>`);
|
||||
let divRemoved = false;
|
||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'hidden' }).then(() => divRemoved = true);
|
||||
await page.waitForSelector('div'); // do a round trip
|
||||
expect(divRemoved).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').remove());
|
||||
expect(await waitForSelector).toBe(true);
|
||||
expect(divRemoved).toBe(true);
|
||||
});
|
||||
it('should return null if waiting to hide non-existing element', async({page, server}) => {
|
||||
const handle = await page.waitForSelector('non-existing', { waitFor: 'hidden' });
|
||||
expect(handle).toBe(null);
|
||||
});
|
||||
it('should respect timeout', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.waitForSelector('div', { timeout: 10 }).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('waiting for selector "div" failed: timeout');
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should have an error message specifically for awaiting an element to be hidden', async({page, server}) => {
|
||||
await page.setContent(`<div></div>`);
|
||||
let error = null;
|
||||
await page.waitForSelector('div', { waitFor: 'hidden', timeout: 10 }).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('waiting for selector "[hidden] div" failed: timeout');
|
||||
});
|
||||
it('should respond to node attribute mutation', async({page, server}) => {
|
||||
let divFound = false;
|
||||
const waitForSelector = page.waitForSelector('.zombo').then(() => divFound = true);
|
||||
await page.setContent(`<div class='notZombo'></div>`);
|
||||
expect(divFound).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').className = 'zombo');
|
||||
expect(await waitForSelector).toBe(true);
|
||||
});
|
||||
it('should return the element handle', async({page, server}) => {
|
||||
const waitForSelector = page.waitForSelector('.zombo');
|
||||
await page.setContent(`<div class='zombo'>anything</div>`);
|
||||
expect(await page.evaluate(x => x.textContent, await waitForSelector)).toBe('anything');
|
||||
});
|
||||
it('should have correct stack trace for timeout', async({page, server}) => {
|
||||
let error;
|
||||
await page.waitForSelector('.zombo', { timeout: 10 }).catch(e => error = e);
|
||||
expect(error.stack).toContain('waittask.spec.js');
|
||||
});
|
||||
it('should throw for unknown waitFor option', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const error = await page.waitForSelector('section', { waitFor: 'foo' }).catch(e => e);
|
||||
expect(error.message).toContain('Unsupported waitFor option');
|
||||
});
|
||||
it('should throw for visibility option', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const error = await page.waitForSelector('section', { visibility: 'hidden' }).catch(e => e);
|
||||
expect(error.message).toBe('options.visibility is not supported, did you mean options.waitFor?');
|
||||
});
|
||||
it('should throw for true waitFor option', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const error = await page.waitForSelector('section', { waitFor: true }).catch(e => e);
|
||||
expect(error.message).toContain('Unsupported waitFor option');
|
||||
});
|
||||
it('should throw for false waitFor option', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const error = await page.waitForSelector('section', { waitFor: false }).catch(e => e);
|
||||
expect(error.message).toContain('Unsupported waitFor option');
|
||||
});
|
||||
it('should support >> selector syntax', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
const watchdog = frame.waitForSelector('css=div >> css=span');
|
||||
await frame.evaluate(addElement, 'br');
|
||||
await frame.evaluate(addElement, 'div');
|
||||
await frame.evaluate(() => document.querySelector('div').appendChild(document.createElement('span')));
|
||||
const eHandle = await watchdog;
|
||||
const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue());
|
||||
expect(tagName).toBe('SPAN');
|
||||
});
|
||||
it('should wait for detached if already detached', async({page, server}) => {
|
||||
await page.setContent('<section id="testAttribute">43543</section>');
|
||||
expect(await page.waitForSelector('css=div', { waitFor: 'detached'})).toBe(null);
|
||||
});
|
||||
it('should wait for detached', async({page, server}) => {
|
||||
await page.setContent('<section id="testAttribute"><div>43543</div></section>');
|
||||
let done = false;
|
||||
const waitFor = page.waitForSelector('css=div', { waitFor: 'detached'}).then(() => done = true);
|
||||
expect(done).toBe(false);
|
||||
await page.waitForSelector('css=section');
|
||||
expect(done).toBe(false);
|
||||
await page.$eval('div', div => div.remove());
|
||||
expect(await waitFor).toBe(true);
|
||||
expect(done).toBe(true);
|
||||
});
|
||||
it('should poll on mutation', async({page, server}) => {
|
||||
let success = false;
|
||||
const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'mutation'})
|
||||
.then(() => success = true);
|
||||
await page.evaluate(() => window.__FOO = 'hit');
|
||||
expect(success).toBe(false);
|
||||
await page.evaluate(() => document.body.appendChild(document.createElement('div')));
|
||||
await watchdog;
|
||||
});
|
||||
|
||||
describe('Frame.waitForSelector xpath', function() {
|
||||
const addElement = tag => document.body.appendChild(document.createElement(tag));
|
||||
|
||||
it('should support some fancy xpath', async({page, server}) => {
|
||||
await page.setContent(`<p>red herring</p><p>hello world </p>`);
|
||||
const waitForXPath = page.waitForSelector('//p[normalize-space(.)="hello world"]');
|
||||
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('hello world ');
|
||||
});
|
||||
it('should respect timeout', async({page}) => {
|
||||
let error = null;
|
||||
await page.waitForSelector('//div', { timeout: 10 }).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('waiting for selector "//div" failed: timeout');
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should run in specified frame', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
const frame1 = page.frames()[1];
|
||||
const frame2 = page.frames()[2];
|
||||
const waitForXPathPromise = frame2.waitForSelector('//div');
|
||||
await frame1.evaluate(addElement, 'div');
|
||||
await frame2.evaluate(addElement, 'div');
|
||||
const eHandle = await waitForXPathPromise;
|
||||
expect(await eHandle.ownerFrame()).toBe(frame2);
|
||||
});
|
||||
it('should throw when frame is detached', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
let waitError = null;
|
||||
const waitPromise = frame.waitForSelector('//*[@class="box"]').catch(e => waitError = e);
|
||||
await utils.detachFrame(page, 'frame1');
|
||||
await waitPromise;
|
||||
expect(waitError).toBeTruthy();
|
||||
expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
|
||||
});
|
||||
it('should return the element handle', async({page, server}) => {
|
||||
const waitForXPath = page.waitForSelector('//*[@class="zombo"]');
|
||||
await page.setContent(`<div class='zombo'>anything</div>`);
|
||||
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('anything');
|
||||
});
|
||||
it('should allow you to select an element with single slash', async({page, server}) => {
|
||||
await page.setContent(`<div>some text</div>`);
|
||||
const waitForXPath = page.waitForSelector('//html/body/div');
|
||||
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text');
|
||||
});
|
||||
it('should poll on raf', async({page, server}) => {
|
||||
const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'raf'});
|
||||
await page.evaluate(() => window.__FOO = 'hit');
|
||||
await watchdog;
|
||||
});
|
||||
it('should work with strict CSP policy', async({page, server}) => {
|
||||
server.setCSP('/empty.html', 'script-src ' + server.PREFIX);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let error = null;
|
||||
await Promise.all([
|
||||
page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'raf'}).catch(e => error = e),
|
||||
page.evaluate(() => window.__FOO = 'hit')
|
||||
]);
|
||||
expect(error).toBe(null);
|
||||
});
|
||||
it('should throw on bad polling value', async({page, server}) => {
|
||||
let error = null;
|
||||
try {
|
||||
await page.waitForFunction(() => !!document.body, {}, {polling: 'unknown'});
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('polling');
|
||||
});
|
||||
it('should throw negative polling interval', async({page, server}) => {
|
||||
let error = null;
|
||||
try {
|
||||
await page.waitForFunction(() => !!document.body, {}, {polling: -10});
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('Cannot poll with non-positive interval');
|
||||
});
|
||||
it('should return the success value as a JSHandle', async({page}) => {
|
||||
expect(await (await page.waitForFunction(() => 5)).jsonValue()).toBe(5);
|
||||
});
|
||||
it('should return the window as a success value', async({ page }) => {
|
||||
expect(await page.waitForFunction(() => window)).toBeTruthy();
|
||||
});
|
||||
it('should accept ElementHandle arguments', async({page}) => {
|
||||
await page.setContent('<div></div>');
|
||||
const div = await page.$('div');
|
||||
let resolved = false;
|
||||
const waitForFunction = page.waitForFunction(element => !element.parentElement, div).then(() => resolved = true);
|
||||
expect(resolved).toBe(false);
|
||||
await page.evaluate(element => element.remove(), div);
|
||||
await waitForFunction;
|
||||
});
|
||||
it('should respect timeout', async({page}) => {
|
||||
let error = null;
|
||||
await page.waitForFunction('false', {}, {timeout: 10}).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('waiting for function failed: timeout');
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should respect default timeout', async({page}) => {
|
||||
page.setDefaultTimeout(1);
|
||||
let error = null;
|
||||
await page.waitForFunction('false').catch(e => error = e);
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
expect(error.message).toContain('waiting for function failed: timeout');
|
||||
});
|
||||
it('should disable timeout when its set to 0', async({page}) => {
|
||||
const watchdog = page.waitForFunction(() => {
|
||||
window.__counter = (window.__counter || 0) + 1;
|
||||
return window.__injected;
|
||||
}, {}, {timeout: 0, polling: 10});
|
||||
await page.waitForFunction(() => window.__counter > 10);
|
||||
await page.evaluate(() => window.__injected = true);
|
||||
await watchdog;
|
||||
});
|
||||
it('should survive cross-process navigation', async({page, server}) => {
|
||||
let fooFound = false;
|
||||
const waitForFunction = page.waitForFunction('window.__FOO === 1').then(() => fooFound = true);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(fooFound).toBe(false);
|
||||
await page.reload();
|
||||
expect(fooFound).toBe(false);
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
expect(fooFound).toBe(false);
|
||||
await page.evaluate(() => window.__FOO = 1);
|
||||
await waitForFunction;
|
||||
expect(fooFound).toBe(true);
|
||||
});
|
||||
it('should survive navigations', async({page, server}) => {
|
||||
const watchdog = page.waitForFunction(() => window.__done);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.goto(server.PREFIX + '/consolelog.html');
|
||||
await page.evaluate(() => window.__done = true);
|
||||
await watchdog;
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
describe('Frame.waitForSelector', function() {
|
||||
const addElement = tag => document.body.appendChild(document.createElement(tag));
|
||||
it('should immediately resolve promise if node exists', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
await frame.waitForSelector('*');
|
||||
await frame.evaluate(addElement, 'div');
|
||||
await frame.waitForSelector('div');
|
||||
});
|
||||
it('should work with removed MutationObserver', async({page, server}) => {
|
||||
await page.evaluate(() => delete window.MutationObserver);
|
||||
const [handle] = await Promise.all([
|
||||
page.waitForSelector('.zombo'),
|
||||
page.setContent(`<div class='zombo'>anything</div>`),
|
||||
]);
|
||||
expect(await page.evaluate(x => x.textContent, handle)).toBe('anything');
|
||||
});
|
||||
it('should resolve promise when node is added', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
const watchdog = frame.waitForSelector('div');
|
||||
await frame.evaluate(addElement, 'br');
|
||||
await frame.evaluate(addElement, 'div');
|
||||
const eHandle = await watchdog;
|
||||
const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue());
|
||||
expect(tagName).toBe('DIV');
|
||||
});
|
||||
it('should work when node is added through innerHTML', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const watchdog = page.waitForSelector('h3 div');
|
||||
await page.evaluate(addElement, 'span');
|
||||
await page.evaluate(() => document.querySelector('span').innerHTML = '<h3><div></div></h3>');
|
||||
await watchdog;
|
||||
});
|
||||
it('Page.$ waitFor is shortcut for main frame', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const otherFrame = page.frames()[1];
|
||||
const watchdog = page.waitForSelector('div');
|
||||
await otherFrame.evaluate(addElement, 'div');
|
||||
await page.evaluate(addElement, 'div');
|
||||
const eHandle = await watchdog;
|
||||
expect(await eHandle.ownerFrame()).toBe(page.mainFrame());
|
||||
});
|
||||
it('should run in specified frame', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
const frame1 = page.frames()[1];
|
||||
const frame2 = page.frames()[2];
|
||||
const waitForSelectorPromise = frame2.waitForSelector('div');
|
||||
await frame1.evaluate(addElement, 'div');
|
||||
await frame2.evaluate(addElement, 'div');
|
||||
const eHandle = await waitForSelectorPromise;
|
||||
expect(await eHandle.ownerFrame()).toBe(frame2);
|
||||
});
|
||||
it('should throw when frame is detached', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
let waitError = null;
|
||||
const waitPromise = frame.waitForSelector('.box').catch(e => waitError = e);
|
||||
await utils.detachFrame(page, 'frame1');
|
||||
await waitPromise;
|
||||
expect(waitError).toBeTruthy();
|
||||
expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
|
||||
});
|
||||
it('should survive cross-process navigation', async({page, server}) => {
|
||||
let boxFound = false;
|
||||
const waitForSelector = page.waitForSelector('.box').then(() => boxFound = true);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(boxFound).toBe(false);
|
||||
await page.reload();
|
||||
expect(boxFound).toBe(false);
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
await waitForSelector;
|
||||
expect(boxFound).toBe(true);
|
||||
});
|
||||
it('should wait for visible', async({page, server}) => {
|
||||
let divFound = false;
|
||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'visible' }).then(() => divFound = true);
|
||||
await page.setContent(`<div style='display: none; visibility: hidden;'>1</div>`);
|
||||
expect(divFound).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
|
||||
expect(divFound).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility'));
|
||||
expect(await waitForSelector).toBe(true);
|
||||
expect(divFound).toBe(true);
|
||||
});
|
||||
it('should wait for visible recursively', async({page, server}) => {
|
||||
let divVisible = false;
|
||||
const waitForSelector = page.waitForSelector('div#inner', { waitFor: 'visible' }).then(() => divVisible = true);
|
||||
await page.setContent(`<div style='display: none; visibility: hidden;'><div id="inner">hi</div></div>`);
|
||||
expect(divVisible).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
|
||||
expect(divVisible).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility'));
|
||||
expect(await waitForSelector).toBe(true);
|
||||
expect(divVisible).toBe(true);
|
||||
});
|
||||
it('hidden should wait for hidden', async({page, server}) => {
|
||||
let divHidden = false;
|
||||
await page.setContent(`<div style='display: block;'></div>`);
|
||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'hidden' }).then(() => divHidden = true);
|
||||
await page.waitForSelector('div'); // do a round trip
|
||||
expect(divHidden).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.setProperty('visibility', 'hidden'));
|
||||
expect(await waitForSelector).toBe(true);
|
||||
expect(divHidden).toBe(true);
|
||||
});
|
||||
it('hidden should wait for display: none', async({page, server}) => {
|
||||
let divHidden = false;
|
||||
await page.setContent(`<div style='display: block;'></div>`);
|
||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'hidden' }).then(() => divHidden = true);
|
||||
await page.waitForSelector('div'); // do a round trip
|
||||
expect(divHidden).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none'));
|
||||
expect(await waitForSelector).toBe(true);
|
||||
expect(divHidden).toBe(true);
|
||||
});
|
||||
it('hidden should wait for removal', async({page, server}) => {
|
||||
await page.setContent(`<div></div>`);
|
||||
let divRemoved = false;
|
||||
const waitForSelector = page.waitForSelector('div', { waitFor: 'hidden' }).then(() => divRemoved = true);
|
||||
await page.waitForSelector('div'); // do a round trip
|
||||
expect(divRemoved).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').remove());
|
||||
expect(await waitForSelector).toBe(true);
|
||||
expect(divRemoved).toBe(true);
|
||||
});
|
||||
it('should return null if waiting to hide non-existing element', async({page, server}) => {
|
||||
const handle = await page.waitForSelector('non-existing', { waitFor: 'hidden' });
|
||||
expect(handle).toBe(null);
|
||||
});
|
||||
it('should respect timeout', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.waitForSelector('div', { timeout: 10 }).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('waiting for selector "div" failed: timeout');
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should have an error message specifically for awaiting an element to be hidden', async({page, server}) => {
|
||||
await page.setContent(`<div></div>`);
|
||||
let error = null;
|
||||
await page.waitForSelector('div', { waitFor: 'hidden', timeout: 10 }).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('waiting for selector "[hidden] div" failed: timeout');
|
||||
});
|
||||
it('should respond to node attribute mutation', async({page, server}) => {
|
||||
let divFound = false;
|
||||
const waitForSelector = page.waitForSelector('.zombo').then(() => divFound = true);
|
||||
await page.setContent(`<div class='notZombo'></div>`);
|
||||
expect(divFound).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('div').className = 'zombo');
|
||||
expect(await waitForSelector).toBe(true);
|
||||
});
|
||||
it('should return the element handle', async({page, server}) => {
|
||||
const waitForSelector = page.waitForSelector('.zombo');
|
||||
await page.setContent(`<div class='zombo'>anything</div>`);
|
||||
expect(await page.evaluate(x => x.textContent, await waitForSelector)).toBe('anything');
|
||||
});
|
||||
it('should have correct stack trace for timeout', async({page, server}) => {
|
||||
let error;
|
||||
await page.waitForSelector('.zombo', { timeout: 10 }).catch(e => error = e);
|
||||
expect(error.stack).toContain('waittask.spec.js');
|
||||
});
|
||||
it('should throw for unknown waitFor option', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const error = await page.waitForSelector('section', { waitFor: 'foo' }).catch(e => e);
|
||||
expect(error.message).toContain('Unsupported waitFor option');
|
||||
});
|
||||
it('should throw for visibility option', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const error = await page.waitForSelector('section', { visibility: 'hidden' }).catch(e => e);
|
||||
expect(error.message).toBe('options.visibility is not supported, did you mean options.waitFor?');
|
||||
});
|
||||
it('should throw for true waitFor option', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const error = await page.waitForSelector('section', { waitFor: true }).catch(e => e);
|
||||
expect(error.message).toContain('Unsupported waitFor option');
|
||||
});
|
||||
it('should throw for false waitFor option', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const error = await page.waitForSelector('section', { waitFor: false }).catch(e => e);
|
||||
expect(error.message).toContain('Unsupported waitFor option');
|
||||
});
|
||||
it('should support >> selector syntax', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
const watchdog = frame.waitForSelector('css=div >> css=span');
|
||||
await frame.evaluate(addElement, 'br');
|
||||
await frame.evaluate(addElement, 'div');
|
||||
await frame.evaluate(() => document.querySelector('div').appendChild(document.createElement('span')));
|
||||
const eHandle = await watchdog;
|
||||
const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue());
|
||||
expect(tagName).toBe('SPAN');
|
||||
});
|
||||
it('should wait for detached if already detached', async({page, server}) => {
|
||||
await page.setContent('<section id="testAttribute">43543</section>');
|
||||
expect(await page.waitForSelector('css=div', { waitFor: 'detached'})).toBe(null);
|
||||
});
|
||||
it('should wait for detached', async({page, server}) => {
|
||||
await page.setContent('<section id="testAttribute"><div>43543</div></section>');
|
||||
let done = false;
|
||||
const waitFor = page.waitForSelector('css=div', { waitFor: 'detached'}).then(() => done = true);
|
||||
expect(done).toBe(false);
|
||||
await page.waitForSelector('css=section');
|
||||
expect(done).toBe(false);
|
||||
await page.$eval('div', div => div.remove());
|
||||
expect(await waitFor).toBe(true);
|
||||
expect(done).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Frame.waitForSelector xpath', function() {
|
||||
const addElement = tag => document.body.appendChild(document.createElement(tag));
|
||||
|
||||
it('should support some fancy xpath', async({page, server}) => {
|
||||
await page.setContent(`<p>red herring</p><p>hello world </p>`);
|
||||
const waitForXPath = page.waitForSelector('//p[normalize-space(.)="hello world"]');
|
||||
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('hello world ');
|
||||
});
|
||||
it('should respect timeout', async({page}) => {
|
||||
let error = null;
|
||||
await page.waitForSelector('//div', { timeout: 10 }).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('waiting for selector "//div" failed: timeout');
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should run in specified frame', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||
const frame1 = page.frames()[1];
|
||||
const frame2 = page.frames()[2];
|
||||
const waitForXPathPromise = frame2.waitForSelector('//div');
|
||||
await frame1.evaluate(addElement, 'div');
|
||||
await frame2.evaluate(addElement, 'div');
|
||||
const eHandle = await waitForXPathPromise;
|
||||
expect(await eHandle.ownerFrame()).toBe(frame2);
|
||||
});
|
||||
it('should throw when frame is detached', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
let waitError = null;
|
||||
const waitPromise = frame.waitForSelector('//*[@class="box"]').catch(e => waitError = e);
|
||||
await utils.detachFrame(page, 'frame1');
|
||||
await waitPromise;
|
||||
expect(waitError).toBeTruthy();
|
||||
expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
|
||||
});
|
||||
it('should return the element handle', async({page, server}) => {
|
||||
const waitForXPath = page.waitForSelector('//*[@class="zombo"]');
|
||||
await page.setContent(`<div class='zombo'>anything</div>`);
|
||||
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('anything');
|
||||
});
|
||||
it('should allow you to select an element with single slash', async({page, server}) => {
|
||||
await page.setContent(`<div>some text</div>`);
|
||||
const waitForXPath = page.waitForSelector('//html/body/div');
|
||||
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,126 +15,122 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {PageTestSuite}
|
||||
*/
|
||||
module.exports.describe = function({FFOX, CHROMIUM, WEBKIT, LINUX}) {
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
|
||||
describe('Workers', function() {
|
||||
it('Page.workers', async function({page, server}) {
|
||||
await Promise.all([
|
||||
page.waitForEvent('worker'),
|
||||
page.goto(server.PREFIX + '/worker/worker.html')]);
|
||||
const worker = page.workers()[0];
|
||||
expect(worker.url()).toContain('worker.js');
|
||||
describe('Workers', function() {
|
||||
it('Page.workers', async function({page, server}) {
|
||||
await Promise.all([
|
||||
page.waitForEvent('worker'),
|
||||
page.goto(server.PREFIX + '/worker/worker.html')]);
|
||||
const worker = page.workers()[0];
|
||||
expect(worker.url()).toContain('worker.js');
|
||||
|
||||
expect(await worker.evaluate(() => self['workerFunction']())).toBe('worker function result');
|
||||
expect(await worker.evaluate(() => self['workerFunction']())).toBe('worker function result');
|
||||
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(page.workers().length).toBe(0);
|
||||
});
|
||||
it('should emit created and destroyed events', async function({page}) {
|
||||
const workerCreatedPromise = page.waitForEvent('worker');
|
||||
const workerObj = await page.evaluateHandle(() => new Worker(URL.createObjectURL(new Blob(['1'], {type: 'application/javascript'}))));
|
||||
const worker = await workerCreatedPromise;
|
||||
const workerThisObj = await worker.evaluateHandle(() => this);
|
||||
const workerDestroyedPromise = new Promise(x => worker.once('close', x));
|
||||
await page.evaluate(workerObj => workerObj.terminate(), workerObj);
|
||||
expect(await workerDestroyedPromise).toBe(worker);
|
||||
const error = await workerThisObj.getProperty('self').catch(error => error);
|
||||
expect(error.message).toContain('Most likely the worker has been closed.');
|
||||
});
|
||||
it('should report console logs', async function({page}) {
|
||||
const [message] = await Promise.all([
|
||||
page.waitForEvent('console'),
|
||||
page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))),
|
||||
]);
|
||||
expect(message.text()).toBe('1');
|
||||
});
|
||||
it('should have JSHandles for console logs', async function({page}) {
|
||||
const logPromise = new Promise(x => page.on('console', x));
|
||||
await page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1,2,3,this)'], {type: 'application/javascript'}))));
|
||||
const log = await logPromise;
|
||||
expect(log.text()).toBe('1 2 3 JSHandle@object');
|
||||
expect(log.args().length).toBe(4);
|
||||
expect(await (await log.args()[3].getProperty('origin')).jsonValue()).toBe('null');
|
||||
});
|
||||
it('should evaluate', async function({page}) {
|
||||
const workerCreatedPromise = page.waitForEvent('worker');
|
||||
page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'}))));
|
||||
const worker = await workerCreatedPromise;
|
||||
expect(await worker.evaluate('1+1')).toBe(2);
|
||||
});
|
||||
it('should report errors', async function({page}) {
|
||||
const errorPromise = new Promise(x => page.on('pageerror', x));
|
||||
page.evaluate(() => new Worker(URL.createObjectURL(new Blob([`setTimeout(() => { throw new Error('this is my error'); })`], {type: 'application/javascript'}))));
|
||||
const errorLog = await errorPromise;
|
||||
expect(errorLog.message).toContain('this is my error');
|
||||
});
|
||||
it('should clear upon navigation', async function({server, page}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const workerCreatedPromise = page.waitForEvent('worker');
|
||||
page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'}))));
|
||||
const worker = await workerCreatedPromise;
|
||||
expect(page.workers().length).toBe(1);
|
||||
let destroyed = false;
|
||||
worker.once('close', () => destroyed = true);
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
expect(destroyed).toBe(true);
|
||||
expect(page.workers().length).toBe(0);
|
||||
});
|
||||
it('should clear upon cross-process navigation', async function({server, page}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const workerCreatedPromise = page.waitForEvent('worker');
|
||||
page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'}))));
|
||||
const worker = await workerCreatedPromise;
|
||||
expect(page.workers().length).toBe(1);
|
||||
let destroyed = false;
|
||||
worker.once('close', () => destroyed = true);
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
expect(destroyed).toBe(true);
|
||||
expect(page.workers().length).toBe(0);
|
||||
});
|
||||
it('should report network activity', async function({page, server}) {
|
||||
const [worker] = await Promise.all([
|
||||
page.waitForEvent('worker'),
|
||||
page.goto(server.PREFIX + '/worker/worker.html'),
|
||||
]);
|
||||
const url = server.PREFIX + '/one-style.css';
|
||||
const requestPromise = page.waitForRequest(url);
|
||||
const responsePromise = page.waitForResponse(url);
|
||||
await worker.evaluate(url => fetch(url).then(response => response.text()).then(console.log), url);
|
||||
const request = await requestPromise;
|
||||
const response = await responsePromise;
|
||||
expect(request.url()).toBe(url);
|
||||
expect(response.request()).toBe(request);
|
||||
expect(response.ok()).toBe(true);
|
||||
});
|
||||
it('should report network activity on worker creation', async function({page, server}) {
|
||||
// Chromium needs waitForDebugger enabled for this one.
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const url = server.PREFIX + '/one-style.css';
|
||||
const requestPromise = page.waitForRequest(url);
|
||||
const responsePromise = page.waitForResponse(url);
|
||||
await page.evaluate(url => new Worker(URL.createObjectURL(new Blob([`
|
||||
fetch("${url}").then(response => response.text()).then(console.log);
|
||||
`], {type: 'application/javascript'}))), url);
|
||||
const request = await requestPromise;
|
||||
const response = await responsePromise;
|
||||
expect(request.url()).toBe(url);
|
||||
expect(response.request()).toBe(request);
|
||||
expect(response.ok()).toBe(true);
|
||||
});
|
||||
it('should format number using context locale', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'ru-RU' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [worker] = await Promise.all([
|
||||
page.waitForEvent('worker'),
|
||||
page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))),
|
||||
]);
|
||||
expect(await worker.evaluate(() => (10000.20).toLocaleString())).toBe('10\u00A0000,2');
|
||||
await context.close();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(page.workers().length).toBe(0);
|
||||
});
|
||||
};
|
||||
it('should emit created and destroyed events', async function({page}) {
|
||||
const workerCreatedPromise = page.waitForEvent('worker');
|
||||
const workerObj = await page.evaluateHandle(() => new Worker(URL.createObjectURL(new Blob(['1'], {type: 'application/javascript'}))));
|
||||
const worker = await workerCreatedPromise;
|
||||
const workerThisObj = await worker.evaluateHandle(() => this);
|
||||
const workerDestroyedPromise = new Promise(x => worker.once('close', x));
|
||||
await page.evaluate(workerObj => workerObj.terminate(), workerObj);
|
||||
expect(await workerDestroyedPromise).toBe(worker);
|
||||
const error = await workerThisObj.getProperty('self').catch(error => error);
|
||||
expect(error.message).toContain('Most likely the worker has been closed.');
|
||||
});
|
||||
it('should report console logs', async function({page}) {
|
||||
const [message] = await Promise.all([
|
||||
page.waitForEvent('console'),
|
||||
page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))),
|
||||
]);
|
||||
expect(message.text()).toBe('1');
|
||||
});
|
||||
it('should have JSHandles for console logs', async function({page}) {
|
||||
const logPromise = new Promise(x => page.on('console', x));
|
||||
await page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1,2,3,this)'], {type: 'application/javascript'}))));
|
||||
const log = await logPromise;
|
||||
expect(log.text()).toBe('1 2 3 JSHandle@object');
|
||||
expect(log.args().length).toBe(4);
|
||||
expect(await (await log.args()[3].getProperty('origin')).jsonValue()).toBe('null');
|
||||
});
|
||||
it('should evaluate', async function({page}) {
|
||||
const workerCreatedPromise = page.waitForEvent('worker');
|
||||
page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'}))));
|
||||
const worker = await workerCreatedPromise;
|
||||
expect(await worker.evaluate('1+1')).toBe(2);
|
||||
});
|
||||
it('should report errors', async function({page}) {
|
||||
const errorPromise = new Promise(x => page.on('pageerror', x));
|
||||
page.evaluate(() => new Worker(URL.createObjectURL(new Blob([`setTimeout(() => { throw new Error('this is my error'); })`], {type: 'application/javascript'}))));
|
||||
const errorLog = await errorPromise;
|
||||
expect(errorLog.message).toContain('this is my error');
|
||||
});
|
||||
it('should clear upon navigation', async function({server, page}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const workerCreatedPromise = page.waitForEvent('worker');
|
||||
page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'}))));
|
||||
const worker = await workerCreatedPromise;
|
||||
expect(page.workers().length).toBe(1);
|
||||
let destroyed = false;
|
||||
worker.once('close', () => destroyed = true);
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
expect(destroyed).toBe(true);
|
||||
expect(page.workers().length).toBe(0);
|
||||
});
|
||||
it('should clear upon cross-process navigation', async function({server, page}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const workerCreatedPromise = page.waitForEvent('worker');
|
||||
page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'}))));
|
||||
const worker = await workerCreatedPromise;
|
||||
expect(page.workers().length).toBe(1);
|
||||
let destroyed = false;
|
||||
worker.once('close', () => destroyed = true);
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
expect(destroyed).toBe(true);
|
||||
expect(page.workers().length).toBe(0);
|
||||
});
|
||||
it('should report network activity', async function({page, server}) {
|
||||
const [worker] = await Promise.all([
|
||||
page.waitForEvent('worker'),
|
||||
page.goto(server.PREFIX + '/worker/worker.html'),
|
||||
]);
|
||||
const url = server.PREFIX + '/one-style.css';
|
||||
const requestPromise = page.waitForRequest(url);
|
||||
const responsePromise = page.waitForResponse(url);
|
||||
await worker.evaluate(url => fetch(url).then(response => response.text()).then(console.log), url);
|
||||
const request = await requestPromise;
|
||||
const response = await responsePromise;
|
||||
expect(request.url()).toBe(url);
|
||||
expect(response.request()).toBe(request);
|
||||
expect(response.ok()).toBe(true);
|
||||
});
|
||||
it('should report network activity on worker creation', async function({page, server}) {
|
||||
// Chromium needs waitForDebugger enabled for this one.
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const url = server.PREFIX + '/one-style.css';
|
||||
const requestPromise = page.waitForRequest(url);
|
||||
const responsePromise = page.waitForResponse(url);
|
||||
await page.evaluate(url => new Worker(URL.createObjectURL(new Blob([`
|
||||
fetch("${url}").then(response => response.text()).then(console.log);
|
||||
`], {type: 'application/javascript'}))), url);
|
||||
const request = await requestPromise;
|
||||
const response = await responsePromise;
|
||||
expect(request.url()).toBe(url);
|
||||
expect(response.request()).toBe(request);
|
||||
expect(response.ok()).toBe(true);
|
||||
});
|
||||
it('should format number using context locale', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'ru-RU' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [worker] = await Promise.all([
|
||||
page.waitForEvent('worker'),
|
||||
page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))),
|
||||
]);
|
||||
expect(await worker.evaluate(() => (10000.20).toLocaleString())).toBe('10\u00A0000,2');
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue