mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(codegen): handle more context options (#15319)
The following options now work across languages: - `recordHar` - `serviceWorkers` In addition, object properties are now sorted alphabetically. Drive-by: fixed `--target` help message to include all available targets.
This commit is contained in:
parent
5f03bd9477
commit
d60b8aba23
@ -68,7 +68,7 @@ Examples:
|
||||
commandWithOpenOptions('codegen [url]', 'open page and generate code for user actions',
|
||||
[
|
||||
['-o, --output <file name>', 'saves the generated script to a file'],
|
||||
['--target <language>', `language to generate, one of javascript, test, python, python-async, csharp`, language()],
|
||||
['--target <language>', `language to generate, one of javascript, test, python, python-async, pytest, csharp, java`, language()],
|
||||
]).action(function(url, options) {
|
||||
codegen(options, url, options.target, options.output).catch(logErrorAndExit);
|
||||
}).addHelpText('afterAll', `
|
||||
|
@ -161,14 +161,14 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
|
||||
|
||||
function formatObject(value: any, indent = ' ', name = ''): string {
|
||||
if (typeof value === 'string') {
|
||||
if (['permissions', 'colorScheme', 'modifiers', 'button'].includes(name))
|
||||
if (['permissions', 'colorScheme', 'modifiers', 'button', 'recordHarContent', 'recordHarMode', 'serviceWorkers'].includes(name))
|
||||
return `${getClassName(name)}.${toPascal(value)}`;
|
||||
return quote(value);
|
||||
}
|
||||
if (Array.isArray(value))
|
||||
return `new[] { ${value.map(o => formatObject(o, indent, name)).join(', ')} }`;
|
||||
if (typeof value === 'object') {
|
||||
const keys = Object.keys(value);
|
||||
const keys = Object.keys(value).filter(key => value[key] !== undefined).sort();
|
||||
if (!keys.length)
|
||||
return name ? `new ${getClassName(name)}` : '';
|
||||
const tokens: string[] = [];
|
||||
@ -193,6 +193,9 @@ function getClassName(value: string): string {
|
||||
case 'permissions': return 'ContextPermission';
|
||||
case 'modifiers': return 'KeyboardModifier';
|
||||
case 'button': return 'MouseButton';
|
||||
case 'recordHarMode': return 'HarMode';
|
||||
case 'recordHarContent': return 'HarContentPolicy';
|
||||
case 'serviceWorkers': return 'ServiceWorkerPolicy';
|
||||
default: return toPascal(value);
|
||||
}
|
||||
}
|
||||
@ -209,19 +212,32 @@ function toPascal(value: string): string {
|
||||
return value[0].toUpperCase() + value.slice(1);
|
||||
}
|
||||
|
||||
function convertContextOptions(options: BrowserContextOptions): any {
|
||||
const result: any = { ...options };
|
||||
if (options.recordHar) {
|
||||
result['recordHarPath'] = options.recordHar.path;
|
||||
result['recordHarContent'] = options.recordHar.content;
|
||||
result['recordHarMode'] = options.recordHar.mode;
|
||||
result['recordHarOmitContent'] = options.recordHar.omitContent;
|
||||
result['recordHarUrlFilter'] = options.recordHar.urlFilter;
|
||||
delete result.recordHar;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function formatContextOptions(options: BrowserContextOptions, deviceName: string | undefined): string {
|
||||
const device = deviceName && deviceDescriptors[deviceName];
|
||||
if (!device) {
|
||||
if (!Object.entries(options).length)
|
||||
return '';
|
||||
return formatObject(options, ' ', 'BrowserNewContextOptions');
|
||||
return formatObject(convertContextOptions(options), ' ', 'BrowserNewContextOptions');
|
||||
}
|
||||
|
||||
options = sanitizeDeviceOptions(device, options);
|
||||
if (!Object.entries(options).length)
|
||||
return `playwright.Devices[${quote(deviceName!)}]`;
|
||||
|
||||
return formatObject(options, ' ', `BrowserNewContextOptions(playwright.Devices[${quote(deviceName!)}])`);
|
||||
return formatObject(convertContextOptions(options), ' ', `BrowserNewContextOptions(playwright.Devices[${quote(deviceName!)}])`);
|
||||
}
|
||||
|
||||
class CSharpFormatter {
|
||||
|
@ -175,13 +175,13 @@ function formatSelectOption(options: string | string[]): string {
|
||||
|
||||
function formatLaunchOptions(options: any): string {
|
||||
const lines = [];
|
||||
if (!Object.keys(options).length)
|
||||
if (!Object.keys(options).filter(key => options[key] !== undefined).length)
|
||||
return '';
|
||||
lines.push('new BrowserType.LaunchOptions()');
|
||||
if (typeof options.headless === 'boolean')
|
||||
lines.push(` .setHeadless(false)`);
|
||||
if (options.channel)
|
||||
lines.push(` .setChannel(${quote(options.channel)})`);
|
||||
if (typeof options.headless === 'boolean')
|
||||
lines.push(` .setHeadless(false)`);
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
@ -210,6 +210,18 @@ function formatContextOptions(contextOptions: BrowserContextOptions, deviceName:
|
||||
lines.push(` .setLocale(${quote(options.locale)})`);
|
||||
if (options.proxy)
|
||||
lines.push(` .setProxy(new Proxy(${quote(options.proxy.server)}))`);
|
||||
if (options.recordHar?.content)
|
||||
lines.push(` .setRecordHarContent(HarContentPolicy.${options.recordHar?.content.toUpperCase()})`);
|
||||
if (options.recordHar?.mode)
|
||||
lines.push(` .setRecordHarMode(HarMode.${options.recordHar?.mode.toUpperCase()})`);
|
||||
if (options.recordHar?.omitContent)
|
||||
lines.push(` .setRecordHarOmitContent(true)`);
|
||||
if (options.recordHar?.path)
|
||||
lines.push(` .setRecordHarPath(Paths.get(${quote(options.recordHar.path)}))`);
|
||||
if (options.recordHar?.urlFilter)
|
||||
lines.push(` .setRecordHarUrlFilter(${quote(options.recordHar.urlFilter as string)})`);
|
||||
if (options.serviceWorkers)
|
||||
lines.push(` .setServiceWorkers(ServiceWorkerPolicy.${options.serviceWorkers.toUpperCase()})`);
|
||||
if (options.storageState)
|
||||
lines.push(` .setStorageStatePath(Paths.get(${quote(options.storageState as string)}))`);
|
||||
if (options.timezoneId)
|
||||
|
@ -222,7 +222,7 @@ function formatObject(value: any, indent = ' '): string {
|
||||
if (Array.isArray(value))
|
||||
return `[${value.map(o => formatObject(o)).join(', ')}]`;
|
||||
if (typeof value === 'object') {
|
||||
const keys = Object.keys(value);
|
||||
const keys = Object.keys(value).filter(key => value[key] !== undefined).sort();
|
||||
if (!keys.length)
|
||||
return '{}';
|
||||
const tokens: string[] = [];
|
||||
|
@ -240,7 +240,7 @@ function toSnakeCase(name: string): string {
|
||||
}
|
||||
|
||||
function formatOptions(value: any, hasArguments: boolean, asDict?: boolean): string {
|
||||
const keys = Object.keys(value);
|
||||
const keys = Object.keys(value).filter(key => value[key] !== undefined).sort();
|
||||
if (!keys.length)
|
||||
return '';
|
||||
return (hasArguments ? ', ' : '') + keys.map(key => {
|
||||
@ -250,11 +250,24 @@ function formatOptions(value: any, hasArguments: boolean, asDict?: boolean): str
|
||||
}).join(', ');
|
||||
}
|
||||
|
||||
function convertContextOptions(options: BrowserContextOptions): any {
|
||||
const result: any = { ...options };
|
||||
if (options.recordHar) {
|
||||
result['record_har_path'] = options.recordHar.path;
|
||||
result['record_har_content'] = options.recordHar.content;
|
||||
result['record_har_mode'] = options.recordHar.mode;
|
||||
result['record_har_omit_content'] = options.recordHar.omitContent;
|
||||
result['record_har_url_filter'] = options.recordHar.urlFilter;
|
||||
delete result.recordHar;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function formatContextOptions(options: BrowserContextOptions, deviceName: string | undefined, asDict?: boolean): string {
|
||||
const device = deviceName && deviceDescriptors[deviceName];
|
||||
if (!device)
|
||||
return formatOptions(options, false, asDict);
|
||||
return `**playwright.devices[${quote(deviceName!)}]` + formatOptions(sanitizeDeviceOptions(device, options), true, asDict);
|
||||
return formatOptions(convertContextOptions(options), false, asDict);
|
||||
return `**playwright.devices[${quote(deviceName!)}]` + formatOptions(convertContextOptions(sanitizeDeviceOptions(device, options)), true, asDict);
|
||||
}
|
||||
|
||||
class PythonFormatter {
|
||||
|
@ -544,14 +544,6 @@ test.describe('cli codegen', () => {
|
||||
expect(fs.existsSync(traceFileName)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should --save-har', async ({ runCLI }, testInfo) => {
|
||||
const harFileName = testInfo.outputPath('har.har');
|
||||
const cli = runCLI([`--save-har=${harFileName}`]);
|
||||
await cli.exited;
|
||||
const json = JSON.parse(fs.readFileSync(harFileName, 'utf-8'));
|
||||
expect(json.log.creator.name).toBe('Playwright');
|
||||
});
|
||||
|
||||
test('should fill tricky characters', async ({ page, openRecorder }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
|
@ -20,7 +20,7 @@ import { test, expect } from './inspectorTest';
|
||||
|
||||
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,`;
|
||||
return channel ? `Channel = "${channel}",\n Headless = false,` : `Headless = false,`;
|
||||
};
|
||||
|
||||
function capitalize(browserName: string): string {
|
||||
@ -70,21 +70,21 @@ test('should print the correct context options for custom settings', async ({ br
|
||||
});
|
||||
var context = await browser.NewContextAsync(new BrowserNewContextOptions
|
||||
{
|
||||
ViewportSize = new ViewportSize
|
||||
{
|
||||
Width = 1280,
|
||||
Height = 720,
|
||||
},
|
||||
ColorScheme = ColorScheme.Dark,
|
||||
Geolocation = new Geolocation
|
||||
{
|
||||
Latitude = 37.819722m,
|
||||
Longitude = -122.478611m,
|
||||
},
|
||||
Permissions = new[] { ContextPermission.Geolocation },
|
||||
UserAgent = "hardkodemium",
|
||||
Locale = "es",
|
||||
ColorScheme = ColorScheme.Dark,
|
||||
Permissions = new[] { ContextPermission.Geolocation },
|
||||
TimezoneId = "Europe/Rome",
|
||||
UserAgent = "hardkodemium",
|
||||
ViewportSize = new ViewportSize
|
||||
{
|
||||
Height = 720,
|
||||
Width = 1280,
|
||||
},
|
||||
});`;
|
||||
await cli.waitFor(expectedResult);
|
||||
expect(cli.text()).toContain(expectedResult);
|
||||
@ -131,21 +131,21 @@ test('should print the correct context options when using a device and additiona
|
||||
});
|
||||
var context = await browser.NewContextAsync(new BrowserNewContextOptions(playwright.Devices["iPhone 11"])
|
||||
{
|
||||
UserAgent = "hardkodemium",
|
||||
ViewportSize = new ViewportSize
|
||||
{
|
||||
Width = 1280,
|
||||
Height = 720,
|
||||
},
|
||||
ColorScheme = ColorScheme.Dark,
|
||||
Geolocation = new Geolocation
|
||||
{
|
||||
Latitude = 37.819722m,
|
||||
Longitude = -122.478611m,
|
||||
},
|
||||
Permissions = new[] { ContextPermission.Geolocation },
|
||||
Locale = "es",
|
||||
ColorScheme = ColorScheme.Dark,
|
||||
Permissions = new[] { ContextPermission.Geolocation },
|
||||
TimezoneId = "Europe/Rome",
|
||||
UserAgent = "hardkodemium",
|
||||
ViewportSize = new ViewportSize
|
||||
{
|
||||
Height = 720,
|
||||
Width = 1280,
|
||||
},
|
||||
});`;
|
||||
|
||||
await cli.waitFor(expectedResult);
|
||||
@ -176,3 +176,20 @@ test('should print load/save storageState', async ({ browserName, channel, runCL
|
||||
`;
|
||||
await cli.waitFor(expectedResult2);
|
||||
});
|
||||
|
||||
test('should work with --save-har', async ({ runCLI }, testInfo) => {
|
||||
const harFileName = testInfo.outputPath('har.har');
|
||||
const cli = runCLI(['--target=csharp', `--save-har=${harFileName}`]);
|
||||
const expectedResult = `
|
||||
var context = await browser.NewContextAsync(new BrowserNewContextOptions
|
||||
{
|
||||
RecordHarMode = HarMode.Minimal,
|
||||
RecordHarPath = ${JSON.stringify(harFileName)},
|
||||
ServiceWorkers = ServiceWorkerPolicy.Block,
|
||||
});`;
|
||||
await cli.waitFor(expectedResult).catch(e => e);
|
||||
expect(cli.text()).toContain(expectedResult);
|
||||
await cli.exited;
|
||||
const json = JSON.parse(fs.readFileSync(harFileName, 'utf-8'));
|
||||
expect(json.log.creator.name).toBe('Playwright');
|
||||
});
|
||||
|
@ -20,7 +20,7 @@ import { test, expect } from './inspectorTest';
|
||||
|
||||
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)';
|
||||
return channel ? `.setChannel("${channel}")\n .setHeadless(false)` : '.setHeadless(false)';
|
||||
};
|
||||
|
||||
test('should print the correct imports and context options', async ({ runCLI, channel, browserName }) => {
|
||||
@ -83,10 +83,24 @@ test('should print load/save storage_state', async ({ runCLI, browserName }, tes
|
||||
await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8');
|
||||
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.replace(/\\/g, '\\\\')}")));`;
|
||||
.setStorageStatePath(Paths.get(${JSON.stringify(loadFileName)})));`;
|
||||
await cli.waitFor(expectedResult1);
|
||||
|
||||
const expectedResult2 = `
|
||||
context.storageState(new BrowserContext.StorageStateOptions().setPath("${saveFileName.replace(/\\/g, '\\\\')}"))`;
|
||||
await cli.waitFor(expectedResult2);
|
||||
});
|
||||
|
||||
test('should work with --save-har', async ({ runCLI }, testInfo) => {
|
||||
const harFileName = testInfo.outputPath('har.har');
|
||||
const cli = runCLI(['--target=java', `--save-har=${harFileName}`]);
|
||||
const expectedResult = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.setRecordHarMode(HarMode.MINIMAL)
|
||||
.setRecordHarPath(Paths.get(${JSON.stringify(harFileName)}))
|
||||
.setServiceWorkers(ServiceWorkerPolicy.BLOCK));`;
|
||||
await cli.waitFor(expectedResult).catch(e => e);
|
||||
expect(cli.text()).toContain(expectedResult);
|
||||
await cli.exited;
|
||||
const json = JSON.parse(fs.readFileSync(harFileName, 'utf-8'));
|
||||
expect(json.log.creator.name).toBe('Playwright');
|
||||
});
|
||||
|
@ -21,7 +21,7 @@ import { test, expect } from './inspectorTest';
|
||||
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';
|
||||
return channel ? `channel: '${channel}',\n headless: false` : 'headless: false';
|
||||
};
|
||||
|
||||
test('should print the correct imports and context options', async ({ browserName, channel, runCLI }) => {
|
||||
|
@ -20,7 +20,7 @@ import { test, expect } from './inspectorTest';
|
||||
|
||||
const emptyHTML = new URL('file://' + path.join(__dirname, '..', '..', 'assets', 'empty.html')).toString();
|
||||
const launchOptions = (channel: string) => {
|
||||
return channel ? `headless=False, channel="${channel}"` : 'headless=False';
|
||||
return channel ? `channel="${channel}", headless=False` : 'headless=False';
|
||||
};
|
||||
|
||||
test('should print the correct imports and context options', async ({ browserName, channel, runCLI }) => {
|
||||
@ -151,3 +151,14 @@ asyncio.run(main())
|
||||
`;
|
||||
await cli.waitFor(expectedResult2);
|
||||
});
|
||||
|
||||
test('should work with --save-har', async ({ runCLI }, testInfo) => {
|
||||
const harFileName = testInfo.outputPath('har.har');
|
||||
const cli = runCLI(['--target=python-async', `--save-har=${harFileName}`]);
|
||||
const expectedResult = `context = await browser.new_context(record_har_mode="minimal", record_har_path=${JSON.stringify(harFileName)}, service_workers="block")`;
|
||||
await cli.waitFor(expectedResult).catch(e => e);
|
||||
expect(cli.text()).toContain(expectedResult);
|
||||
await cli.exited;
|
||||
const json = JSON.parse(fs.readFileSync(harFileName, 'utf-8'));
|
||||
expect(json.log.creator.name).toBe('Playwright');
|
||||
});
|
||||
|
@ -20,7 +20,7 @@ import { test, expect } from './inspectorTest';
|
||||
|
||||
const emptyHTML = new URL('file://' + path.join(__dirname, '..', '..', 'assets', 'empty.html')).toString();
|
||||
const launchOptions = (channel: string) => {
|
||||
return channel ? `headless=False, channel="${channel}"` : 'headless=False';
|
||||
return channel ? `channel="${channel}", headless=False` : 'headless=False';
|
||||
};
|
||||
|
||||
test('should print the correct imports and context options', async ({ runCLI, channel, browserName }) => {
|
||||
|
@ -90,3 +90,18 @@ test('test', async ({ page }) => {`;
|
||||
|
||||
await cli.waitFor(expectedResult);
|
||||
});
|
||||
|
||||
test('should work with --save-har', async ({ runCLI }, testInfo) => {
|
||||
const harFileName = testInfo.outputPath('har.har');
|
||||
const cli = runCLI(['--target=test', `--save-har=${harFileName}`]);
|
||||
const expectedResult = `
|
||||
recordHar: {
|
||||
mode: 'minimal',
|
||||
path: '${harFileName.replace(/\\/g, '\\\\')}'
|
||||
}`;
|
||||
await cli.waitFor(expectedResult).catch(e => e);
|
||||
expect(cli.text()).toContain(expectedResult);
|
||||
await cli.exited;
|
||||
const json = JSON.parse(fs.readFileSync(harFileName, 'utf-8'));
|
||||
expect(json.log.creator.name).toBe('Playwright');
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user