test: migrate cli tests to new folio (#6048)
This commit is contained in:
parent
561cb23e8d
commit
d0afa9d8de
|
|
@ -14,16 +14,18 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { folio } from './cli.fixtures';
|
||||
import { test, expect } from '../config/cliTest';
|
||||
import * as http from 'http';
|
||||
|
||||
const { it, describe, expect } = folio;
|
||||
test.describe('cli codegen', () => {
|
||||
test.beforeEach(async ({ mode, browserName, headful }) => {
|
||||
test.skip(mode !== 'default');
|
||||
test.fixme(browserName === 'firefox' && headful, 'Focus is off');
|
||||
});
|
||||
|
||||
test('should click', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
describe('cli codegen', (suite, { browserName, headful, mode }) => {
|
||||
suite.skip(mode !== 'default');
|
||||
suite.fixme(browserName === 'firefox' && headful, 'Focus is off');
|
||||
}, () => {
|
||||
it('should click', async ({ page, recorder }) => {
|
||||
await recorder.setContentAndWait(`<button onclick="console.log('click')">Submit</button>`);
|
||||
|
||||
const selector = await recorder.hoverOverElement('button');
|
||||
|
|
@ -58,7 +60,9 @@ await page.ClickAsync("text=Submit");`);
|
|||
expect(message.text()).toBe('click');
|
||||
});
|
||||
|
||||
it('should click after same-document navigation', async ({ page, recorder, httpServer }) => {
|
||||
test('should click after same-document navigation', async ({ page, openRecorder, httpServer }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
httpServer.setHandler((req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||
res.end('');
|
||||
|
|
@ -87,7 +91,9 @@ await page.ClickAsync("text=Submit");`);
|
|||
expect(message.text()).toBe('click');
|
||||
});
|
||||
|
||||
it('should work with TrustedTypes', async ({ page, recorder }) => {
|
||||
test('should work with TrustedTypes', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`
|
||||
<head>
|
||||
<meta http-equiv="Content-Security-Policy" content="trusted-types unsafe escape; require-trusted-types-for 'script'">
|
||||
|
|
@ -128,7 +134,9 @@ await page.ClickAsync("text=Submit");`);
|
|||
expect(message.text()).toBe('click');
|
||||
});
|
||||
|
||||
it('should not target selector preview by text regexp', async ({ page, recorder }) => {
|
||||
test('should not target selector preview by text regexp', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<span>dummy</span>`);
|
||||
|
||||
// Force highlight.
|
||||
|
|
@ -160,7 +168,9 @@ await page.ClickAsync("text=Submit");`);
|
|||
expect(message.text()).toBe('click');
|
||||
});
|
||||
|
||||
it('should fill', async ({ page, recorder }) => {
|
||||
test('should fill', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<input id="input" name="name" oninput="console.log(input.value)"></input>`);
|
||||
const selector = await recorder.focusElement('input');
|
||||
expect(selector).toBe('input[name="name"]');
|
||||
|
|
@ -193,7 +203,9 @@ await page.FillAsync(\"input[name=\\\"name\\\"]\", \"John\");`);
|
|||
expect(message.text()).toBe('John');
|
||||
});
|
||||
|
||||
it('should fill textarea', async ({ page, recorder }) => {
|
||||
test('should fill textarea', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<textarea id="textarea" name="name" oninput="console.log(textarea.value)"></textarea>`);
|
||||
const selector = await recorder.focusElement('textarea');
|
||||
expect(selector).toBe('textarea[name="name"]');
|
||||
|
|
@ -209,7 +221,9 @@ await page.FillAsync(\"input[name=\\\"name\\\"]\", \"John\");`);
|
|||
expect(message.text()).toBe('John');
|
||||
});
|
||||
|
||||
it('should press', async ({ page, recorder }) => {
|
||||
test('should press', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<input name="name" onkeypress="console.log('press')"></input>`);
|
||||
|
||||
const selector = await recorder.focusElement('input');
|
||||
|
|
@ -246,7 +260,9 @@ await page.PressAsync(\"input[name=\\\"name\\\"]\", \"Shift+Enter\");`);
|
|||
expect(messages[0].text()).toBe('press');
|
||||
});
|
||||
|
||||
it('should update selected element after pressing Tab', async ({ page, recorder }) => {
|
||||
test('should update selected element after pressing Tab', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`
|
||||
<input name="one"></input>
|
||||
<input name="two"></input>
|
||||
|
|
@ -276,7 +292,9 @@ await page.PressAsync(\"input[name=\\\"name\\\"]\", \"Shift+Enter\");`);
|
|||
await page.fill('input[name="two"]', 'barfoo321');`);
|
||||
});
|
||||
|
||||
it('should record ArrowDown', async ({ page, recorder }) => {
|
||||
test('should record ArrowDown', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<input name="name" onkeydown="console.log('press:' + event.key)"></input>`);
|
||||
|
||||
const selector = await recorder.focusElement('input');
|
||||
|
|
@ -297,7 +315,9 @@ await page.PressAsync(\"input[name=\\\"name\\\"]\", \"Shift+Enter\");`);
|
|||
expect(messages[0].text()).toBe('press:ArrowDown');
|
||||
});
|
||||
|
||||
it('should emit single keyup on ArrowDown', async ({ page, recorder }) => {
|
||||
test('should emit single keyup on ArrowDown', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<input name="name" onkeydown="console.log('down:' + event.key)" onkeyup="console.log('up:' + event.key)"></input>`);
|
||||
|
||||
const selector = await recorder.focusElement('input');
|
||||
|
|
@ -320,7 +340,9 @@ await page.PressAsync(\"input[name=\\\"name\\\"]\", \"Shift+Enter\");`);
|
|||
expect(messages[1].text()).toBe('up:ArrowDown');
|
||||
});
|
||||
|
||||
it('should check', async ({ page, recorder }) => {
|
||||
test('should check', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<input id="checkbox" type="checkbox" name="accept" onchange="console.log(checkbox.checked)"></input>`);
|
||||
|
||||
const selector = await recorder.focusElement('input');
|
||||
|
|
@ -355,7 +377,9 @@ await page.CheckAsync(\"input[name=\\\"accept\\\"]\");`);
|
|||
expect(message.text()).toBe('true');
|
||||
});
|
||||
|
||||
it('should check with keyboard', async ({ page, recorder }) => {
|
||||
test('should check with keyboard', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<input id="checkbox" type="checkbox" name="accept" onchange="console.log(checkbox.checked)"></input>`);
|
||||
|
||||
const selector = await recorder.focusElement('input');
|
||||
|
|
@ -373,7 +397,9 @@ await page.CheckAsync(\"input[name=\\\"accept\\\"]\");`);
|
|||
expect(message.text()).toBe('true');
|
||||
});
|
||||
|
||||
it('should uncheck', async ({ page, recorder }) => {
|
||||
test('should uncheck', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<input id="checkbox" type="checkbox" checked name="accept" onchange="console.log(checkbox.checked)"></input>`);
|
||||
|
||||
const selector = await recorder.focusElement('input');
|
||||
|
|
@ -408,7 +434,9 @@ await page.UncheckAsync(\"input[name=\\\"accept\\\"]\");`);
|
|||
expect(message.text()).toBe('false');
|
||||
});
|
||||
|
||||
it('should select', async ({ page, recorder }) => {
|
||||
test('should select', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait('<select id="age" onchange="console.log(age.selectedOptions[0].value)"><option value="1"><option value="2"></select>');
|
||||
|
||||
const selector = await recorder.hoverOverElement('select');
|
||||
|
|
@ -443,9 +471,10 @@ await page.SelectOptionAsync(\"select\", \"2\");`);
|
|||
expect(message.text()).toBe('2');
|
||||
});
|
||||
|
||||
it('should await popup', (test, { browserName, headful }) => {
|
||||
test('should await popup', async ({ page, openRecorder, browserName, headful }) => {
|
||||
test.fixme(browserName === 'webkit' && headful, 'Middle click does not open a popup in our webkit embedder');
|
||||
}, async ({ page, recorder }) => {
|
||||
|
||||
const recorder = await openRecorder();
|
||||
await recorder.setContentAndWait('<a target=_blank rel=noopener href="about:blank">link</a>');
|
||||
|
||||
const selector = await recorder.hoverOverElement('a');
|
||||
|
|
@ -491,7 +520,9 @@ await Task.WhenAll(
|
|||
expect(popup.url()).toBe('about:blank');
|
||||
});
|
||||
|
||||
it('should assert navigation', async ({ page, recorder }) => {
|
||||
test('should assert navigation', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<a onclick="window.location.href='about:blank#foo'">link</a>`);
|
||||
|
||||
const selector = await recorder.hoverOverElement('a');
|
||||
|
|
@ -531,7 +562,9 @@ await page.ClickAsync(\"text=link\");
|
|||
});
|
||||
|
||||
|
||||
it('should await navigation', async ({ page, recorder }) => {
|
||||
test('should await navigation', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<a onclick="setTimeout(() => window.location.href='about:blank#foo', 1000)">link</a>`);
|
||||
|
||||
const selector = await recorder.hoverOverElement('a');
|
||||
|
|
@ -14,16 +14,18 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { folio } from './cli.fixtures';
|
||||
import { test, expect } from '../config/cliTest';
|
||||
import * as http from 'http';
|
||||
import * as url from 'url';
|
||||
|
||||
const { it, describe, expect } = folio;
|
||||
test.describe('cli codegen', () => {
|
||||
test.beforeEach(async ({ mode }) => {
|
||||
test.skip(mode !== 'default');
|
||||
});
|
||||
|
||||
test('should contain open page', async ({ openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
describe('cli codegen', (suite, { mode }) => {
|
||||
suite.skip(mode !== 'default');
|
||||
}, () => {
|
||||
it('should contain open page', async ({ recorder }) => {
|
||||
await recorder.setContentAndWait(``);
|
||||
const sources = await recorder.waitForOutput('<javascript>', `page.goto`);
|
||||
|
||||
|
|
@ -48,9 +50,11 @@ describe('cli codegen', (suite, { mode }) => {
|
|||
var page = await context.NewPageAsync();`);
|
||||
});
|
||||
|
||||
it('should contain second page', async ({ context, recorder }) => {
|
||||
test('should contain second page', async ({ openRecorder, page }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(``);
|
||||
await context.newPage();
|
||||
await page.context().newPage();
|
||||
const sources = await recorder.waitForOutput('<javascript>', 'page1');
|
||||
|
||||
expect(sources.get('<javascript>').text).toContain(`
|
||||
|
|
@ -74,9 +78,11 @@ var page = await context.NewPageAsync();`);
|
|||
var page1 = await context.NewPageAsync();`);
|
||||
});
|
||||
|
||||
it('should contain close page', async ({ context, recorder }) => {
|
||||
test('should contain close page', async ({ openRecorder, page }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(``);
|
||||
await context.newPage();
|
||||
await page.context().newPage();
|
||||
await recorder.page.close();
|
||||
const sources = await recorder.waitForOutput('<javascript>', 'page.close();');
|
||||
|
||||
|
|
@ -96,9 +102,11 @@ var page1 = await context.NewPageAsync();`);
|
|||
await page.CloseAsync();`);
|
||||
});
|
||||
|
||||
it('should not lead to an error if html gets clicked', async ({ context, recorder }) => {
|
||||
test('should not lead to an error if html gets clicked', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait('');
|
||||
await context.newPage();
|
||||
await page.context().newPage();
|
||||
const errors: any[] = [];
|
||||
recorder.page.on('pageerror', e => errors.push(e));
|
||||
await recorder.page.evaluate(() => document.querySelector('body').remove());
|
||||
|
|
@ -109,9 +117,10 @@ await page.CloseAsync();`);
|
|||
expect(errors.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should upload a single file', (test, { browserName }) => {
|
||||
test('should upload a single file', async ({ page, openRecorder, browserName }) => {
|
||||
test.fixme(browserName === 'firefox', 'Hangs');
|
||||
}, async ({ page, recorder }) => {
|
||||
|
||||
const recorder = await openRecorder();
|
||||
await recorder.setContentAndWait(`
|
||||
<form>
|
||||
<input type="file">
|
||||
|
|
@ -146,9 +155,10 @@ await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", \"file-to-upload.txt
|
|||
|
||||
});
|
||||
|
||||
it('should upload multiple files', (test, { browserName }) => {
|
||||
test('should upload multiple files', async ({ page, openRecorder, browserName }) => {
|
||||
test.fixme(browserName === 'firefox', 'Hangs');
|
||||
}, async ({ page, recorder }) => {
|
||||
|
||||
const recorder = await openRecorder();
|
||||
await recorder.setContentAndWait(`
|
||||
<form>
|
||||
<input type="file" multiple>
|
||||
|
|
@ -182,9 +192,10 @@ await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", \"file-to-upload.txt
|
|||
await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", new[] { \"file-to-upload.txt\", \"file-to-upload-2.txt\" });`);
|
||||
});
|
||||
|
||||
it('should clear files', (test, { browserName }) => {
|
||||
test('should clear files', async ({ page, openRecorder, browserName }) => {
|
||||
test.fixme(browserName === 'firefox', 'Hangs');
|
||||
}, async ({ page, recorder }) => {
|
||||
|
||||
const recorder = await openRecorder();
|
||||
await recorder.setContentAndWait(`
|
||||
<form>
|
||||
<input type="file" multiple>
|
||||
|
|
@ -219,7 +230,9 @@ await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", new[] { });`);
|
|||
|
||||
});
|
||||
|
||||
it('should download files', async ({ page, recorder, httpServer }) => {
|
||||
test('should download files', async ({ page, openRecorder, httpServer }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
httpServer.setHandler((req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||
const pathName = url.parse(req.url!).path;
|
||||
if (pathName === '/download') {
|
||||
|
|
@ -274,7 +287,9 @@ await Task.WhenAll(
|
|||
page.ClickAsync(\"text=Download\"));`);
|
||||
});
|
||||
|
||||
it('should handle dialogs', async ({ page, recorder }) => {
|
||||
test('should handle dialogs', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`
|
||||
<button onclick="alert()">click me</button>
|
||||
`);
|
||||
|
|
@ -325,7 +340,9 @@ await page.ClickAsync(\"text=click me\");`);
|
|||
|
||||
});
|
||||
|
||||
it('should handle history.postData', async ({ page, recorder, httpServer }) => {
|
||||
test('should handle history.postData', async ({ page, openRecorder, httpServer }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
httpServer.setHandler((req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||
res.end('Hello world');
|
||||
|
|
@ -343,9 +360,10 @@ await page.ClickAsync(\"text=click me\");`);
|
|||
}
|
||||
});
|
||||
|
||||
it('should record open in a new tab with url', (test, { browserName }) => {
|
||||
test('should record open in a new tab with url', async ({ page, openRecorder, browserName, platform }) => {
|
||||
test.fixme(browserName === 'webkit', 'Ctrl+click does not open in new tab on WebKit');
|
||||
}, async ({ page, recorder, browserName, platform }) => {
|
||||
|
||||
const recorder = await openRecorder();
|
||||
await recorder.setContentAndWait(`<a href="about:blank?foo">link</a>`);
|
||||
|
||||
const selector = await recorder.hoverOverElement('a');
|
||||
|
|
@ -371,9 +389,10 @@ await page.ClickAsync(\"text=click me\");`);
|
|||
}
|
||||
});
|
||||
|
||||
it('should not clash pages', (test, { browserName }) => {
|
||||
test('should not clash pages', async ({ page, openRecorder, browserName }) => {
|
||||
test.fixme(browserName === 'firefox', 'Times out on Firefox, maybe the focus issue');
|
||||
}, async ({ page, recorder }) => {
|
||||
|
||||
const recorder = await openRecorder();
|
||||
const [popup1] = await Promise.all([
|
||||
page.context().waitForEvent('page'),
|
||||
page.evaluate(`window.open('about:blank')`)
|
||||
|
|
@ -409,7 +428,9 @@ await page.ClickAsync(\"text=click me\");`);
|
|||
expect(sources.get('<csharp>').text).toContain(`await page2.FillAsync(\"input\", \"TextB\");`);
|
||||
});
|
||||
|
||||
it('click should emit events in order', async ({ page, recorder }) => {
|
||||
test('click should emit events in order', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`
|
||||
<button id=button>
|
||||
<script>
|
||||
|
|
@ -428,7 +449,9 @@ await page.ClickAsync(\"text=click me\");`);
|
|||
expect(messages).toEqual(['mousedown', 'mouseup', 'click']);
|
||||
});
|
||||
|
||||
it('should update hover model on action', async ({ page, recorder }) => {
|
||||
test('should update hover model on action', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<input id="checkbox" type="checkbox" name="accept" onchange="checkbox.name='updated'"></input>`);
|
||||
const [ models ] = await Promise.all([
|
||||
recorder.waitForActionPerformed(),
|
||||
|
|
@ -437,10 +460,11 @@ await page.ClickAsync(\"text=click me\");`);
|
|||
expect(models.hovered).toBe('input[name="updated"]');
|
||||
});
|
||||
|
||||
it('should update active model on action', (test, { browserName, headful }) => {
|
||||
test('should update active model on action', async ({ page, openRecorder, browserName, headful }) => {
|
||||
test.fixme(browserName === 'webkit' && !headful);
|
||||
test.fixme(browserName === 'firefox' && !headful);
|
||||
}, async ({ page, recorder }) => {
|
||||
|
||||
const recorder = await openRecorder();
|
||||
await recorder.setContentAndWait(`<input id="checkbox" type="checkbox" name="accept" onchange="checkbox.name='updated'"></input>`);
|
||||
const [ models ] = await Promise.all([
|
||||
recorder.waitForActionPerformed(),
|
||||
|
|
@ -449,7 +473,8 @@ await page.ClickAsync(\"text=click me\");`);
|
|||
expect(models.active).toBe('input[name="updated"]');
|
||||
});
|
||||
|
||||
it('should check input with chaning id', async ({ page, recorder }) => {
|
||||
test('should check input with chaning id', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
await recorder.setContentAndWait(`<input id="checkbox" type="checkbox" name="accept" onchange="checkbox.name = 'updated'"></input>`);
|
||||
await Promise.all([
|
||||
recorder.waitForActionPerformed(),
|
||||
|
|
@ -457,7 +482,8 @@ await page.ClickAsync(\"text=click me\");`);
|
|||
]);
|
||||
});
|
||||
|
||||
it('should prefer frame name', async ({ page, recorder, server }) => {
|
||||
test('should prefer frame name', async ({ page, openRecorder, server }) => {
|
||||
const recorder = await openRecorder();
|
||||
await recorder.setContentAndWait(`
|
||||
<iframe src='./frames/frame.html' name='one'></iframe>
|
||||
<iframe src='./frames/frame.html' name='two'></iframe>
|
||||
|
|
@ -549,7 +575,8 @@ await page.GetFrame(name: \"two\").ClickAsync(\"text=Hi, I'm frame\");`);
|
|||
await page.GetFrame(url: \"http://localhost:${server.PORT}/frames/frame.html\").ClickAsync(\"text=Hi, I'm frame\");`);
|
||||
});
|
||||
|
||||
it('should record navigations after identical pushState', async ({ page, recorder, httpServer }) => {
|
||||
test('should record navigations after identical pushState', async ({ page, openRecorder, httpServer }) => {
|
||||
const recorder = await openRecorder();
|
||||
httpServer.setHandler((req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||
res.end('Hello world');
|
||||
|
|
@ -16,11 +16,9 @@
|
|||
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { folio } from './cli.fixtures';
|
||||
import { test, expect } from '../config/cliTest';
|
||||
|
||||
const { it, expect } = folio;
|
||||
|
||||
const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString();
|
||||
const emptyHTML = new URL('file://' + path.join(__dirname, '..', '..', 'test', 'assets', 'empty.html')).toString();
|
||||
const launchOptions = (channel: string) => {
|
||||
return channel ? `headless: false,\n channel: "${channel}"` : 'headless: false';
|
||||
};
|
||||
|
|
@ -29,7 +27,7 @@ function capitalize(browserName: string): string {
|
|||
return browserName[0].toUpperCase() + browserName.slice(1);
|
||||
}
|
||||
|
||||
it('should print the correct imports and context options', async ({ browserName, browserChannel, runCLI }) => {
|
||||
test('should print the correct imports and context options', async ({ browserName, browserChannel, runCLI }) => {
|
||||
const cli = runCLI(['--target=csharp', emptyHTML]);
|
||||
const expectedResult = `await Playwright.InstallAsync();
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
|
|
@ -41,7 +39,7 @@ var context = await browser.NewContextAsync();`;
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options for custom settings', async ({ browserName, browserChannel, runCLI }) => {
|
||||
test('should print the correct context options for custom settings', async ({ browserName, browserChannel, runCLI }) => {
|
||||
const cli = runCLI([
|
||||
'--color-scheme=dark',
|
||||
'--geolocation=37.819722,-122.478611',
|
||||
|
|
@ -81,9 +79,9 @@ var context = await browser.NewContextAsync(
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options when using a device', (test, { browserName }) => {
|
||||
test('should print the correct context options when using a device', async ({ browserName, browserChannel, runCLI }) => {
|
||||
test.skip(browserName !== 'chromium');
|
||||
}, async ({ browserChannel, runCLI }) => {
|
||||
|
||||
const cli = runCLI(['--device=Pixel 2', '--target=csharp', emptyHTML]);
|
||||
const expectedResult = `await Playwright.InstallAsync();
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
|
|
@ -95,9 +93,9 @@ var context = await browser.NewContextAsync(playwright.Devices["Pixel 2"]);`;
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options when using a device and additional options', (test, { browserName }) => {
|
||||
test('should print the correct context options when using a device and additional options', async ({ browserName, browserChannel, runCLI }) => {
|
||||
test.skip(browserName !== 'webkit');
|
||||
}, async ({ browserChannel, runCLI }) => {
|
||||
|
||||
const cli = runCLI([
|
||||
'--device=iPhone 11',
|
||||
'--color-scheme=dark',
|
||||
|
|
@ -141,7 +139,7 @@ var context = await browser.NewContextAsync(new BrowserContextOptions(playwright
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print load/save storageState', async ({ browserName, browserChannel, runCLI, testInfo }) => {
|
||||
test('should print load/save storageState', async ({ browserName, browserChannel, runCLI }, testInfo) => {
|
||||
const loadFileName = testInfo.outputPath('load.json');
|
||||
const saveFileName = testInfo.outputPath('save.json');
|
||||
await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8');
|
||||
|
|
@ -16,16 +16,14 @@
|
|||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { folio } from './cli.fixtures';
|
||||
import { test, expect } from '../config/cliTest';
|
||||
|
||||
const { it, expect } = folio;
|
||||
|
||||
const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString();
|
||||
const emptyHTML = new URL('file://' + path.join(__dirname, '..', '..', 'test', 'assets', 'empty.html')).toString();
|
||||
const launchOptions = (channel: string) => {
|
||||
return channel ? `.setHeadless(false)\n .setChannel("${channel}")` : '.setHeadless(false)';
|
||||
};
|
||||
|
||||
it('should print the correct imports and context options', async ({ runCLI, browserChannel, browserName }) => {
|
||||
test('should print the correct imports and context options', async ({ runCLI, browserChannel, browserName }) => {
|
||||
const cli = runCLI(['--target=java', emptyHTML]);
|
||||
const expectedResult = `import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.*;
|
||||
|
|
@ -40,7 +38,7 @@ public class Example {
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options for custom settings', async ({ runCLI, browserName }) => {
|
||||
test('should print the correct context options for custom settings', async ({ runCLI, browserName }) => {
|
||||
const cli = runCLI(['--color-scheme=light', '--target=java', emptyHTML]);
|
||||
const expectedResult = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.setColorScheme(ColorScheme.LIGHT));`;
|
||||
|
|
@ -48,9 +46,9 @@ it('should print the correct context options for custom settings', async ({ runC
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options when using a device', (test, { browserName }) => {
|
||||
test('should print the correct context options when using a device', async ({ browserName, runCLI }) => {
|
||||
test.skip(browserName !== 'chromium');
|
||||
}, async ({ runCLI }) => {
|
||||
|
||||
const cli = runCLI(['--device=Pixel 2', '--target=java', emptyHTML]);
|
||||
const expectedResult = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.setUserAgent("Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36")
|
||||
|
|
@ -62,9 +60,9 @@ it('should print the correct context options when using a device', (test, { brow
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options when using a device and additional options', (test, { browserName }) => {
|
||||
test('should print the correct context options when using a device and additional options', async ({ browserName, runCLI }) => {
|
||||
test.skip(browserName !== 'webkit');
|
||||
}, async ({ runCLI }) => {
|
||||
|
||||
const cli = runCLI(['--color-scheme=light', '--device=iPhone 11', '--target=java', emptyHTML]);
|
||||
const expectedResult = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.setColorScheme(ColorScheme.LIGHT)
|
||||
|
|
@ -77,7 +75,7 @@ it('should print the correct context options when using a device and additional
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print load/save storage_state', async ({ runCLI, browserName, testInfo }) => {
|
||||
test('should print load/save storage_state', async ({ runCLI, browserName }, testInfo) => {
|
||||
const loadFileName = testInfo.outputPath('load.json');
|
||||
const saveFileName = testInfo.outputPath('save.json');
|
||||
await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8');
|
||||
|
|
@ -16,17 +16,15 @@
|
|||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { folio } from './cli.fixtures';
|
||||
import { test, expect } from '../config/cliTest';
|
||||
|
||||
const { it, expect } = folio;
|
||||
|
||||
const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString();
|
||||
const emptyHTML = new URL('file://' + path.join(__dirname, '..', '..', 'test', 'assets', 'empty.html')).toString();
|
||||
|
||||
const launchOptions = (channel: string) => {
|
||||
return channel ? `headless: false,\n channel: '${channel}'` : 'headless: false';
|
||||
};
|
||||
|
||||
it('should print the correct imports and context options', async ({ browserName, browserChannel, runCLI }) => {
|
||||
test('should print the correct imports and context options', async ({ browserName, browserChannel, runCLI }) => {
|
||||
const cli = runCLI([emptyHTML]);
|
||||
const expectedResult = `const { ${browserName} } = require('playwright');
|
||||
|
||||
|
|
@ -39,7 +37,7 @@ it('should print the correct imports and context options', async ({ browserName,
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options for custom settings', async ({ browserName, browserChannel, runCLI }) => {
|
||||
test('should print the correct context options for custom settings', async ({ browserName, browserChannel, runCLI }) => {
|
||||
const cli = runCLI(['--color-scheme=light', emptyHTML]);
|
||||
const expectedResult = `const { ${browserName} } = require('playwright');
|
||||
|
||||
|
|
@ -55,9 +53,9 @@ it('should print the correct context options for custom settings', async ({ brow
|
|||
});
|
||||
|
||||
|
||||
it('should print the correct context options when using a device', (test, { browserName, browserChannel }) => {
|
||||
test('should print the correct context options when using a device', async ({ browserName, browserChannel, runCLI }) => {
|
||||
test.skip(browserName !== 'chromium');
|
||||
}, async ({ browserChannel, runCLI }) => {
|
||||
|
||||
const cli = runCLI(['--device=Pixel 2', emptyHTML]);
|
||||
const expectedResult = `const { chromium, devices } = require('playwright');
|
||||
|
||||
|
|
@ -72,9 +70,9 @@ it('should print the correct context options when using a device', (test, { brow
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options when using a device and additional options', (test, { browserName }) => {
|
||||
test('should print the correct context options when using a device and additional options', async ({ browserName, browserChannel, runCLI }) => {
|
||||
test.skip(browserName !== 'webkit');
|
||||
}, async ({ browserChannel, runCLI }) => {
|
||||
|
||||
const cli = runCLI(['--color-scheme=light', '--device=iPhone 11', emptyHTML]);
|
||||
const expectedResult = `const { webkit, devices } = require('playwright');
|
||||
|
||||
|
|
@ -90,7 +88,7 @@ it('should print the correct context options when using a device and additional
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should save the codegen output to a file if specified', async ({ browserName, browserChannel, runCLI, testInfo }) => {
|
||||
test('should save the codegen output to a file if specified', async ({ browserName, browserChannel, runCLI }, testInfo) => {
|
||||
const tmpFile = testInfo.outputPath('script.js');
|
||||
const cli = runCLI(['--output', tmpFile, emptyHTML]);
|
||||
await cli.exited;
|
||||
|
|
@ -118,7 +116,7 @@ it('should save the codegen output to a file if specified', async ({ browserName
|
|||
})();`);
|
||||
});
|
||||
|
||||
it('should print load/save storageState', async ({ browserName, browserChannel, runCLI, testInfo }) => {
|
||||
test('should print load/save storageState', async ({ browserName, browserChannel, runCLI }, testInfo) => {
|
||||
const loadFileName = testInfo.outputPath('load.json');
|
||||
const saveFileName = testInfo.outputPath('save.json');
|
||||
await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8');
|
||||
|
|
@ -16,16 +16,14 @@
|
|||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { folio } from './cli.fixtures';
|
||||
import { test, expect } from '../config/cliTest';
|
||||
|
||||
const { it, expect } = folio;
|
||||
|
||||
const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString();
|
||||
const emptyHTML = new URL('file://' + path.join(__dirname, '..', '..', 'test', 'assets', 'empty.html')).toString();
|
||||
const launchOptions = (channel: string) => {
|
||||
return channel ? `headless=False, channel="${channel}"` : 'headless=False';
|
||||
};
|
||||
|
||||
it('should print the correct imports and context options', async ({ browserName, browserChannel, runCLI }) => {
|
||||
test('should print the correct imports and context options', async ({ browserName, browserChannel, runCLI }) => {
|
||||
const cli = runCLI(['--target=python-async', emptyHTML]);
|
||||
const expectedResult = `import asyncio
|
||||
from playwright.async_api import async_playwright
|
||||
|
|
@ -37,7 +35,7 @@ async def run(playwright):
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options for custom settings', async ({ browserName, browserChannel, runCLI }) => {
|
||||
test('should print the correct context options for custom settings', async ({ browserName, browserChannel, runCLI }) => {
|
||||
const cli = runCLI(['--color-scheme=light', '--target=python-async', emptyHTML]);
|
||||
const expectedResult = `import asyncio
|
||||
from playwright.async_api import async_playwright
|
||||
|
|
@ -49,9 +47,9 @@ async def run(playwright):
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options when using a device', (test, { browserName }) => {
|
||||
test('should print the correct context options when using a device', async ({ browserName, browserChannel, runCLI }) => {
|
||||
test.skip(browserName !== 'chromium');
|
||||
}, async ({ browserChannel, runCLI }) => {
|
||||
|
||||
const cli = runCLI(['--device=Pixel 2', '--target=python-async', emptyHTML]);
|
||||
const expectedResult = `import asyncio
|
||||
from playwright.async_api import async_playwright
|
||||
|
|
@ -63,9 +61,9 @@ async def run(playwright):
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options when using a device and additional options', (test, { browserName }) => {
|
||||
test('should print the correct context options when using a device and additional options', async ({ browserName, browserChannel, runCLI }) => {
|
||||
test.skip(browserName !== 'webkit');
|
||||
}, async ({ browserChannel, runCLI }) => {
|
||||
|
||||
const cli = runCLI(['--color-scheme=light', '--device=iPhone 11', '--target=python-async', emptyHTML]);
|
||||
const expectedResult = `import asyncio
|
||||
from playwright.async_api import async_playwright
|
||||
|
|
@ -77,7 +75,7 @@ async def run(playwright):
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should save the codegen output to a file if specified', async ({ browserName, browserChannel, runCLI, testInfo }) => {
|
||||
test('should save the codegen output to a file if specified', async ({ browserName, browserChannel, runCLI }, testInfo) => {
|
||||
const tmpFile = testInfo.outputPath('script.js');
|
||||
const cli = runCLI(['--target=python-async', '--output', tmpFile, emptyHTML]);
|
||||
await cli.exited;
|
||||
|
|
@ -108,7 +106,7 @@ async def main():
|
|||
asyncio.run(main())`);
|
||||
});
|
||||
|
||||
it('should print load/save storage_state', async ({ browserName, browserChannel, runCLI, testInfo }) => {
|
||||
test('should print load/save storage_state', async ({ browserName, browserChannel, runCLI }, testInfo) => {
|
||||
const loadFileName = testInfo.outputPath('load.json');
|
||||
const saveFileName = testInfo.outputPath('save.json');
|
||||
await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8');
|
||||
|
|
@ -16,16 +16,14 @@
|
|||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { folio } from './cli.fixtures';
|
||||
import { test, expect } from '../config/cliTest';
|
||||
|
||||
const { it, expect } = folio;
|
||||
|
||||
const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString();
|
||||
const emptyHTML = new URL('file://' + path.join(__dirname, '..', '..', 'test', 'assets', 'empty.html')).toString();
|
||||
const launchOptions = (channel: string) => {
|
||||
return channel ? `headless=False, channel="${channel}"` : 'headless=False';
|
||||
};
|
||||
|
||||
it('should print the correct imports and context options', async ({ runCLI, browserChannel, browserName }) => {
|
||||
test('should print the correct imports and context options', async ({ runCLI, browserChannel, browserName }) => {
|
||||
const cli = runCLI(['--target=python', emptyHTML]);
|
||||
const expectedResult = `from playwright.sync_api import sync_playwright
|
||||
|
||||
|
|
@ -36,7 +34,7 @@ def run(playwright):
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options for custom settings', async ({ runCLI, browserChannel, browserName }) => {
|
||||
test('should print the correct context options for custom settings', async ({ runCLI, browserChannel, browserName }) => {
|
||||
const cli = runCLI(['--color-scheme=light', '--target=python', emptyHTML]);
|
||||
const expectedResult = `from playwright.sync_api import sync_playwright
|
||||
|
||||
|
|
@ -47,9 +45,9 @@ def run(playwright):
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options when using a device', (test, { browserName }) => {
|
||||
test('should print the correct context options when using a device', async ({ browserName, browserChannel, runCLI }) => {
|
||||
test.skip(browserName !== 'chromium');
|
||||
}, async ({ browserChannel, runCLI }) => {
|
||||
|
||||
const cli = runCLI(['--device=Pixel 2', '--target=python', emptyHTML]);
|
||||
const expectedResult = `from playwright.sync_api import sync_playwright
|
||||
|
||||
|
|
@ -60,9 +58,9 @@ def run(playwright):
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options when using a device and additional options', (test, { browserName }) => {
|
||||
test('should print the correct context options when using a device and additional options', async ({ browserName, browserChannel, runCLI }) => {
|
||||
test.skip(browserName !== 'webkit');
|
||||
}, async ({ browserChannel, runCLI }) => {
|
||||
|
||||
const cli = runCLI(['--color-scheme=light', '--device=iPhone 11', '--target=python', emptyHTML]);
|
||||
const expectedResult = `from playwright.sync_api import sync_playwright
|
||||
|
||||
|
|
@ -73,7 +71,7 @@ def run(playwright):
|
|||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should save the codegen output to a file if specified', async ({ runCLI, browserChannel, browserName, testInfo }) => {
|
||||
test('should save the codegen output to a file if specified', async ({ runCLI, browserChannel, browserName }, testInfo) => {
|
||||
const tmpFile = testInfo.outputPath('script.js');
|
||||
const cli = runCLI(['--target=python', '--output', tmpFile, emptyHTML]);
|
||||
await cli.exited;
|
||||
|
|
@ -101,7 +99,7 @@ with sync_playwright() as playwright:
|
|||
run(playwright)`);
|
||||
});
|
||||
|
||||
it('should print load/save storage_state', async ({ runCLI, browserChannel, browserName, testInfo }) => {
|
||||
test('should print load/save storage_state', async ({ runCLI, browserChannel, browserName }, testInfo) => {
|
||||
const loadFileName = testInfo.outputPath('load.json');
|
||||
const saveFileName = testInfo.outputPath('save.json');
|
||||
await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8');
|
||||
|
|
@ -100,7 +100,7 @@ class DefaultMode {
|
|||
|
||||
export class PlaywrightEnv implements Env<PlaywrightTestArgs> {
|
||||
private _mode: DriverMode | ServiceMode | DefaultMode;
|
||||
private _browserName: BrowserName;
|
||||
protected _browserName: BrowserName;
|
||||
protected _options: LaunchOptions & TestOptions;
|
||||
protected _browserOptions: LaunchOptions;
|
||||
private _playwright: typeof import('../../index');
|
||||
|
|
@ -200,6 +200,7 @@ export class PlaywrightEnv implements Env<PlaywrightTestArgs> {
|
|||
}
|
||||
|
||||
async afterAll(workerInfo: WorkerInfo) {
|
||||
await this._mode.teardown();
|
||||
const { coverage, uninstall } = this._coverage!;
|
||||
uninstall();
|
||||
const coveragePath = path.join(__dirname, '..', '..', 'test', 'coverage-report', workerInfo.workerIndex + '.json');
|
||||
|
|
|
|||
74
tests/config/cliEnv.ts
Normal file
74
tests/config/cliEnv.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Env, TestInfo, WorkerInfo } from '../folio/out';
|
||||
import { PageEnv } from './browserEnv';
|
||||
import { CLIMock, CLITestArgs, Recorder } from './cliTest';
|
||||
import * as http from 'http';
|
||||
import { recorderPageGetter } from '../../test/utils';
|
||||
|
||||
export class CLIEnv extends PageEnv implements Env<CLITestArgs> {
|
||||
private _server: http.Server | undefined;
|
||||
private _handler = (req: http.IncomingMessage, res: http.ServerResponse) => res.end();
|
||||
private _port: number;
|
||||
private _cli: CLIMock | undefined;
|
||||
|
||||
async beforeAll(workerInfo: WorkerInfo) {
|
||||
await super.beforeAll(workerInfo);
|
||||
|
||||
this._port = 9907 + workerInfo.workerIndex;
|
||||
this._server = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => this._handler(req, res)).listen(this._port);
|
||||
}
|
||||
|
||||
private _runCLI(args: string[]) {
|
||||
this._cli = new CLIMock(this._browserName, this._browserOptions.channel, !!this._browserOptions.headless, args);
|
||||
return this._cli;
|
||||
}
|
||||
|
||||
async beforeEach(testInfo: TestInfo) {
|
||||
const result = await super.beforeEach(testInfo);
|
||||
const { page, context, toImpl } = result;
|
||||
return {
|
||||
...result,
|
||||
httpServer: {
|
||||
setHandler: newHandler => this._handler = newHandler,
|
||||
PREFIX: `http://127.0.0.1:${this._port}`,
|
||||
},
|
||||
runCLI: this._runCLI.bind(this),
|
||||
openRecorder: async () => {
|
||||
await (page.context() as any)._enableRecorder({ language: 'javascript', startRecording: true });
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
return new Recorder(page, recorderPage);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async afterEach(testInfo: TestInfo) {
|
||||
if (this._cli) {
|
||||
await this._cli.exited;
|
||||
this._cli = undefined;
|
||||
}
|
||||
await super.afterEach(testInfo);
|
||||
}
|
||||
|
||||
async afterAll(workerInfo: WorkerInfo) {
|
||||
if (this._server) {
|
||||
this._server.close();
|
||||
this._server = undefined;
|
||||
}
|
||||
await super.afterAll(workerInfo);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,54 +14,31 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { newTestType } from '../folio/out';
|
||||
import type { Page } from '../../index';
|
||||
import type { ServerTestArgs } from './serverTest';
|
||||
import type { BrowserTestArgs } from './browserTest';
|
||||
import * as http from 'http';
|
||||
import path from 'path';
|
||||
import { ChildProcess, spawn } from 'child_process';
|
||||
import { folio as baseFolio } from '../fixtures';
|
||||
import type { BrowserType, Browser, Page } from '../..';
|
||||
export { config } from 'folio';
|
||||
import * as path from 'path';
|
||||
import type { Source } from '../../src/server/supplements/recorder/recorderTypes';
|
||||
import { recorderPageGetter } from '../utils';
|
||||
import { ChildProcess, spawn } from 'child_process';
|
||||
export { expect } from 'folio';
|
||||
|
||||
type WorkerFixtures = {
|
||||
browserType: BrowserType<Browser>;
|
||||
browser: Browser;
|
||||
httpServer: httpServer;
|
||||
};
|
||||
interface CLIHTTPServer {
|
||||
setHandler: (handler: http.RequestListener) => void
|
||||
PREFIX: string
|
||||
}
|
||||
|
||||
type TestFixtures = {
|
||||
recorder: Recorder;
|
||||
export type CLITestArgs = BrowserTestArgs & {
|
||||
page: Page;
|
||||
httpServer: CLIHTTPServer;
|
||||
openRecorder: () => Promise<Recorder>;
|
||||
runCLI: (args: string[]) => CLIMock;
|
||||
};
|
||||
|
||||
export const fixtures = baseFolio.extend<TestFixtures, WorkerFixtures>();
|
||||
export const test = newTestType<CLITestArgs & ServerTestArgs>();
|
||||
|
||||
fixtures.recorder.init(async ({ page, context, toImpl }, runTest) => {
|
||||
await (page.context() as any)._enableRecorder({ language: 'javascript', startRecording: true });
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
await runTest(new Recorder(page, recorderPage));
|
||||
});
|
||||
|
||||
fixtures.httpServer.init(async ({testWorkerIndex}, runTest) => {
|
||||
let handler = (req: http.IncomingMessage, res: http.ServerResponse) => res.end();
|
||||
const port = 9907 + testWorkerIndex;
|
||||
const server = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => handler(req, res)).listen(port);
|
||||
await runTest({
|
||||
setHandler: newHandler => handler = newHandler,
|
||||
PREFIX: `http://127.0.0.1:${port}`,
|
||||
});
|
||||
server.close();
|
||||
}, { scope: 'worker' });
|
||||
|
||||
function removeAnsiColors(input: string): string {
|
||||
const pattern = [
|
||||
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
||||
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'
|
||||
].join('|');
|
||||
return input.replace(new RegExp(pattern, 'g'), '');
|
||||
}
|
||||
|
||||
class Recorder {
|
||||
export class Recorder {
|
||||
page: Page;
|
||||
_highlightCallback: Function
|
||||
_highlightInstalled: boolean
|
||||
|
|
@ -150,17 +127,7 @@ class Recorder {
|
|||
}
|
||||
}
|
||||
|
||||
fixtures.runCLI.init(async ({ browserName, browserChannel, headful }, runTest) => {
|
||||
let cli: CLIMock;
|
||||
const cliFactory = (args: string[]) => {
|
||||
cli = new CLIMock(browserName, browserChannel, !headful, args);
|
||||
return cli;
|
||||
};
|
||||
await runTest(cliFactory);
|
||||
await cli.exited;
|
||||
});
|
||||
|
||||
class CLIMock {
|
||||
export class CLIMock {
|
||||
private process: ChildProcess;
|
||||
private data: string;
|
||||
private waitForText: string;
|
||||
|
|
@ -210,9 +177,10 @@ class CLIMock {
|
|||
}
|
||||
}
|
||||
|
||||
interface httpServer {
|
||||
setHandler: (handler: http.RequestListener) => void
|
||||
PREFIX: string
|
||||
function removeAnsiColors(input: string): string {
|
||||
const pattern = [
|
||||
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
||||
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'
|
||||
].join('|');
|
||||
return input.replace(new RegExp(pattern, 'g'), '');
|
||||
}
|
||||
|
||||
export const folio = fixtures.build();
|
||||
|
|
@ -20,9 +20,11 @@ import { test as playwrightTest, slowTest as playwrightSlowTest } from './playwr
|
|||
import { test as browserTest } from './browserTest';
|
||||
import { test as pageTest } from './pageTest';
|
||||
import { test as electronTest } from './electronTest';
|
||||
import { test as cliTest } from './cliTest';
|
||||
import { PlaywrightEnv, BrowserEnv, PageEnv, BrowserName } from './browserEnv';
|
||||
import { ServerEnv } from './serverEnv';
|
||||
import { ElectronEnv } from './electronEnv';
|
||||
import { CLIEnv } from './cliEnv';
|
||||
|
||||
const config: Config = {
|
||||
testDir: path.join(__dirname, '..'),
|
||||
|
|
@ -63,6 +65,7 @@ for (const browserName of browsers) {
|
|||
playwrightSlowTest.runWith(browserName, serverEnv, new PlaywrightEnv(browserName, options), { timeout: config.timeout * 3 });
|
||||
browserTest.runWith(browserName, serverEnv, new BrowserEnv(browserName, options), {});
|
||||
pageTest.runWith(browserName, serverEnv, new PageEnv(browserName, options), {});
|
||||
cliTest.runWith(browserName, serverEnv, new CLIEnv(browserName, options), {});
|
||||
if (browserName === 'chromium')
|
||||
electronTest.runWith(browserName, serverEnv, new ElectronEnv({ mode }));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,31 +136,22 @@ export function newTestTypeImpl(): any {
|
|||
function hook(name: string, fn: Function) {
|
||||
if (!currentFile)
|
||||
throw errorWithCallLocation(`Hook can only be defined in a test file.`);
|
||||
ensureSuiteForCurrentLocation();
|
||||
suites[0]._addHook(name, fn);
|
||||
}
|
||||
|
||||
const modifier = (type: 'skip' | 'fail' | 'fixme', arg?: boolean | string, description?: string) => {
|
||||
const processed = interpretCondition(arg, description);
|
||||
if (!processed.condition)
|
||||
return;
|
||||
|
||||
if (currentFile) {
|
||||
suites[0]._annotations.push({ type, description: processed.description });
|
||||
const processed = interpretCondition(arg, description);
|
||||
if (processed.condition)
|
||||
suites[0]._annotations.push({ type, description: processed.description });
|
||||
return;
|
||||
}
|
||||
|
||||
const testInfo = currentTestInfo();
|
||||
if (!testInfo)
|
||||
throw new Error(`test.${type} can only be called inside the test`);
|
||||
|
||||
testInfo.annotations.push({ type, description: processed.description });
|
||||
if (type === 'skip' || type === 'fixme') {
|
||||
testInfo.expectedStatus = 'skipped';
|
||||
throw new SkipError(processed.description);
|
||||
} else if (type === 'fail') {
|
||||
if (testInfo.expectedStatus !== 'skipped')
|
||||
testInfo.expectedStatus = 'failed';
|
||||
}
|
||||
(testInfo[type] as any)(arg, description);
|
||||
};
|
||||
|
||||
const test: any = spec.bind(null, 'default');
|
||||
|
|
@ -197,9 +188,6 @@ export function newTestTypeImpl(): any {
|
|||
return test;
|
||||
}
|
||||
|
||||
export class SkipError extends Error {
|
||||
}
|
||||
|
||||
export function setConfig(config: Config) {
|
||||
// TODO: add config validation.
|
||||
configFile.config = config;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,23 @@ export interface Config extends RunWithConfig {
|
|||
}
|
||||
export type FullConfig = Required<Config>;
|
||||
|
||||
interface TestModifier {
|
||||
skip(): void;
|
||||
skip(condition: boolean): void;
|
||||
skip(description: string): void;
|
||||
skip(condition: boolean, description: string): void;
|
||||
|
||||
fixme(): void;
|
||||
fixme(condition: boolean): void;
|
||||
fixme(description: string): void;
|
||||
fixme(condition: boolean, description: string): void;
|
||||
|
||||
fail(): void;
|
||||
fail(condition: boolean): void;
|
||||
fail(description: string): void;
|
||||
fail(condition: boolean, description: string): void;
|
||||
}
|
||||
|
||||
export type TestStatus = 'passed' | 'failed' | 'timedOut' | 'skipped';
|
||||
|
||||
export interface WorkerInfo {
|
||||
|
|
@ -48,7 +65,7 @@ export interface WorkerInfo {
|
|||
globalSetupResult: any;
|
||||
}
|
||||
|
||||
export interface TestInfo extends WorkerInfo {
|
||||
export interface TestInfo extends WorkerInfo, TestModifier {
|
||||
// Declaration
|
||||
title: string;
|
||||
file: string;
|
||||
|
|
@ -87,7 +104,7 @@ interface TestFunction<TestArgs, TestOptions> {
|
|||
(name: string, options: TestOptions, fn: (args: TestArgs, testInfo: TestInfo) => any): void;
|
||||
}
|
||||
|
||||
export interface TestType<TestArgs, TestOptions> extends TestFunction<TestArgs, TestOptions> {
|
||||
export interface TestType<TestArgs, TestOptions> extends TestFunction<TestArgs, TestOptions>, TestModifier {
|
||||
only: TestFunction<TestArgs, TestOptions>;
|
||||
describe: SuiteFunction & {
|
||||
only: SuiteFunction;
|
||||
|
|
@ -100,21 +117,6 @@ export interface TestType<TestArgs, TestOptions> extends TestFunction<TestArgs,
|
|||
|
||||
expect: Expect;
|
||||
|
||||
skip(): void;
|
||||
skip(condition: boolean): void;
|
||||
skip(description: string): void;
|
||||
skip(condition: boolean, description: string): void;
|
||||
|
||||
fixme(): void;
|
||||
fixme(condition: boolean): void;
|
||||
fixme(description: string): void;
|
||||
fixme(condition: boolean, description: string): void;
|
||||
|
||||
fail(): void;
|
||||
fail(condition: boolean): void;
|
||||
fail(description: string): void;
|
||||
fail(condition: boolean, description: string): void;
|
||||
|
||||
runWith(config?: RunWithConfig): void;
|
||||
runWith(alias: string, config?: RunWithConfig): void;
|
||||
runWith(env: Env<TestArgs>, config?: RunWithConfig): void;
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { EventEmitter } from 'events';
|
||||
import { monotonicTime, raceAgainstDeadline, serializeError } from './util';
|
||||
import { interpretCondition, monotonicTime, raceAgainstDeadline, serializeError } from './util';
|
||||
import { TestBeginPayload, TestEndPayload, RunPayload, TestEntry, DonePayload, WorkerInitParams } from './ipc';
|
||||
import { setCurrentTestInfo } from './globals';
|
||||
import { Loader } from './loader';
|
||||
import { Spec, Suite, Test } from './test';
|
||||
import { TestInfo, WorkerInfo } from './types';
|
||||
import { SkipError, RunListDescription } from './spec';
|
||||
import { RunListDescription } from './spec';
|
||||
|
||||
export class WorkerRunner extends EventEmitter {
|
||||
private _params: WorkerInitParams;
|
||||
|
|
@ -230,6 +230,9 @@ export class WorkerRunner extends EventEmitter {
|
|||
return path.join(basePath, ...pathSegments);
|
||||
},
|
||||
testOptions: spec.testOptions,
|
||||
skip: (arg?: boolean | string, description?: string) => modifier(testInfo, 'skip', arg, description),
|
||||
fixme: (arg?: boolean | string, description?: string) => modifier(testInfo, 'fixme', arg, description),
|
||||
fail: (arg?: boolean | string, description?: string) => modifier(testInfo, 'fail', arg, description),
|
||||
};
|
||||
this._setCurrentTestInfo(testInfo);
|
||||
|
||||
|
|
@ -438,3 +441,20 @@ function buildTestEndPayload(testId: string, testInfo: TestInfo): TestEndPayload
|
|||
timeout: testInfo.timeout,
|
||||
};
|
||||
}
|
||||
|
||||
function modifier(testInfo: TestInfo, type: 'skip' | 'fail' | 'fixme', arg?: boolean | string, description?: string) {
|
||||
const processed = interpretCondition(arg, description);
|
||||
if (!processed.condition)
|
||||
return;
|
||||
testInfo.annotations.push({ type, description: processed.description });
|
||||
if (type === 'skip' || type === 'fixme') {
|
||||
testInfo.expectedStatus = 'skipped';
|
||||
throw new SkipError(processed.description);
|
||||
} else if (type === 'fail') {
|
||||
if (testInfo.expectedStatus !== 'skipped')
|
||||
testInfo.expectedStatus = 'failed';
|
||||
}
|
||||
}
|
||||
|
||||
class SkipError extends Error {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,95 +29,88 @@ test('should close the browser when the node process closes', async ({startRemot
|
|||
expect(await remoteServer.childExitCode()).toBe(isWindows ? 1 : 0);
|
||||
});
|
||||
|
||||
test('should report browser close signal', async ({startRemoteServer, server, platform, headful}) => {
|
||||
test.skip(platform === 'win32' || headful);
|
||||
test.describe('signals', () => {
|
||||
test.beforeEach(async ({platform, headful}) => {
|
||||
test.skip(platform === 'win32' || headful);
|
||||
});
|
||||
|
||||
const remoteServer = await startRemoteServer({ url: server.EMPTY_PAGE });
|
||||
const pid = await remoteServer.out('pid');
|
||||
process.kill(-pid, 'SIGTERM');
|
||||
expect(await remoteServer.out('exitCode')).toBe('null');
|
||||
expect(await remoteServer.out('signal')).toBe('SIGTERM');
|
||||
process.kill(remoteServer.child().pid);
|
||||
await remoteServer.childExitCode();
|
||||
});
|
||||
|
||||
test('should report browser close signal 2', async ({startRemoteServer, server, platform, headful}) => {
|
||||
test.skip(platform === 'win32' || headful);
|
||||
|
||||
const remoteServer = await startRemoteServer({ url: server.EMPTY_PAGE });
|
||||
const pid = await remoteServer.out('pid');
|
||||
process.kill(-pid, 'SIGKILL');
|
||||
expect(await remoteServer.out('exitCode')).toBe('null');
|
||||
expect(await remoteServer.out('signal')).toBe('SIGKILL');
|
||||
process.kill(remoteServer.child().pid);
|
||||
await remoteServer.childExitCode();
|
||||
});
|
||||
|
||||
test('should close the browser on SIGINT', async ({startRemoteServer, server, browserChannel, platform, headful}) => {
|
||||
test.skip(platform === 'win32' || headful);
|
||||
test.fixme(!!browserChannel, 'Uncomment on roll');
|
||||
|
||||
const remoteServer = await startRemoteServer({ url: server.EMPTY_PAGE });
|
||||
process.kill(remoteServer.child().pid, 'SIGINT');
|
||||
expect(await remoteServer.out('exitCode')).toBe('0');
|
||||
expect(await remoteServer.out('signal')).toBe('null');
|
||||
expect(await remoteServer.childExitCode()).toBe(130);
|
||||
});
|
||||
|
||||
test('should close the browser on SIGTERM', async ({startRemoteServer, server, browserChannel, platform, headful}) => {
|
||||
test.skip(platform === 'win32' || headful);
|
||||
test.fixme(!!browserChannel, 'Uncomment on roll');
|
||||
|
||||
const remoteServer = await startRemoteServer({ url: server.EMPTY_PAGE });
|
||||
process.kill(remoteServer.child().pid, 'SIGTERM');
|
||||
expect(await remoteServer.out('exitCode')).toBe('0');
|
||||
expect(await remoteServer.out('signal')).toBe('null');
|
||||
expect(await remoteServer.childExitCode()).toBe(0);
|
||||
});
|
||||
|
||||
test('should close the browser on SIGHUP', async ({startRemoteServer, server, browserChannel, platform, headful}) => {
|
||||
test.skip(platform === 'win32' || headful);
|
||||
test.fixme(!!browserChannel, 'Uncomment on roll');
|
||||
|
||||
const remoteServer = await startRemoteServer({ url: server.EMPTY_PAGE });
|
||||
process.kill(remoteServer.child().pid, 'SIGHUP');
|
||||
expect(await remoteServer.out('exitCode')).toBe('0');
|
||||
expect(await remoteServer.out('signal')).toBe('null');
|
||||
expect(await remoteServer.childExitCode()).toBe(0);
|
||||
});
|
||||
|
||||
test('should kill the browser on double SIGINT', async ({startRemoteServer, server, platform, headful}) => {
|
||||
test.skip(platform === 'win32' || headful);
|
||||
|
||||
const remoteServer = await startRemoteServer({ stallOnClose: true, url: server.EMPTY_PAGE });
|
||||
process.kill(remoteServer.child().pid, 'SIGINT');
|
||||
await remoteServer.out('stalled');
|
||||
process.kill(remoteServer.child().pid, 'SIGINT');
|
||||
expect(await remoteServer.out('exitCode')).toBe('null');
|
||||
expect(await remoteServer.out('signal')).toBe('SIGKILL');
|
||||
expect(await remoteServer.childExitCode()).toBe(130);
|
||||
});
|
||||
|
||||
test('should kill the browser on SIGINT + SIGTERM', async ({startRemoteServer, server, platform, headful}) => {
|
||||
test.skip(platform === 'win32' || headful);
|
||||
|
||||
const remoteServer = await startRemoteServer({ stallOnClose: true, url: server.EMPTY_PAGE });
|
||||
process.kill(remoteServer.child().pid, 'SIGINT');
|
||||
await remoteServer.out('stalled');
|
||||
process.kill(remoteServer.child().pid, 'SIGTERM');
|
||||
expect(await remoteServer.out('exitCode')).toBe('null');
|
||||
expect(await remoteServer.out('signal')).toBe('SIGKILL');
|
||||
expect(await remoteServer.childExitCode()).toBe(0);
|
||||
});
|
||||
|
||||
test('should kill the browser on SIGTERM + SIGINT', async ({startRemoteServer, server, platform, headful}) => {
|
||||
test.skip(platform === 'win32' || headful);
|
||||
|
||||
const remoteServer = await startRemoteServer({ stallOnClose: true, url: server.EMPTY_PAGE });
|
||||
process.kill(remoteServer.child().pid, 'SIGTERM');
|
||||
await remoteServer.out('stalled');
|
||||
process.kill(remoteServer.child().pid, 'SIGINT');
|
||||
expect(await remoteServer.out('exitCode')).toBe('null');
|
||||
expect(await remoteServer.out('signal')).toBe('SIGKILL');
|
||||
expect(await remoteServer.childExitCode()).toBe(130);
|
||||
test('should report browser close signal', async ({startRemoteServer, server}) => {
|
||||
const remoteServer = await startRemoteServer({ url: server.EMPTY_PAGE });
|
||||
const pid = await remoteServer.out('pid');
|
||||
process.kill(-pid, 'SIGTERM');
|
||||
expect(await remoteServer.out('exitCode')).toBe('null');
|
||||
expect(await remoteServer.out('signal')).toBe('SIGTERM');
|
||||
process.kill(remoteServer.child().pid);
|
||||
await remoteServer.childExitCode();
|
||||
});
|
||||
|
||||
test('should report browser close signal 2', async ({startRemoteServer, server}) => {
|
||||
const remoteServer = await startRemoteServer({ url: server.EMPTY_PAGE });
|
||||
const pid = await remoteServer.out('pid');
|
||||
process.kill(-pid, 'SIGKILL');
|
||||
expect(await remoteServer.out('exitCode')).toBe('null');
|
||||
expect(await remoteServer.out('signal')).toBe('SIGKILL');
|
||||
process.kill(remoteServer.child().pid);
|
||||
await remoteServer.childExitCode();
|
||||
});
|
||||
|
||||
test('should close the browser on SIGINT', async ({startRemoteServer, server, browserChannel}) => {
|
||||
test.fixme(!!browserChannel, 'Uncomment on roll');
|
||||
|
||||
const remoteServer = await startRemoteServer({ url: server.EMPTY_PAGE });
|
||||
process.kill(remoteServer.child().pid, 'SIGINT');
|
||||
expect(await remoteServer.out('exitCode')).toBe('0');
|
||||
expect(await remoteServer.out('signal')).toBe('null');
|
||||
expect(await remoteServer.childExitCode()).toBe(130);
|
||||
});
|
||||
|
||||
test('should close the browser on SIGTERM', async ({startRemoteServer, server, browserChannel}) => {
|
||||
test.fixme(!!browserChannel, 'Uncomment on roll');
|
||||
|
||||
const remoteServer = await startRemoteServer({ url: server.EMPTY_PAGE });
|
||||
process.kill(remoteServer.child().pid, 'SIGTERM');
|
||||
expect(await remoteServer.out('exitCode')).toBe('0');
|
||||
expect(await remoteServer.out('signal')).toBe('null');
|
||||
expect(await remoteServer.childExitCode()).toBe(0);
|
||||
});
|
||||
|
||||
test('should close the browser on SIGHUP', async ({startRemoteServer, server, browserChannel}) => {
|
||||
test.fixme(!!browserChannel, 'Uncomment on roll');
|
||||
|
||||
const remoteServer = await startRemoteServer({ url: server.EMPTY_PAGE });
|
||||
process.kill(remoteServer.child().pid, 'SIGHUP');
|
||||
expect(await remoteServer.out('exitCode')).toBe('0');
|
||||
expect(await remoteServer.out('signal')).toBe('null');
|
||||
expect(await remoteServer.childExitCode()).toBe(0);
|
||||
});
|
||||
|
||||
test('should kill the browser on double SIGINT', async ({startRemoteServer, server}) => {
|
||||
const remoteServer = await startRemoteServer({ stallOnClose: true, url: server.EMPTY_PAGE });
|
||||
process.kill(remoteServer.child().pid, 'SIGINT');
|
||||
await remoteServer.out('stalled');
|
||||
process.kill(remoteServer.child().pid, 'SIGINT');
|
||||
expect(await remoteServer.out('exitCode')).toBe('null');
|
||||
expect(await remoteServer.out('signal')).toBe('SIGKILL');
|
||||
expect(await remoteServer.childExitCode()).toBe(130);
|
||||
});
|
||||
|
||||
test('should kill the browser on SIGINT + SIGTERM', async ({startRemoteServer, server}) => {
|
||||
const remoteServer = await startRemoteServer({ stallOnClose: true, url: server.EMPTY_PAGE });
|
||||
process.kill(remoteServer.child().pid, 'SIGINT');
|
||||
await remoteServer.out('stalled');
|
||||
process.kill(remoteServer.child().pid, 'SIGTERM');
|
||||
expect(await remoteServer.out('exitCode')).toBe('null');
|
||||
expect(await remoteServer.out('signal')).toBe('SIGKILL');
|
||||
expect(await remoteServer.childExitCode()).toBe(0);
|
||||
});
|
||||
|
||||
test('should kill the browser on SIGTERM + SIGINT', async ({startRemoteServer, server}) => {
|
||||
const remoteServer = await startRemoteServer({ stallOnClose: true, url: server.EMPTY_PAGE });
|
||||
process.kill(remoteServer.child().pid, 'SIGTERM');
|
||||
await remoteServer.out('stalled');
|
||||
process.kill(remoteServer.child().pid, 'SIGINT');
|
||||
expect(await remoteServer.out('exitCode')).toBe('null');
|
||||
expect(await remoteServer.out('signal')).toBe('SIGKILL');
|
||||
expect(await remoteServer.childExitCode()).toBe(130);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue