infra(channel): wire release channel to all tests (#5820)

This commit is contained in:
Pavel Feldman 2021-03-15 08:07:57 -07:00 committed by GitHub
parent a96d6a7dbb
commit 1dd6bd3316
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 198 additions and 108 deletions

View file

@ -272,8 +272,12 @@ jobs:
- name: Install Chrome Stable
run: sudo apt install google-chrome-stable
- run: npm ci
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build
- run: node lib/cli/cli install-deps chromium
# This only created problems, should we move ffmpeg back into npm?
- run: node lib/cli/cli install ffmpeg
- run: mkdir -p coredumps
# Set core dump file name pattern
- run: sudo bash -c 'echo "$(pwd -P)/coredumps/core-pid_%p.dump" > /proc/sys/kernel/core_pattern'

View file

@ -299,6 +299,19 @@ Whether to run browser in headless mode. More details for
[Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true` unless the
[`option: devtools`] option is `true`.
### option: BrowserType.launchPersistentContext.channel
- `channel` <[string]>
Chromium distribution channel, one of
* chrome
* chrome-beta
* chrome-dev
* chrome-canary
* msedge
* msedge-beta
* msedge-dev
* msedge-canary
### option: BrowserType.launchPersistentContext.executablePath
- `executablePath` <[path]>

View file

@ -85,7 +85,7 @@ program
.description('ensure browsers necessary for this version of Playwright are installed')
.action(async function(browserType) {
try {
const allBrowsers = new Set(['chromium', 'firefox', 'webkit']);
const allBrowsers = new Set(['chromium', 'firefox', 'webkit', 'ffmpeg']);
for (const type of browserType) {
if (!allBrowsers.has(type)) {
console.log(`Invalid browser name: '${type}'. Expecting 'chromium', 'firefox' or 'webkit'.`);
@ -186,6 +186,7 @@ else
type Options = {
browser: string;
channel?: string;
colorScheme?: string;
device?: string;
geolocation?: string;
@ -209,6 +210,9 @@ async function launchContext(options: Options, headless: boolean): Promise<{ bro
validateOptions(options);
const browserType = lookupBrowserType(options);
const launchOptions: LaunchOptions = { headless };
if (options.channel)
launchOptions.channel = options.channel;
const contextOptions: BrowserContextOptions =
// Copy the device descriptor since we have to compare and modify the options.
options.device ? { ...playwright.devices[options.device] } : {};
@ -452,6 +456,7 @@ function commandWithOpenOptions(command: string, description: string, options: a
result = result.option(option[0], ...option.slice(1));
return result
.option('-b, --browser <browserType>', 'browser to use, one of cr, chromium, ff, firefox, wk, webkit', 'chromium')
.option('--channel <channel>', 'Chromium distribution channel, "chrome", "chrome-beta", "msedge-dev", etc')
.option('--color-scheme <scheme>', 'emulate preferred color scheme, "light" or "dark"')
.option('--device <deviceName>', 'emulate device, for example "iPhone 11"')
.option('--geolocation <coordinates>', 'specify geolocation coordinates, for example "37.819722,-122.478611"')

View file

@ -100,6 +100,7 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel, chann
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined,
ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs),
env: options.env ? envObjectToArray(options.env) : undefined,
channel: options.channel,
userDataDir,
};
const result = await channel.launchPersistentContext(persistentParams);

View file

@ -268,6 +268,7 @@ export type BrowserTypeLaunchResult = {
browser: BrowserChannel,
};
export type BrowserTypeLaunchPersistentContextParams = {
channel?: string,
userDataDir: string,
sdkLanguage: string,
executablePath?: string,
@ -333,6 +334,7 @@ export type BrowserTypeLaunchPersistentContextParams = {
},
};
export type BrowserTypeLaunchPersistentContextOptions = {
channel?: string,
executablePath?: string,
args?: string[],
ignoreAllDefaultArgs?: boolean,

View file

@ -334,6 +334,7 @@ BrowserType:
launchPersistentContext:
parameters:
channel: string?
userDataDir: string
sdkLanguage: string
executablePath: string?

View file

@ -178,6 +178,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
slowMo: tOptional(tNumber),
});
scheme.BrowserTypeLaunchPersistentContextParams = tObject({
channel: tOptional(tString),
userDataDir: tString,
sdkLanguage: tString,
executablePath: tOptional(tString),

View file

@ -39,6 +39,7 @@ export type PlaywrightOptions = {
export type BrowserOptions = PlaywrightOptions & {
name: string,
isChromium: boolean,
channel?: string,
downloadsPath?: string,
headful?: boolean,
persistent?: types.BrowserContextOptions, // Undefined means no persistent context.

View file

@ -104,6 +104,7 @@ export abstract class BrowserType extends SdkObject {
...this._playwrightOptions,
name: this._name,
isChromium: this._name === 'chromium',
channel: options.channel,
slowMo: options.slowMo,
persistent,
headful: !options.headless,
@ -176,7 +177,7 @@ export abstract class BrowserType extends SdkObject {
throw new Error(errorMessageLines.join('\n'));
}
if (!executablePath) {
if (!executable) {
// We can only validate dependencies for bundled browsers.
await validateHostRequirements(this._registry, this._name);
}

View file

@ -20,6 +20,8 @@ import path from 'path';
function darwin(channel: string): string | undefined {
switch (channel) {
case 'chrome': return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
case 'chrome-beta': return '/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta';
case 'chrome-dev': return '/Applications/Google Chrome Dev.app/Contents/MacOS/Google Chrome Dev';
case 'chrome-canary': return '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary';
case 'msedge': return '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge';
case 'msedge-beta': return '/Applications/Microsoft Edge Beta.app/Contents/MacOS/Microsoft Edge Beta';
@ -41,6 +43,8 @@ function win32(channel: string): string | undefined {
let suffix: string | undefined;
switch (channel) {
case 'chrome': suffix = `\\Google\\Chrome\\Application\\chrome.exe`; break;
case 'chrome-beta': suffix = `\\Google\\Chrome Beta\\Application\\chrome.exe`; break;
case 'chrome-dev': suffix = `\\Google\\Chrome Dev\\Application\\chrome.exe`; break;
case 'chrome-canary': suffix = `\\Google\\Chrome SxS\\Application\\chrome.exe`; break;
case 'msedge': suffix = `\\Microsoft\\Edge\\Application\\msedge.exe`; break;
case 'msedge-beta': suffix = `\\Microsoft\\Edge Beta\\Application\\msedge.exe`; break;

View file

@ -134,7 +134,8 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
formatter.add(`
await Playwright.InstallAsync();
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.${toPascal(options.browserName)}.LaunchAsync(${formatArgs(options.launchOptions)});
await using var browser = await playwright.${toPascal(options.browserName)}.LaunchAsync(${formatArgs(options.launchOptions)}
);
var context = await browser.NewContextAsync(${formatContextOptions(options.contextOptions, options.deviceName)});`);
return formatter.format();
}
@ -178,10 +179,7 @@ function formatArgs(value: any, indent = ' '): string {
const tokens: string[] = [];
for (const key of keys)
tokens.push(`${keys.length !== 1 ? indent : ''}${key}: ${formatObject(value[key], indent, key)}`);
if (keys.length === 1)
return `${tokens.join(`,\n${indent}`)}`;
else
return `\n${indent}${tokens.join(`,\n${indent}`)}`;
return `\n${indent}${tokens.join(`,\n${indent}`)}`;
}
return String(value);
}
@ -271,7 +269,7 @@ class CSharpFormatter {
return this._lines.map((line: string) => {
if (line === '')
return line;
if (line.startsWith('}') || line.startsWith(']') || line.includes('});'))
if (line.startsWith('}') || line.startsWith(']') || line.includes('});') || line === ');')
spaces = spaces.substring(this._baseIndent.length);
const extraSpaces = /^(for|while|if).*\(.*\)$/.test(previousLine) ? this._baseIndent : '';

View file

@ -164,6 +164,8 @@ function formatLaunchOptions(options: any): string {
lines.push('new BrowserType.LaunchOptions()');
if (typeof options.headless === 'boolean')
lines.push(` .setHeadless(false)`);
if (options.channel)
lines.push(` .setChannel("${options.channel}")`);
return lines.join('\n');
}

View file

@ -102,6 +102,7 @@ export class RecorderApp extends EventEmitter {
if (isUnderTest())
args.push(`--remote-debugging-port=0`);
const context = await recorderPlaywright.chromium.launchPersistentContext(internalCallMetadata(), '', {
channel: inspectedContext._browser.options.channel,
sdkLanguage: inspectedContext._options.sdkLanguage,
args,
noDefaultViewport: true,

View file

@ -18,9 +18,11 @@
import fs from 'fs';
import { it, expect } from './fixtures';
it('browserType.executablePath should work', test => {
it('browserType.executablePath should work', (test, { browserChannel }) => {
test.fixme(!!browserChannel, 'Uncomment on roll');
test.skip(Boolean(process.env.CRPATH || process.env.FFPATH || process.env.WKPATH));
}, async ({browserType}) => {
}, async ({ browserType, browserChannel }) => {
// Interesting, unless I use browserChannel in test, filter above does not work!
const executablePath = browserType.executablePath();
expect(fs.existsSync(executablePath)).toBe(true);
});

View file

@ -60,7 +60,9 @@ describe('launch server', (suite, { mode }) => {
await browserServer.close();
});
it('should fire close event', async ({browserType, browserOptions}) => {
it('should fire close event', (test, { browserChannel }) => {
test.fixme(!!browserChannel, 'Uncomment on roll');
}, async ({browserType, browserOptions}) => {
const browserServer = await browserType.launchServer(browserOptions);
const [result] = await Promise.all([
// @ts-expect-error The signal parameter is not documented.

View file

@ -21,24 +21,28 @@ import { folio } from './cli.fixtures';
const { it, expect } = folio;
const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString();
const launchOptions = (channel: string) => {
return channel ? `headless: false,\n channel: "${channel}"` : 'headless: false';
};
function capitalize(browserName: string): string {
return browserName[0].toUpperCase() + browserName.slice(1);
}
it('should print the correct imports and context options', async ({ browserName, runCLI }) => {
const cli = runCLI(['codegen', '--target=csharp', emptyHTML]);
it('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();
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(headless: false);
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(
${launchOptions(browserChannel)}
);
var context = await browser.NewContextAsync();`;
await cli.waitFor(expectedResult).catch(e => e);
expect(cli.text()).toContain(expectedResult);
});
it('should print the correct context options for custom settings', async ({ browserName, runCLI }) => {
it('should print the correct context options for custom settings', async ({ browserName, browserChannel, runCLI }) => {
const cli = runCLI([
'codegen',
'--color-scheme=dark',
'--geolocation=37.819722,-122.478611',
'--lang=es',
@ -51,11 +55,12 @@ it('should print the correct context options for custom settings', async ({ brow
const expectedResult = `await Playwright.InstallAsync();
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(
headless: false,
${launchOptions(browserChannel)},
proxy: new ProxySettings
{
Server = "http://myproxy:3128",
});
}
);
var context = await browser.NewContextAsync(
viewport: new ViewportSize
{
@ -78,21 +83,22 @@ var context = await browser.NewContextAsync(
it('should print the correct context options when using a device', (test, { browserName }) => {
test.skip(browserName !== 'chromium');
}, async ({ runCLI }) => {
const cli = runCLI(['codegen', '--device=Pixel 2', '--target=csharp', emptyHTML]);
}, async ({ browserChannel, runCLI }) => {
const cli = runCLI(['--device=Pixel 2', '--target=csharp', emptyHTML]);
const expectedResult = `await Playwright.InstallAsync();
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync(headless: false);
await using var browser = await playwright.Chromium.LaunchAsync(
${launchOptions(browserChannel)}
);
var context = await browser.NewContextAsync(playwright.Devices["Pixel 2"]);`;
await cli.waitFor(expectedResult);
expect(cli.text()).toContain(expectedResult);
});
it('should print the correct context options when using a device and additional options', (test, {browserName}) => {
it('should print the correct context options when using a device and additional options', (test, { browserName }) => {
test.skip(browserName !== 'webkit');
}, async ({ runCLI }) => {
}, async ({ browserChannel, runCLI }) => {
const cli = runCLI([
'codegen',
'--device=iPhone 11',
'--color-scheme=dark',
'--geolocation=37.819722,-122.478611',
@ -106,11 +112,12 @@ it('should print the correct context options when using a device and additional
const expectedResult = `await Playwright.InstallAsync();
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Webkit.LaunchAsync(
headless: false,
${launchOptions(browserChannel)},
proxy: new ProxySettings
{
Server = "http://myproxy:3128",
});
}
);
var context = await browser.NewContextAsync(new BrowserContextOptions(playwright.Devices["iPhone 11"])
{
UserAgent = "hardkodemium",
@ -134,15 +141,18 @@ var context = await browser.NewContextAsync(new BrowserContextOptions(playwright
expect(cli.text()).toContain(expectedResult);
});
it('should print load/save storageState', async ({ browserName, runCLI, testInfo }) => {
it('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');
const cli = runCLI(['codegen', `--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=csharp', emptyHTML]);
const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=csharp', emptyHTML]);
const expectedResult1 = `await Playwright.InstallAsync();
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(headless: false);
var context = await browser.NewContextAsync(storageState: "${loadFileName}");`;
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(
${launchOptions(browserChannel)}
);
var context = await browser.NewContextAsync(
storageState: "${loadFileName}");`;
await cli.waitFor(expectedResult1);
const expectedResult2 = `

View file

@ -21,9 +21,12 @@ import { folio } from './cli.fixtures';
const { it, expect } = folio;
const emptyHTML = new URL('file://' + path.join(__dirname, '..', '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, browserName }) => {
const cli = runCLI(['codegen', '--target=java', emptyHTML]);
it('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.*;
@ -31,14 +34,14 @@ public class Example {
public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.${browserName}().launch(new BrowserType.LaunchOptions()
.setHeadless(false));
${launchOptions(browserChannel)});
BrowserContext context = browser.newContext();`;
await cli.waitFor(expectedResult);
expect(cli.text()).toContain(expectedResult);
});
it('should print the correct context options for custom settings', async ({ runCLI, browserName }) => {
const cli = runCLI(['codegen', '--color-scheme=light', '--target=java', emptyHTML]);
const cli = runCLI(['--color-scheme=light', '--target=java', emptyHTML]);
const expectedResult = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.setColorScheme(ColorScheme.LIGHT));`;
await cli.waitFor(expectedResult);
@ -48,7 +51,7 @@ it('should print the correct context options for custom settings', async ({ runC
it('should print the correct context options when using a device', (test, { browserName }) => {
test.skip(browserName !== 'chromium');
}, async ({ runCLI }) => {
const cli = runCLI(['codegen', '--device=Pixel 2', '--target=java', emptyHTML]);
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")
.setViewportSize(411, 731)
@ -62,7 +65,7 @@ it('should print the correct context options when using a device', (test, { brow
it('should print the correct context options when using a device and additional options', (test, { browserName }) => {
test.skip(browserName !== 'webkit');
}, async ({ runCLI }) => {
const cli = runCLI(['codegen', '--color-scheme=light', '--device=iPhone 11', '--target=java', emptyHTML]);
const cli = runCLI(['--color-scheme=light', '--device=iPhone 11', '--target=java', emptyHTML]);
const expectedResult = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.setColorScheme(ColorScheme.LIGHT)
.setUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Mobile/15E148 Safari/604.1")
@ -78,7 +81,7 @@ it('should print load/save storage_state', async ({ runCLI, browserName, testInf
const loadFileName = testInfo.outputPath('load.json');
const saveFileName = testInfo.outputPath('save.json');
await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8');
const cli = runCLI(['codegen', `--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=java', emptyHTML]);
const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=java', emptyHTML]);
const expectedResult1 = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.setStorageStatePath(Paths.get("${loadFileName}")));`;
await cli.waitFor(expectedResult1);

View file

@ -22,26 +22,30 @@ const { it, expect } = folio;
const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString();
it('should print the correct imports and context options', async ({ browserName, runCLI }) => {
const cli = runCLI(['codegen', emptyHTML]);
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 }) => {
const cli = runCLI([emptyHTML]);
const expectedResult = `const { ${browserName} } = require('playwright');
(async () => {
const browser = await ${browserName}.launch({
headless: false
${launchOptions(browserChannel)}
});
const context = await browser.newContext();`;
await cli.waitFor(expectedResult);
expect(cli.text()).toContain(expectedResult);
});
it('should print the correct context options for custom settings', async ({ browserName, runCLI }) => {
const cli = runCLI(['codegen', '--color-scheme=light', emptyHTML]);
it('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');
(async () => {
const browser = await ${browserName}.launch({
headless: false
${launchOptions(browserChannel)}
});
const context = await browser.newContext({
colorScheme: 'light'
@ -51,15 +55,15 @@ 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 }) => {
it('should print the correct context options when using a device', (test, { browserName, browserChannel }) => {
test.skip(browserName !== 'chromium');
}, async ({ runCLI }) => {
const cli = runCLI(['codegen', '--device=Pixel 2', emptyHTML]);
}, async ({ browserChannel, runCLI }) => {
const cli = runCLI(['--device=Pixel 2', emptyHTML]);
const expectedResult = `const { chromium, devices } = require('playwright');
(async () => {
const browser = await chromium.launch({
headless: false
${launchOptions(browserChannel)}
});
const context = await browser.newContext({
...devices['Pixel 2'],
@ -70,13 +74,13 @@ it('should print the correct context options when using a device', (test, { brow
it('should print the correct context options when using a device and additional options', (test, { browserName }) => {
test.skip(browserName !== 'webkit');
}, async ({ runCLI }) => {
const cli = runCLI(['codegen', '--color-scheme=light', '--device=iPhone 11', emptyHTML]);
}, async ({ browserChannel, runCLI }) => {
const cli = runCLI(['--color-scheme=light', '--device=iPhone 11', emptyHTML]);
const expectedResult = `const { webkit, devices } = require('playwright');
(async () => {
const browser = await webkit.launch({
headless: false
${launchOptions(browserChannel)}
});
const context = await browser.newContext({
...devices['iPhone 11'],
@ -86,16 +90,16 @@ 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, runCLI, testInfo }) => {
it('should save the codegen output to a file if specified', async ({ browserName, browserChannel, runCLI, testInfo }) => {
const tmpFile = testInfo.outputPath('script.js');
const cli = runCLI(['codegen', '--output', tmpFile, emptyHTML]);
const cli = runCLI(['--output', tmpFile, emptyHTML]);
await cli.exited;
const content = fs.readFileSync(tmpFile);
expect(content.toString()).toBe(`const { ${browserName} } = require('playwright');
(async () => {
const browser = await ${browserName}.launch({
headless: false
${launchOptions(browserChannel)}
});
const context = await browser.newContext();
@ -114,16 +118,16 @@ it('should save the codegen output to a file if specified', async ({ browserName
})();`);
});
it('should print load/save storageState', async ({ browserName, runCLI, testInfo }) => {
it('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');
const cli = runCLI(['codegen', `--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, emptyHTML]);
const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, emptyHTML]);
const expectedResult1 = `const { ${browserName} } = require('playwright');
(async () => {
const browser = await ${browserName}.launch({
headless: false
${launchOptions(browserChannel)}
});
const context = await browser.newContext({
storageState: '${loadFileName}'

View file

@ -21,26 +21,29 @@ import { folio } from './cli.fixtures';
const { it, expect } = folio;
const emptyHTML = new URL('file://' + path.join(__dirname, '..', '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, runCLI }) => {
const cli = runCLI(['codegen', '--target=python-async', emptyHTML]);
it('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
async def run(playwright):
browser = await playwright.${browserName}.launch(headless=False)
browser = await playwright.${browserName}.launch(${launchOptions(browserChannel)})
context = await browser.new_context()`;
await cli.waitFor(expectedResult);
expect(cli.text()).toContain(expectedResult);
});
it('should print the correct context options for custom settings', async ({ browserName, runCLI }) => {
const cli = runCLI(['codegen', '--color-scheme=light', '--target=python-async', emptyHTML]);
it('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
async def run(playwright):
browser = await playwright.${browserName}.launch(headless=False)
browser = await playwright.${browserName}.launch(${launchOptions(browserChannel)})
context = await browser.new_context(color_scheme="light")`;
await cli.waitFor(expectedResult);
expect(cli.text()).toContain(expectedResult);
@ -48,13 +51,13 @@ async def run(playwright):
it('should print the correct context options when using a device', (test, { browserName }) => {
test.skip(browserName !== 'chromium');
}, async ({ runCLI }) => {
const cli = runCLI(['codegen', '--device=Pixel 2', '--target=python-async', emptyHTML]);
}, async ({ browserChannel, runCLI }) => {
const cli = runCLI(['--device=Pixel 2', '--target=python-async', emptyHTML]);
const expectedResult = `import asyncio
from playwright.async_api import async_playwright
async def run(playwright):
browser = await playwright.chromium.launch(headless=False)
browser = await playwright.chromium.launch(${launchOptions(browserChannel)})
context = await browser.new_context(**playwright.devices["Pixel 2"])`;
await cli.waitFor(expectedResult);
expect(cli.text()).toContain(expectedResult);
@ -62,28 +65,28 @@ async def run(playwright):
it('should print the correct context options when using a device and additional options', (test, { browserName }) => {
test.skip(browserName !== 'webkit');
}, async ({ runCLI }) => {
const cli = runCLI(['codegen', '--color-scheme=light', '--device=iPhone 11', '--target=python-async', emptyHTML]);
}, 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
async def run(playwright):
browser = await playwright.webkit.launch(headless=False)
browser = await playwright.webkit.launch(${launchOptions(browserChannel)})
context = await browser.new_context(**playwright.devices["iPhone 11"], color_scheme="light")`;
await cli.waitFor(expectedResult);
expect(cli.text()).toContain(expectedResult);
});
it('should save the codegen output to a file if specified', async ({ browserName, runCLI, testInfo }) => {
it('should save the codegen output to a file if specified', async ({ browserName, browserChannel, runCLI, testInfo }) => {
const tmpFile = testInfo.outputPath('script.js');
const cli = runCLI(['codegen', '--target=python-async', '--output', tmpFile, emptyHTML]);
const cli = runCLI(['--target=python-async', '--output', tmpFile, emptyHTML]);
await cli.exited;
const content = await fs.readFileSync(tmpFile);
expect(content.toString()).toBe(`import asyncio
from playwright.async_api import async_playwright
async def run(playwright):
browser = await playwright.${browserName}.launch(headless=False)
browser = await playwright.${browserName}.launch(${launchOptions(browserChannel)})
context = await browser.new_context()
# Open new page
@ -105,16 +108,16 @@ async def main():
asyncio.run(main())`);
});
it('should print load/save storage_state', async ({ browserName, runCLI, testInfo }) => {
it('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');
const cli = runCLI(['codegen', `--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=python-async', emptyHTML]);
const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=python-async', emptyHTML]);
const expectedResult1 = `import asyncio
from playwright.async_api import async_playwright
async def run(playwright):
browser = await playwright.${browserName}.launch(headless=False)
browser = await playwright.${browserName}.launch(${launchOptions(browserChannel)})
context = await browser.new_context(storage_state="${loadFileName}")`;
await cli.waitFor(expectedResult1);

View file

@ -21,24 +21,27 @@ import { folio } from './cli.fixtures';
const { it, expect } = folio;
const emptyHTML = new URL('file://' + path.join(__dirname, '..', '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, browserName }) => {
const cli = runCLI(['codegen', '--target=python', emptyHTML]);
it('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
def run(playwright):
browser = playwright.${browserName}.launch(headless=False)
browser = playwright.${browserName}.launch(${launchOptions(browserChannel)})
context = browser.new_context()`;
await cli.waitFor(expectedResult);
expect(cli.text()).toContain(expectedResult);
});
it('should print the correct context options for custom settings', async ({ runCLI, browserName }) => {
const cli = runCLI(['codegen', '--color-scheme=light', '--target=python', emptyHTML]);
it('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
def run(playwright):
browser = playwright.${browserName}.launch(headless=False)
browser = playwright.${browserName}.launch(${launchOptions(browserChannel)})
context = browser.new_context(color_scheme="light")`;
await cli.waitFor(expectedResult);
expect(cli.text()).toContain(expectedResult);
@ -46,12 +49,12 @@ def run(playwright):
it('should print the correct context options when using a device', (test, { browserName }) => {
test.skip(browserName !== 'chromium');
}, async ({ runCLI }) => {
const cli = runCLI(['codegen', '--device=Pixel 2', '--target=python', emptyHTML]);
}, async ({ browserChannel, runCLI }) => {
const cli = runCLI(['--device=Pixel 2', '--target=python', emptyHTML]);
const expectedResult = `from playwright.sync_api import sync_playwright
def run(playwright):
browser = playwright.chromium.launch(headless=False)
browser = playwright.chromium.launch(${launchOptions(browserChannel)})
context = browser.new_context(**playwright.devices["Pixel 2"])`;
await cli.waitFor(expectedResult);
expect(cli.text()).toContain(expectedResult);
@ -59,26 +62,26 @@ def run(playwright):
it('should print the correct context options when using a device and additional options', (test, { browserName }) => {
test.skip(browserName !== 'webkit');
}, async ({ runCLI }) => {
const cli = runCLI(['codegen', '--color-scheme=light', '--device=iPhone 11', '--target=python', emptyHTML]);
}, async ({ browserChannel, runCLI }) => {
const cli = runCLI(['--color-scheme=light', '--device=iPhone 11', '--target=python', emptyHTML]);
const expectedResult = `from playwright.sync_api import sync_playwright
def run(playwright):
browser = playwright.webkit.launch(headless=False)
browser = playwright.webkit.launch(${launchOptions(browserChannel)})
context = browser.new_context(**playwright.devices["iPhone 11"], color_scheme="light")`;
await cli.waitFor(expectedResult);
expect(cli.text()).toContain(expectedResult);
});
it('should save the codegen output to a file if specified', async ({ runCLI, browserName, testInfo }) => {
it('should save the codegen output to a file if specified', async ({ runCLI, browserChannel, browserName, testInfo }) => {
const tmpFile = testInfo.outputPath('script.js');
const cli = runCLI(['codegen', '--target=python', '--output', tmpFile, emptyHTML]);
const cli = runCLI(['--target=python', '--output', tmpFile, emptyHTML]);
await cli.exited;
const content = fs.readFileSync(tmpFile);
expect(content.toString()).toBe(`from playwright.sync_api import sync_playwright
def run(playwright):
browser = playwright.${browserName}.launch(headless=False)
browser = playwright.${browserName}.launch(${launchOptions(browserChannel)})
context = browser.new_context()
# Open new page
@ -98,15 +101,15 @@ with sync_playwright() as playwright:
run(playwright)`);
});
it('should print load/save storage_state', async ({ runCLI, browserName, testInfo }) => {
it('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');
const cli = runCLI(['codegen', `--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=python', emptyHTML]);
const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=python', emptyHTML]);
const expectedResult1 = `from playwright.sync_api import sync_playwright
def run(playwright):
browser = playwright.${browserName}.launch(headless=False)
browser = playwright.${browserName}.launch(${launchOptions(browserChannel)})
context = browser.new_context(storage_state="${loadFileName}")`;
await cli.waitFor(expectedResult1);

View file

@ -149,10 +149,10 @@ class Recorder {
}
}
fixtures.runCLI.init(async ({ browserName, headful }, runTest) => {
fixtures.runCLI.init(async ({ browserName, browserChannel, headful }, runTest) => {
let cli: CLIMock;
const cliFactory = (args: string[]) => {
cli = new CLIMock(browserName, !headful, args);
cli = new CLIMock(browserName, browserChannel, !headful, args);
return cli;
};
await runTest(cliFactory);
@ -166,13 +166,17 @@ class CLIMock {
private waitForCallback: () => void;
exited: Promise<void>;
constructor(browserName: string, headless: boolean, args: string[]) {
constructor(browserName: string, browserChannel: string, headless: boolean, args: string[]) {
this.data = '';
this.process = spawn('node', [
const nodeArgs = [
path.join(__dirname, '..', '..', 'lib', 'cli', 'cli.js'),
'codegen',
...args,
`--browser=${browserName}`,
], {
];
if (browserChannel)
nodeArgs.push(`--channel=${browserChannel}`);
this.process = spawn('node', nodeArgs, {
env: {
...process.env,
PWCLI_EXIT_FOR_TEST: '1',

View file

@ -39,7 +39,7 @@ fixtures.downloadsBrowser.init(async ({ server, browserType, browserOptions, tes
await browser.close();
});
fixtures.persistentDownloadsContext.init(async ({ server, launchPersistent, testInfo }, test) => {
fixtures.persistentDownloadsContext.init(async ({ server, launchPersistent, testInfo, browserChannel }, test) => {
server.setRoute('/download', (req, res) => {
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
@ -49,7 +49,8 @@ fixtures.persistentDownloadsContext.init(async ({ server, launchPersistent, test
const { context, page } = await launchPersistent(
{
downloadsPath: testInfo.outputPath(''),
acceptDownloads: true
acceptDownloads: true,
channel: browserChannel,
}
);
logOnCI('--- setting content for the page ---');

View file

@ -80,21 +80,27 @@ describe('fixtures', (suite, { platform, headful }) => {
await connectedRemoteServer.childExitCode();
});
it('should close the browser on SIGINT', async ({connectedRemoteServer}) => {
it('should close the browser on SIGINT', (test, { browserChannel }) => {
test.fixme(!!browserChannel, 'Uncomment on roll');
}, async ({connectedRemoteServer}) => {
process.kill(connectedRemoteServer.child().pid, 'SIGINT');
expect(await connectedRemoteServer.out('exitCode')).toBe('0');
expect(await connectedRemoteServer.out('signal')).toBe('null');
expect(await connectedRemoteServer.childExitCode()).toBe(130);
});
it('should close the browser on SIGTERM', async ({connectedRemoteServer}) => {
it('should close the browser on SIGTERM', (test, { browserChannel }) => {
test.fixme(!!browserChannel, 'Uncomment on roll');
}, async ({connectedRemoteServer}) => {
process.kill(connectedRemoteServer.child().pid, 'SIGTERM');
expect(await connectedRemoteServer.out('exitCode')).toBe('0');
expect(await connectedRemoteServer.out('signal')).toBe('null');
expect(await connectedRemoteServer.childExitCode()).toBe(0);
});
it('should close the browser on SIGHUP', async ({connectedRemoteServer}) => {
it('should close the browser on SIGHUP', (test, { browserChannel }) => {
test.fixme(!!browserChannel, 'Uncomment on roll');
}, async ({connectedRemoteServer}) => {
process.kill(connectedRemoteServer.child().pid, 'SIGHUP');
expect(await connectedRemoteServer.out('exitCode')).toBe('0');
expect(await connectedRemoteServer.out('signal')).toBe('null');

View file

@ -87,12 +87,12 @@ fixtures.launchPersistent.init(async ({ createUserDataDir, browserOptions, brows
await context.close();
});
fixtures.browserOptions.override(async ({ browserName, headful, slowMo }, run) => {
fixtures.browserOptions.override(async ({ browserName, headful, slowMo, browserChannel }, run) => {
const executablePath = getExecutablePath(browserName);
if (executablePath)
console.error(`Using executable at ${executablePath}`);
await run({
channel: process.env.PW_CHROMIUM_CHANNEL,
channel: browserChannel,
executablePath,
handleSIGINT: false,
slowMo,

View file

@ -34,6 +34,8 @@ config.timeout = 30000;
type PlaywrightParameters = {
// Browser type name.
browserName: 'chromium' | 'firefox' | 'webkit';
// Browser release channel, if applicable.
browserChannel: string | undefined;
// Whether to run tests headless or headful.
headful: boolean;
// Operating system.
@ -94,12 +96,14 @@ fixtures.platform.initParameter('Operating system', process.platform as ('win32'
fixtures.screenshotOnFailure.initParameter('Generate screenshot on failure', false);
fixtures.slowMo.initParameter('Slows down Playwright operations by the specified amount of milliseconds', 0);
fixtures.video.initParameter('Record videos while running tests', false);
fixtures.browserChannel.initParameter('Browser release channel', process.env.PW_CHROMIUM_CHANNEL);
fixtures.browserOptions.init(async ({ headful, slowMo }, run) => {
fixtures.browserOptions.init(async ({ headful, slowMo, browserChannel }, run) => {
await run({
handleSIGINT: false,
slowMo,
headless: !headful,
channel: browserChannel,
});
}, { scope: 'worker' });

View file

@ -69,11 +69,12 @@ export class RemoteServer {
this._didExit = false;
this._browserType = browserType;
const launchOptions = {...browserOptions,
const launchOptions = {
...browserOptions,
handleSIGINT: true,
handleSIGTERM: true,
handleSIGHUP: true,
executablePath: browserOptions.executablePath || browserType.executablePath(),
executablePath: browserOptions.channel ? undefined : browserOptions.executablePath || browserType.executablePath(),
logger: undefined,
};
const options = {

View file

@ -415,7 +415,7 @@ describe('screencast', suite => {
expect(videoPlayer.videoHeight).toBe(600);
});
it('should be 800x450 by default', async ({browser, testInfo}) => {
it('should be 800x450 by default', async ({ browser, testInfo }) => {
const context = await browser.newContext({
recordVideo: {
dir: testInfo.outputPath(''),

13
types/types.d.ts vendored
View file

@ -6356,6 +6356,19 @@ export interface BrowserType<Browser> {
*/
bypassCSP?: boolean;
/**
* Chromium distribution channel, one of
* - chrome
* - chrome-beta
* - chrome-dev
* - chrome-canary
* - msedge
* - msedge-beta
* - msedge-dev
* - msedge-canary
*/
channel?: string;
/**
* Enable Chromium sandboxing. Defaults to `true`.
*/