From 36955123f28ebc9c015f7f53f1c85d692d44eb5d Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Wed, 13 Jan 2021 11:58:03 -0800 Subject: [PATCH] fix: fix the cli tests, generate snake python --- src/cli/cli.ts | 3 +- src/cli/codegen/languages/csharp.ts | 4 +- src/cli/codegen/languages/index.ts | 2 +- src/cli/codegen/languages/javascript.ts | 4 +- src/cli/codegen/languages/python.ts | 19 +++++--- src/cli/codegen/outputs.ts | 54 ++++++++++++++++++----- test/cli/cli-codegen-csharp.spec.ts | 2 +- test/cli/cli-codegen-javascript.spec.ts | 4 +- test/cli/cli-codegen-python-async.spec.ts | 42 +++++++++--------- test/cli/cli-codegen-python.spec.ts | 36 +++++++-------- test/cli/cli.fixtures.ts | 24 +++++----- 11 files changed, 116 insertions(+), 78 deletions(-) diff --git a/src/cli/cli.ts b/src/cli/cli.ts index 272fc07b369c61..6dfabc03de787c 100755 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -341,9 +341,10 @@ async function codegen(options: Options, url: string | undefined, target: string if (process.env.PWTRACE) contextOptions.recordVideo = { dir: path.join(process.cwd(), '.trace') }; - const outputs: CodeGeneratorOutput[] = [new TerminalOutput(process.stdout, languageGenerator.highligherType())]; + const outputs: CodeGeneratorOutput[] = [TerminalOutput.create(process.stdout, languageGenerator.highlighterType())]; if (outputFile) outputs.push(new FileOutput(outputFile)); + console.log(outputFile); const output = new OutputMultiplexer(outputs); const generator = new CodeGenerator(browserName, launchOptions, contextOptions, output, languageGenerator, options.device, options.saveStorage); diff --git a/src/cli/codegen/languages/csharp.ts b/src/cli/codegen/languages/csharp.ts index 1c6c512030d1bb..de97fef718d948 100644 --- a/src/cli/codegen/languages/csharp.ts +++ b/src/cli/codegen/languages/csharp.ts @@ -23,7 +23,7 @@ import { MouseClickOptions, toModifiers } from '../utils'; export class CSharpLanguageGenerator implements LanguageGenerator { - highligherType(): HighlighterType { + highlighterType(): HighlighterType { return 'csharp'; } @@ -155,7 +155,7 @@ export class CSharpLanguageGenerator implements LanguageGenerator { } generateFooter(saveStorage: string | undefined): string { - const storageStateLine = saveStorage ? `\nawait context.StorageStateAsync(path: "${saveStorage}")` : ''; + const storageStateLine = saveStorage ? `\nawait context.StorageStateAsync(path: "${saveStorage}");` : ''; return `// ---------------------${storageStateLine}`; } } diff --git a/src/cli/codegen/languages/index.ts b/src/cli/codegen/languages/index.ts index 441b139ddb5495..8611dd62431992 100644 --- a/src/cli/codegen/languages/index.ts +++ b/src/cli/codegen/languages/index.ts @@ -23,7 +23,7 @@ export interface LanguageGenerator { generateHeader(browserName: string, launchOptions: LaunchOptions, contextOptions: BrowserContextOptions, deviceName?: string): string; generateAction(actionInContext: ActionInContext, performingAction: boolean): string; generateFooter(saveStorage: string | undefined): string; - highligherType(): HighlighterType; + highlighterType(): HighlighterType; } export { JavaScriptLanguageGenerator } from './javascript'; \ No newline at end of file diff --git a/src/cli/codegen/languages/javascript.ts b/src/cli/codegen/languages/javascript.ts index bf0abc7d486b55..ade581f78b8c15 100644 --- a/src/cli/codegen/languages/javascript.ts +++ b/src/cli/codegen/languages/javascript.ts @@ -23,7 +23,7 @@ import { MouseClickOptions, toModifiers } from '../utils'; export class JavaScriptLanguageGenerator implements LanguageGenerator { - highligherType(): HighlighterType { + highlighterType(): HighlighterType { return 'javascript'; } @@ -157,7 +157,7 @@ export class JavaScriptLanguageGenerator implements LanguageGenerator { } generateFooter(saveStorage: string | undefined): string { - const storageStateLine = saveStorage ? `\n await context.storageState({ path: '${saveStorage}' })` : ''; + const storageStateLine = saveStorage ? `\n await context.storageState({ path: '${saveStorage}' });` : ''; return ` // ---------------------${storageStateLine} await context.close(); await browser.close(); diff --git a/src/cli/codegen/languages/python.ts b/src/cli/codegen/languages/python.ts index dadffc4d4ef74b..64a0ca075543d4 100644 --- a/src/cli/codegen/languages/python.ts +++ b/src/cli/codegen/languages/python.ts @@ -32,7 +32,7 @@ export class PythonLanguageGenerator implements LanguageGenerator { this._asyncPrefix = isAsync ? 'async ' : ''; } - highligherType(): HighlighterType { + highlighterType(): HighlighterType { return 'python'; } @@ -43,7 +43,7 @@ export class PythonLanguageGenerator implements LanguageGenerator { formatter.add('# ' + actionTitle(action)); if (action.name === 'openPage') { - formatter.add(`${pageAlias} = ${this._awaitPrefix}context.newPage()`); + formatter.add(`${pageAlias} = ${this._awaitPrefix}context.new_page()`); if (action.url && action.url !== 'about:blank' && action.url !== 'chrome://newtab/') formatter.add(`${pageAlias}.goto('${action.url}')`); return formatter.format(); @@ -155,21 +155,21 @@ from playwright.async_api import async_playwright async def run(playwright) { browser = await playwright.${browserName}.launch(${formatOptions(launchOptions, false)}) - context = await browser.newContext(${formatContextOptions(contextOptions, deviceName)})`); + context = await browser.new_context(${formatContextOptions(contextOptions, deviceName)})`); } else { formatter.add(` from playwright.sync_api import sync_playwright def run(playwright) { browser = playwright.${browserName}.launch(${formatOptions(launchOptions, false)}) - context = browser.newContext(${formatContextOptions(contextOptions, deviceName)})`); + context = browser.new_context(${formatContextOptions(contextOptions, deviceName)})`); } return formatter.format(); } generateFooter(saveStorage: string | undefined): string { if (this._isAsync) { - const storageStateLine = saveStorage ? `\n await context.storageState(path="${saveStorage}")` : ''; + const storageStateLine = saveStorage ? `\n await context.storage_state(path="${saveStorage}")` : ''; return ` # ---------------------${storageStateLine} await context.close() await browser.close() @@ -179,7 +179,7 @@ async def main(): await run(playwright) asyncio.run(main())`; } else { - const storageStateLine = saveStorage ? `\n context.storageState(path="${saveStorage}")` : ''; + const storageStateLine = saveStorage ? `\n context.storage_state(path="${saveStorage}")` : ''; return ` # ---------------------${storageStateLine} context.close() browser.close() @@ -204,11 +204,16 @@ function formatValue(value: any): string { return String(value); } +function toSnakeCase(name: string): string { + const toSnakeCaseRegex = /((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))/g; + return name.replace(toSnakeCaseRegex, `_$1`).toLowerCase(); +} + function formatOptions(value: any, hasArguments: boolean): string { const keys = Object.keys(value); if (!keys.length) return ''; - return (hasArguments ? ', ' : '') + keys.map(key => `${key}=${formatValue(value[key])}`).join(', '); + return (hasArguments ? ', ' : '') + keys.map(key => `${toSnakeCase(key)}=${formatValue(value[key])}`).join(', '); } function formatContextOptions(options: BrowserContextOptions, deviceName: string | undefined): string { diff --git a/src/cli/codegen/outputs.ts b/src/cli/codegen/outputs.ts index 5a899b36e68051..f3251163ff8148 100644 --- a/src/cli/codegen/outputs.ts +++ b/src/cli/codegen/outputs.ts @@ -42,24 +42,32 @@ export class OutputMultiplexer implements CodeGeneratorOutput { } } -export class FileOutput implements CodeGeneratorOutput { - private _fileName: string; - private _lines: string[]; - constructor(fileName: string) { - this._fileName = fileName; - this._lines = []; - } +export class BufferOutput { + lines: string[] = []; printLn(text: string) { - this._lines.push(...text.trimEnd().split('\n')); + this.lines.push(...text.trimEnd().split('\n')); } popLn(text: string) { - this._lines.length -= text.trimEnd().split('\n').length; + this.lines.length -= text.trimEnd().split('\n').length; + } + + buffer(): string { + return this.lines.join('\n'); + } +} + +export class FileOutput extends BufferOutput implements CodeGeneratorOutput { + private _fileName: string; + + constructor(fileName: string) { + super(); + this._fileName = fileName; } flush() { - fs.writeFileSync(this._fileName, this._lines.join('\n')); + fs.writeFileSync(this._fileName, this.buffer()); } } @@ -67,6 +75,12 @@ export class TerminalOutput implements CodeGeneratorOutput { private _output: Writable private _language: string; + static create(output: Writable, language: string) { + if (process.stdout.columns) + return new TerminalOutput(output, language); + return new FlushingTerminalOutput(output); + } + constructor(output: Writable, language: string) { this._output = output; this._language = language; @@ -112,3 +126,23 @@ export class TerminalOutput implements CodeGeneratorOutput { flush() {} } + +export class FlushingTerminalOutput extends BufferOutput implements CodeGeneratorOutput { + private _output: Writable + + constructor(output: Writable) { + super(); + this._output = output; + } + + printLn(text: string) { + super.printLn(text); + this._output.write('-------------8<-------------\n'); + this._output.write(this.buffer() + '\n'); + this._output.write('-------------8<-------------\n'); + } + + flush() { + this._output.write(this.buffer() + '\n'); + } +} diff --git a/test/cli/cli-codegen-csharp.spec.ts b/test/cli/cli-codegen-csharp.spec.ts index d9a5523aa68bab..a30d7fdcb2c47a 100644 --- a/test/cli/cli-codegen-csharp.spec.ts +++ b/test/cli/cli-codegen-csharp.spec.ts @@ -136,7 +136,7 @@ it('should print load/save storageState', async ({ runCLI, testInfo }) => { const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, 'codegen', '--target=csharp', emptyHTML]); const expectedResult = `await Playwright.InstallAsync(); using var playwright = await Playwright.CreateAsync(); -await using var browser = await playwright.Chromium.LaunchAsync(); +await using var browser = await playwright.Chromium.LaunchAsync(headless: false); var context = await browser.NewContextAsync(storageState: "${loadFileName}"); // Open new page diff --git a/test/cli/cli-codegen-javascript.spec.ts b/test/cli/cli-codegen-javascript.spec.ts index d77adcf371cfd2..e281f885d1e300 100644 --- a/test/cli/cli-codegen-javascript.spec.ts +++ b/test/cli/cli-codegen-javascript.spec.ts @@ -86,7 +86,7 @@ it('should save the codegen output to a file if specified', async ({ runCLI, tes const tmpFile = testInfo.outputPath('script.js'); const cli = runCLI(['codegen', '--output', tmpFile, emptyHTML]); await cli.exited; - const content = await fs.readFileSync(tmpFile); + const content = fs.readFileSync(tmpFile); expect(content.toString()).toBe(`const { chromium } = require('playwright'); (async () => { @@ -115,7 +115,7 @@ it('should print load/save storageState', async ({ runCLI, testInfo }) => { const saveFileName = testInfo.outputPath('save.json'); await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8'); const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, 'codegen', emptyHTML]); - const expectedResult = `const { chromium, devices } = require('playwright'); + const expectedResult = `const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch({ diff --git a/test/cli/cli-codegen-python-async.spec.ts b/test/cli/cli-codegen-python-async.spec.ts index 844987ce4fa077..c1eac595f02026 100644 --- a/test/cli/cli-codegen-python-async.spec.ts +++ b/test/cli/cli-codegen-python-async.spec.ts @@ -29,7 +29,7 @@ from playwright.async_api import async_playwright async def run(playwright): browser = await playwright.chromium.launch(headless=False) - context = await browser.newContext()`; + context = await browser.new_context()`; await cli.waitFor(expectedResult); expect(cli.text()).toContain(expectedResult); }); @@ -41,7 +41,7 @@ from playwright.async_api import async_playwright async def run(playwright): browser = await playwright.chromium.launch(headless=False) - context = await browser.newContext(colorScheme="light")`; + context = await browser.new_context(color_scheme="light")`; await cli.waitFor(expectedResult); expect(cli.text()).toContain(expectedResult); }); @@ -53,7 +53,7 @@ from playwright.async_api import async_playwright async def run(playwright): browser = await playwright.chromium.launch(headless=False) - context = await browser.newContext(**playwright.devices["Pixel 2"])`; + context = await browser.new_context(**playwright.devices["Pixel 2"])`; await cli.waitFor(expectedResult); expect(cli.text()).toContain(expectedResult); }); @@ -65,7 +65,7 @@ from playwright.async_api import async_playwright async def run(playwright): browser = await playwright.chromium.launch(headless=False) - context = await browser.newContext(**playwright.devices["Pixel 2"], colorScheme="light")`; + context = await browser.new_context(**playwright.devices["Pixel 2"], color_scheme="light")`; await cli.waitFor(expectedResult); expect(cli.text()).toContain(expectedResult); }); @@ -80,10 +80,10 @@ from playwright.async_api import async_playwright async def run(playwright): browser = await playwright.chromium.launch(headless=False) - context = await browser.newContext() + context = await browser.new_context() # Open new page - page = await context.newPage() + page = await context.new_page() # Go to ${emptyHTML} await page.goto("${emptyHTML}") @@ -101,29 +101,29 @@ async def main(): asyncio.run(main())`); }); -it('should print load/save storageState', async ({ runCLI, testInfo }) => { +it('should print load/save storage_state', async ({ 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([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, 'codegen', '--target=python-async', emptyHTML]); const expectedResult = `import asyncio - from playwright.async_api import async_playwright +from playwright.async_api import async_playwright - async def run(playwright): - browser = await playwright.chromium.launch(headless=False) - context = await browser.newContext(storageState="${loadFileName}") +async def run(playwright): + browser = await playwright.chromium.launch(headless=False) + context = await browser.new_context(storage_state="${loadFileName}") - # Open new page - page = await context.newPage() + # Open new page + page = await context.new_page() - # --------------------- - await context.storageState(path="${saveFileName}") - await context.close() - await browser.close() + # --------------------- + await context.storage_state(path="${saveFileName}") + await context.close() + await browser.close() - async def main(): - async with async_playwright() as playwright: - await run(playwright) - asyncio.run(main())`; +async def main(): + async with async_playwright() as playwright: + await run(playwright) +asyncio.run(main())`; await cli.waitFor(expectedResult); }); diff --git a/test/cli/cli-codegen-python.spec.ts b/test/cli/cli-codegen-python.spec.ts index 2638f556c3fc39..29dae3bea75ae2 100644 --- a/test/cli/cli-codegen-python.spec.ts +++ b/test/cli/cli-codegen-python.spec.ts @@ -28,7 +28,7 @@ it('should print the correct imports and context options', async ({ runCLI }) => def run(playwright): browser = playwright.chromium.launch(headless=False) - context = browser.newContext()`; + context = browser.new_context()`; await cli.waitFor(expectedResult); expect(cli.text()).toContain(expectedResult); }); @@ -39,7 +39,7 @@ it('should print the correct context options for custom settings', async ({ runC def run(playwright): browser = playwright.chromium.launch(headless=False) - context = browser.newContext(colorScheme="light")`; + context = browser.new_context(color_scheme="light")`; await cli.waitFor(expectedResult); expect(cli.text()).toContain(expectedResult); }); @@ -50,7 +50,7 @@ it('should print the correct context options when using a device', async ({ runC def run(playwright): browser = playwright.chromium.launch(headless=False) - context = browser.newContext(**playwright.devices["Pixel 2"])`; + context = browser.new_context(**playwright.devices["Pixel 2"])`; await cli.waitFor(expectedResult); expect(cli.text()).toContain(expectedResult); }); @@ -61,7 +61,7 @@ it('should print the correct context options when using a device and additional def run(playwright): browser = playwright.chromium.launch(headless=False) - context = browser.newContext(**playwright.devices["Pixel 2"], colorScheme="light")`; + context = browser.new_context(**playwright.devices["Pixel 2"], color_scheme="light")`; await cli.waitFor(expectedResult); expect(cli.text()).toContain(expectedResult); }); @@ -75,10 +75,10 @@ it('should save the codegen output to a file if specified', async ({ runCLI, tes def run(playwright): browser = playwright.chromium.launch(headless=False) - context = browser.newContext() + context = browser.new_context() # Open new page - page = context.newPage() + page = context.new_page() # Go to ${emptyHTML} page.goto("${emptyHTML}") @@ -94,26 +94,26 @@ with sync_playwright() as playwright: run(playwright)`); }); -it('should print load/save storageState', async ({ runCLI, testInfo }) => { +it('should print load/save storage_state', async ({ 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([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, 'codegen', '--target=python', emptyHTML]); const expectedResult = `from playwright.sync_api import sync_playwright - def run(playwright): - browser = playwright.chromium.launch(headless=False) - context = browser.newContext(storageState="${loadFileName}") +def run(playwright): + browser = playwright.chromium.launch(headless=False) + context = browser.new_context(storage_state="${loadFileName}") - # Open new page - page = context.newPage() + # Open new page + page = context.new_page() - # --------------------- - context.storageState(path="${saveFileName}") - context.close() - browser.close() + # --------------------- + context.storage_state(path="${saveFileName}") + context.close() + browser.close() - with sync_playwright() as playwright: - run(playwright)`; +with sync_playwright() as playwright: + run(playwright)`; await cli.waitFor(expectedResult); }); diff --git a/test/cli/cli.fixtures.ts b/test/cli/cli.fixtures.ts index 5f24117058ebb6..c06a589e8eba71 100644 --- a/test/cli/cli.fixtures.ts +++ b/test/cli/cli.fixtures.ts @@ -197,7 +197,7 @@ fixtures.runCLI.init(async ({ }, runTest) => { return cli; }; await runTest(cliFactory); - cli.kill(); + await cli.exited; }); class CLIMock { @@ -216,18 +216,20 @@ class CLIMock { env: { ...process.env, PWCLI_EXIT_FOR_TEST: '1' - } + }, + stdio: 'pipe' }); - this.process.stdout.on('data', line => { - this.data += removeAnsiColors(line.toString()); + this.process.stdout.on('data', data => { + this.data = data.toString(); if (this.waitForCallback && this.data.includes(this.waitForText)) this.waitForCallback(); }); - this.exited = new Promise(r => this.process.on('exit', () => { - if (this.waitForCallback) - this.waitForCallback(); - return r(); - })); + this.exited = new Promise((f, r) => { + this.process.stderr.on('data', data => { + r(new Error(data)); + }); + this.process.on('exit', f); + }); } async waitFor(text: string): Promise { @@ -240,10 +242,6 @@ class CLIMock { text() { return removeAnsiColors(this.data); } - - kill() { - this.process.kill(); - } } interface httpServer {