mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-14 13:45:36 +03:00
feat(java): implement codegen (#5692)
This commit is contained in:
parent
5903e771da
commit
d3eff50386
211
src/server/supplements/recorder/java.ts
Normal file
211
src/server/supplements/recorder/java.ts
Normal file
@ -0,0 +1,211 @@
|
||||
/**
|
||||
* 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 { BrowserContextOptions } from '../../../..';
|
||||
import { LanguageGenerator, LanguageGeneratorOptions, toSignalMap } from './language';
|
||||
import { ActionInContext } from './codeGenerator';
|
||||
import { Action, actionTitle } from './recorderActions';
|
||||
import { toModifiers } from './utils';
|
||||
import deviceDescriptors = require('../../deviceDescriptors');
|
||||
import { JavaScriptFormatter } from './javascript';
|
||||
|
||||
export class JavaLanguageGenerator implements LanguageGenerator {
|
||||
id = 'java';
|
||||
fileName = '<java>';
|
||||
highlighter = 'java';
|
||||
|
||||
generateAction(actionInContext: ActionInContext): string {
|
||||
const { action, pageAlias } = actionInContext;
|
||||
const formatter = new JavaScriptFormatter(6);
|
||||
formatter.newLine();
|
||||
formatter.add('// ' + actionTitle(action));
|
||||
|
||||
if (action.name === 'openPage') {
|
||||
formatter.add(`Page ${pageAlias} = context.newPage();`);
|
||||
if (action.url && action.url !== 'about:blank' && action.url !== 'chrome://newtab/')
|
||||
formatter.add(`${pageAlias}.navigate("${action.url}");`);
|
||||
return formatter.format();
|
||||
}
|
||||
|
||||
const subject = actionInContext.isMainFrame ? pageAlias :
|
||||
(actionInContext.frameName ?
|
||||
`${pageAlias}.frame(${quote(actionInContext.frameName)})` :
|
||||
`${pageAlias}.frameByUrl(${quote(actionInContext.frameUrl)})`);
|
||||
|
||||
const signals = toSignalMap(action);
|
||||
|
||||
if (signals.dialog) {
|
||||
formatter.add(` ${pageAlias}.onceDialog(dialog -> {
|
||||
System.out.println(String.format("Dialog message: %s", dialog.message()));
|
||||
dialog.dismiss();
|
||||
});`);
|
||||
}
|
||||
|
||||
const actionCall = this._generateActionCall(action);
|
||||
let code = `${subject}.${actionCall};`;
|
||||
|
||||
if (signals.popup) {
|
||||
code = `Page ${signals.popup.popupAlias} = ${pageAlias}.waitForPopup(() -> {
|
||||
${code}
|
||||
});`;
|
||||
}
|
||||
|
||||
if (signals.download) {
|
||||
code = `Download download = ${pageAlias}.waitForDownload(() -> {
|
||||
${code}
|
||||
});`;
|
||||
}
|
||||
|
||||
if (signals.waitForNavigation) {
|
||||
code = `
|
||||
// ${pageAlias}.waitForNavigation(new Page.WaitForNavigationOptions().withUrl(${quote(signals.waitForNavigation.url)}), () ->
|
||||
${pageAlias}.waitForNavigation(() -> {
|
||||
${code}
|
||||
});`;
|
||||
}
|
||||
|
||||
formatter.add(code);
|
||||
|
||||
if (signals.assertNavigation)
|
||||
formatter.add(`// assert ${pageAlias}.url().equals(${quote(signals.assertNavigation.url)});`);
|
||||
return formatter.format();
|
||||
}
|
||||
|
||||
private _generateActionCall(action: Action): string {
|
||||
switch (action.name) {
|
||||
case 'openPage':
|
||||
throw Error('Not reached');
|
||||
case 'closePage':
|
||||
return 'close()';
|
||||
case 'click': {
|
||||
let method = 'click';
|
||||
if (action.clickCount === 2)
|
||||
method = 'dblclick';
|
||||
return `${method}(${quote(action.selector)})`;
|
||||
}
|
||||
case 'check':
|
||||
return `check(${quote(action.selector)})`;
|
||||
case 'uncheck':
|
||||
return `uncheck(${quote(action.selector)})`;
|
||||
case 'fill':
|
||||
return `fill(${quote(action.selector)}, ${quote(action.text)})`;
|
||||
case 'setInputFiles':
|
||||
return `setInputFiles(${quote(action.selector)}, ${formatPath(action.files.length === 1 ? action.files[0] : action.files)})`;
|
||||
case 'press': {
|
||||
const modifiers = toModifiers(action.modifiers);
|
||||
const shortcut = [...modifiers, action.key].join('+');
|
||||
return `press(${quote(action.selector)}, ${quote(shortcut)})`;
|
||||
}
|
||||
case 'navigate':
|
||||
return `navigate(${quote(action.url)})`;
|
||||
case 'select':
|
||||
return `selectOption(${quote(action.selector)}, ${formatSelectOption(action.options.length > 1 ? action.options : action.options[0])})`;
|
||||
}
|
||||
}
|
||||
|
||||
generateHeader(options: LanguageGeneratorOptions): string {
|
||||
const formatter = new JavaScriptFormatter();
|
||||
formatter.add(`
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.*;
|
||||
|
||||
public class Example {
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.${options.browserName}().launch(${formatLaunchOptions(options.launchOptions)});
|
||||
BrowserContext context = browser.newContext(${formatContextOptions(options.contextOptions, options.deviceName)});`);
|
||||
return formatter.format();
|
||||
}
|
||||
|
||||
generateFooter(saveStorage: string | undefined): string {
|
||||
const storageStateLine = saveStorage ? `\n context.storageState(new BrowserContext.StorageStateOptions().withPath(${quote(saveStorage)}));` : '';
|
||||
return `\n // ---------------------${storageStateLine}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
}
|
||||
}
|
||||
|
||||
function formatPath(files: string | string[]): string {
|
||||
if (Array.isArray(files)) {
|
||||
if (files.length === 0)
|
||||
return 'new Path[0]';
|
||||
return `new Path[] {${files.map(s => 'Paths.get(' + quote(s) + ')').join(', ')}}`;
|
||||
}
|
||||
return `Paths.get(${quote(files)})`;
|
||||
}
|
||||
|
||||
function formatSelectOption(options: string | string[]): string {
|
||||
if (Array.isArray(options)) {
|
||||
if (options.length === 0)
|
||||
return 'new String[0]';
|
||||
return `new String[] {${options.map(s => quote(s)).join(', ')}}`;
|
||||
}
|
||||
return quote(options);
|
||||
}
|
||||
|
||||
function formatLaunchOptions(options: any): string {
|
||||
const lines = [];
|
||||
if (!Object.keys(options).length)
|
||||
return '';
|
||||
lines.push('new BrowserType.LaunchOptions()');
|
||||
if (typeof options.headless === 'boolean')
|
||||
lines.push(` .withHeadless(false)`);
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function formatContextOptions(contextOptions: BrowserContextOptions, deviceName: string | undefined): string {
|
||||
const lines = [];
|
||||
if (!Object.keys(contextOptions).length && !deviceName)
|
||||
return '';
|
||||
const device = deviceName ? deviceDescriptors[deviceName] : {};
|
||||
const options: BrowserContextOptions = { ...device, ...contextOptions };
|
||||
lines.push('new Browser.NewContextOptions()');
|
||||
if (options.colorScheme)
|
||||
lines.push(` .withColorScheme(ColorScheme.${options.colorScheme.toUpperCase()})`);
|
||||
if (options.geolocation)
|
||||
lines.push(` .withGeolocation(${options.geolocation.latitude}, ${options.geolocation.longitude})`);
|
||||
if (options.locale)
|
||||
lines.push(` .withLocale("${options.locale}")`);
|
||||
if (options.proxy)
|
||||
lines.push(` .withProxy(new Proxy("${options.proxy.server}"))`);
|
||||
if (options.timezoneId)
|
||||
lines.push(` .withTimezoneId("${options.timezoneId}")`);
|
||||
if (options.userAgent)
|
||||
lines.push(` .withUserAgent("${options.userAgent}")`);
|
||||
if (options.viewport)
|
||||
lines.push(` .withViewportSize(${options.viewport.width}, ${options.viewport.height})`);
|
||||
if (options.deviceScaleFactor)
|
||||
lines.push(` .withDeviceScaleFactor(${options.deviceScaleFactor})`);
|
||||
if (options.isMobile)
|
||||
lines.push(` .withIsMobile(${options.isMobile})`);
|
||||
if (options.hasTouch)
|
||||
lines.push(` .withHasTouch(${options.hasTouch})`);
|
||||
if (options.storageState)
|
||||
lines.push(` .withStorageStatePath(Paths.get(${quote(options.storageState as string)}))`);
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function quote(text: string, char: string = '\"') {
|
||||
if (char === '\'')
|
||||
return char + text.replace(/[']/g, '\\\'') + char;
|
||||
if (char === '"')
|
||||
return char + text.replace(/["]/g, '\\"') + char;
|
||||
if (char === '`')
|
||||
return char + text.replace(/[`]/g, '\\`') + char;
|
||||
throw new Error('Invalid escape char');
|
||||
}
|
@ -193,7 +193,7 @@ function formatContextOptions(options: BrowserContextOptions, deviceName: string
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
class JavaScriptFormatter {
|
||||
export class JavaScriptFormatter {
|
||||
private _baseIndent: string;
|
||||
private _baseOffset: string;
|
||||
private _lines: string[] = [];
|
||||
@ -224,10 +224,11 @@ class JavaScriptFormatter {
|
||||
if (line.startsWith('}') || line.startsWith(']'))
|
||||
spaces = spaces.substring(this._baseIndent.length);
|
||||
|
||||
const extraSpaces = /^(for|while|if).*\(.*\)$/.test(previousLine) ? this._baseIndent : '';
|
||||
const extraSpaces = /^(for|while|if|try).*\(.*\)$/.test(previousLine) ? this._baseIndent : '';
|
||||
previousLine = line;
|
||||
|
||||
line = spaces + extraSpaces + line;
|
||||
const callCarryOver = line.startsWith('.with');
|
||||
line = spaces + extraSpaces + (callCarryOver ? this._baseIndent : '') + line;
|
||||
if (line.endsWith('{') || line.endsWith('['))
|
||||
spaces += this._baseIndent;
|
||||
return this._baseOffset + line;
|
||||
|
@ -22,6 +22,7 @@ import { describeFrame, toClickOptions, toModifiers } from './recorder/utils';
|
||||
import { Page } from '../page';
|
||||
import { Frame } from '../frames';
|
||||
import { BrowserContext } from '../browserContext';
|
||||
import { JavaLanguageGenerator } from './recorder/java';
|
||||
import { JavaScriptLanguageGenerator } from './recorder/javascript';
|
||||
import { CSharpLanguageGenerator } from './recorder/csharp';
|
||||
import { PythonLanguageGenerator } from './recorder/python';
|
||||
@ -81,6 +82,7 @@ export class RecorderSupplement {
|
||||
const language = params.language || context._options.sdkLanguage;
|
||||
|
||||
const languages = new Set([
|
||||
new JavaLanguageGenerator(),
|
||||
new JavaScriptLanguageGenerator(),
|
||||
new PythonLanguageGenerator(false),
|
||||
new PythonLanguageGenerator(true),
|
||||
|
@ -3,5 +3,6 @@ var hljs = require('./core');
|
||||
hljs.registerLanguage('javascript', require('./languages/javascript'));
|
||||
hljs.registerLanguage('python', require('./languages/python'));
|
||||
hljs.registerLanguage('csharp', require('./languages/csharp'));
|
||||
hljs.registerLanguage('java', require('./languages/java'));
|
||||
|
||||
module.exports = hljs;
|
176
src/third_party/highlightjs/highlightjs/languages/java.js
vendored
Normal file
176
src/third_party/highlightjs/highlightjs/languages/java.js
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
// https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10
|
||||
var decimalDigits = '[0-9](_*[0-9])*';
|
||||
var frac = `\\.(${decimalDigits})`;
|
||||
var hexDigits = '[0-9a-fA-F](_*[0-9a-fA-F])*';
|
||||
var NUMERIC = {
|
||||
className: 'number',
|
||||
variants: [
|
||||
// DecimalFloatingPointLiteral
|
||||
// including ExponentPart
|
||||
{ begin: `(\\b(${decimalDigits})((${frac})|\\.)?|(${frac}))` +
|
||||
`[eE][+-]?(${decimalDigits})[fFdD]?\\b` },
|
||||
// excluding ExponentPart
|
||||
{ begin: `\\b(${decimalDigits})((${frac})[fFdD]?\\b|\\.([fFdD]\\b)?)` },
|
||||
{ begin: `(${frac})[fFdD]?\\b` },
|
||||
{ begin: `\\b(${decimalDigits})[fFdD]\\b` },
|
||||
|
||||
// HexadecimalFloatingPointLiteral
|
||||
{ begin: `\\b0[xX]((${hexDigits})\\.?|(${hexDigits})?\\.(${hexDigits}))` +
|
||||
`[pP][+-]?(${decimalDigits})[fFdD]?\\b` },
|
||||
|
||||
// DecimalIntegerLiteral
|
||||
{ begin: '\\b(0|[1-9](_*[0-9])*)[lL]?\\b' },
|
||||
|
||||
// HexIntegerLiteral
|
||||
{ begin: `\\b0[xX](${hexDigits})[lL]?\\b` },
|
||||
|
||||
// OctalIntegerLiteral
|
||||
{ begin: '\\b0(_*[0-7])*[lL]?\\b' },
|
||||
|
||||
// BinaryIntegerLiteral
|
||||
{ begin: '\\b0[bB][01](_*[01])*[lL]?\\b' },
|
||||
],
|
||||
relevance: 0
|
||||
};
|
||||
|
||||
/*
|
||||
Language: Java
|
||||
Author: Vsevolod Solovyov <vsevolod.solovyov@gmail.com>
|
||||
Category: common, enterprise
|
||||
Website: https://www.java.com/
|
||||
*/
|
||||
|
||||
function java(hljs) {
|
||||
var JAVA_IDENT_RE = '[\u00C0-\u02B8a-zA-Z_$][\u00C0-\u02B8a-zA-Z_$0-9]*';
|
||||
var GENERIC_IDENT_RE = JAVA_IDENT_RE + '(<' + JAVA_IDENT_RE + '(\\s*,\\s*' + JAVA_IDENT_RE + ')*>)?';
|
||||
var KEYWORDS = 'false synchronized int abstract float private char boolean var static null if const ' +
|
||||
'for true while long strictfp finally protected import native final void ' +
|
||||
'enum else break transient catch instanceof byte super volatile case assert short ' +
|
||||
'package default double public try this switch continue throws protected public private ' +
|
||||
'module requires exports do';
|
||||
|
||||
var ANNOTATION = {
|
||||
className: 'meta',
|
||||
begin: '@' + JAVA_IDENT_RE,
|
||||
contains: [
|
||||
{
|
||||
begin: /\(/,
|
||||
end: /\)/,
|
||||
contains: ["self"] // allow nested () inside our annotation
|
||||
},
|
||||
]
|
||||
};
|
||||
const NUMBER = NUMERIC;
|
||||
|
||||
return {
|
||||
name: 'Java',
|
||||
aliases: ['jsp'],
|
||||
keywords: KEYWORDS,
|
||||
illegal: /<\/|#/,
|
||||
contains: [
|
||||
hljs.COMMENT(
|
||||
'/\\*\\*',
|
||||
'\\*/',
|
||||
{
|
||||
relevance: 0,
|
||||
contains: [
|
||||
{
|
||||
// eat up @'s in emails to prevent them to be recognized as doctags
|
||||
begin: /\w+@/, relevance: 0
|
||||
},
|
||||
{
|
||||
className: 'doctag',
|
||||
begin: '@[A-Za-z]+'
|
||||
}
|
||||
]
|
||||
}
|
||||
),
|
||||
// relevance boost
|
||||
{
|
||||
begin: /import java\.[a-z]+\./,
|
||||
keywords: "import",
|
||||
relevance: 2
|
||||
},
|
||||
hljs.C_LINE_COMMENT_MODE,
|
||||
hljs.C_BLOCK_COMMENT_MODE,
|
||||
hljs.APOS_STRING_MODE,
|
||||
hljs.QUOTE_STRING_MODE,
|
||||
{
|
||||
className: 'class',
|
||||
beginKeywords: 'class interface enum', end: /[{;=]/, excludeEnd: true,
|
||||
keywords: 'class interface enum',
|
||||
illegal: /[:"\[\]]/,
|
||||
contains: [
|
||||
{ beginKeywords: 'extends implements' },
|
||||
hljs.UNDERSCORE_TITLE_MODE
|
||||
]
|
||||
},
|
||||
{
|
||||
// Expression keywords prevent 'keyword Name(...)' from being
|
||||
// recognized as a function definition
|
||||
beginKeywords: 'new throw return else',
|
||||
relevance: 0
|
||||
},
|
||||
{
|
||||
className: 'class',
|
||||
begin: 'record\\s+' + hljs.UNDERSCORE_IDENT_RE + '\\s*\\(',
|
||||
returnBegin: true,
|
||||
excludeEnd: true,
|
||||
end: /[{;=]/,
|
||||
keywords: KEYWORDS,
|
||||
contains: [
|
||||
{ beginKeywords: "record" },
|
||||
{
|
||||
begin: hljs.UNDERSCORE_IDENT_RE + '\\s*\\(',
|
||||
returnBegin: true,
|
||||
relevance: 0,
|
||||
contains: [hljs.UNDERSCORE_TITLE_MODE]
|
||||
},
|
||||
{
|
||||
className: 'params',
|
||||
begin: /\(/, end: /\)/,
|
||||
keywords: KEYWORDS,
|
||||
relevance: 0,
|
||||
contains: [
|
||||
hljs.C_BLOCK_COMMENT_MODE
|
||||
]
|
||||
},
|
||||
hljs.C_LINE_COMMENT_MODE,
|
||||
hljs.C_BLOCK_COMMENT_MODE
|
||||
]
|
||||
},
|
||||
{
|
||||
className: 'function',
|
||||
begin: '(' + GENERIC_IDENT_RE + '\\s+)+' + hljs.UNDERSCORE_IDENT_RE + '\\s*\\(', returnBegin: true, end: /[{;=]/,
|
||||
excludeEnd: true,
|
||||
keywords: KEYWORDS,
|
||||
contains: [
|
||||
{
|
||||
begin: hljs.UNDERSCORE_IDENT_RE + '\\s*\\(', returnBegin: true,
|
||||
relevance: 0,
|
||||
contains: [hljs.UNDERSCORE_TITLE_MODE]
|
||||
},
|
||||
{
|
||||
className: 'params',
|
||||
begin: /\(/, end: /\)/,
|
||||
keywords: KEYWORDS,
|
||||
relevance: 0,
|
||||
contains: [
|
||||
ANNOTATION,
|
||||
hljs.APOS_STRING_MODE,
|
||||
hljs.QUOTE_STRING_MODE,
|
||||
NUMBER,
|
||||
hljs.C_BLOCK_COMMENT_MODE
|
||||
]
|
||||
},
|
||||
hljs.C_LINE_COMMENT_MODE,
|
||||
hljs.C_BLOCK_COMMENT_MODE
|
||||
]
|
||||
},
|
||||
NUMBER,
|
||||
ANNOTATION
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = java;
|
2
src/third_party/highlightjs/roll.sh
vendored
2
src/third_party/highlightjs/roll.sh
vendored
@ -5,7 +5,7 @@ set +x
|
||||
# Pick a stable release revision from here:
|
||||
# https://github.com/highlightjs/highlight.js/releases
|
||||
RELEASE_REVISION="af20048d5c601d6e30016d8171317bfdf8a6c242"
|
||||
LANGUAGES="javascript python csharp"
|
||||
LANGUAGES="javascript python csharp java"
|
||||
STYLES="tomorrow.css"
|
||||
|
||||
trap "cd $(pwd -P)" EXIT
|
||||
|
@ -47,6 +47,10 @@ describe('cli codegen', (suite, { browserName, headful, mode }) => {
|
||||
# Click text=Submit
|
||||
await page.click("text=Submit")`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Click text=Submit
|
||||
page.click("text=Submit");`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Click text=Submit
|
||||
await page.ClickAsync("text=Submit");`);
|
||||
@ -113,6 +117,10 @@ await page.ClickAsync("text=Submit");`);
|
||||
# Click text=Submit
|
||||
await page.click("text=Submit")`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Click text=Submit
|
||||
page.click("text=Submit");`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Click text=Submit
|
||||
await page.ClickAsync("text=Submit");`);
|
||||
@ -166,6 +174,9 @@ await page.ClickAsync("text=Submit");`);
|
||||
expect(sources.get('<javascript>').text).toContain(`
|
||||
// Fill input[name="name"]
|
||||
await page.fill('input[name="name"]', 'John');`);
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Fill input[name="name"]
|
||||
page.fill("input[name=\\\"name\\\"]", "John");`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Fill input[name="name"]
|
||||
@ -216,6 +227,10 @@ await page.FillAsync(\"input[name=\\\"name\\\"]\", \"John\");`);
|
||||
// Press Enter with modifiers
|
||||
await page.press('input[name="name"]', 'Shift+Enter');`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Press Enter with modifiers
|
||||
page.press("input[name=\\\"name\\\"]", "Shift+Enter");`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Press Enter with modifiers
|
||||
page.press(\"input[name=\\\"name\\\"]\", \"Shift+Enter\")`);
|
||||
@ -321,6 +336,10 @@ await page.PressAsync(\"input[name=\\\"name\\\"]\", \"Shift+Enter\");`);
|
||||
// Check input[name="accept"]
|
||||
await page.check('input[name="accept"]');`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Check input[name="accept"]
|
||||
page.check("input[name=\\\"accept\\\"]");`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Check input[name="accept"]
|
||||
page.check(\"input[name=\\\"accept\\\"]\")`);
|
||||
@ -370,6 +389,10 @@ await page.CheckAsync(\"input[name=\\\"accept\\\"]\");`);
|
||||
// Uncheck input[name="accept"]
|
||||
await page.uncheck('input[name="accept"]');`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Uncheck input[name="accept"]
|
||||
page.uncheck("input[name=\\\"accept\\\"]");`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Uncheck input[name="accept"]
|
||||
page.uncheck(\"input[name=\\\"accept\\\"]\")`);
|
||||
@ -401,6 +424,10 @@ await page.UncheckAsync(\"input[name=\\\"accept\\\"]\");`);
|
||||
// Select 2
|
||||
await page.selectOption('select', '2');`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Select 2
|
||||
page.selectOption("select", "2");`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Select 2
|
||||
page.select_option(\"select\", \"2\")`);
|
||||
@ -437,6 +464,12 @@ await page.SelectOptionAsync(\"select\", \"2\");`);
|
||||
page.click('text=link')
|
||||
]);`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Click text=link
|
||||
Page page1 = page.waitForPopup(() -> {
|
||||
page.click("text=link");
|
||||
});`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Click text=link
|
||||
with page.expect_popup() as popup_info:
|
||||
@ -474,6 +507,11 @@ await Task.WhenAll(
|
||||
await page.click('text=link');
|
||||
// assert.equal(page.url(), 'about:blank#foo');`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Click text=link
|
||||
page.click("text=link");
|
||||
// assert page.url().equals("about:blank#foo");`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Click text=link
|
||||
page.click(\"text=link\")
|
||||
@ -512,6 +550,13 @@ await page.ClickAsync(\"text=link\");
|
||||
page.click('text=link')
|
||||
]);`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Click text=link
|
||||
// page.waitForNavigation(new Page.WaitForNavigationOptions().withUrl("about:blank#foo"), () ->
|
||||
page.waitForNavigation(() -> {
|
||||
page.click("text=link");
|
||||
});`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Click text=link
|
||||
# with page.expect_navigation(url=\"about:blank#foo\"):
|
||||
|
@ -31,6 +31,10 @@ describe('cli codegen', (suite, { mode }) => {
|
||||
// Open new page
|
||||
const page = await context.newPage();`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Open new page
|
||||
Page page = context.newPage();`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Open new page
|
||||
page = context.new_page()`);
|
||||
@ -53,6 +57,10 @@ var page = await context.NewPageAsync();`);
|
||||
// Open new page
|
||||
const page1 = await context.newPage();`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Open new page
|
||||
Page page1 = context.newPage();`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Open new page
|
||||
page1 = context.new_page()`);
|
||||
@ -75,6 +83,9 @@ var page1 = await context.NewPageAsync();`);
|
||||
expect(sources.get('<javascript>').text).toContain(`
|
||||
await page.close();`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
page.close();`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
page.close()`);
|
||||
|
||||
@ -117,6 +128,10 @@ await page.CloseAsync();`);
|
||||
// Upload file-to-upload.txt
|
||||
await page.setInputFiles('input[type="file"]', 'file-to-upload.txt');`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Upload file-to-upload.txt
|
||||
page.setInputFiles("input[type=\\\"file\\\"]", Paths.get("file-to-upload.txt"));`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Upload file-to-upload.txt
|
||||
page.set_input_files(\"input[type=\\\"file\\\"]\", \"file-to-upload.txt\")`);
|
||||
@ -150,6 +165,10 @@ await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", \"file-to-upload.txt
|
||||
// Upload file-to-upload.txt, file-to-upload-2.txt
|
||||
await page.setInputFiles('input[type=\"file\"]', ['file-to-upload.txt', 'file-to-upload-2.txt']);`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Upload file-to-upload.txt, file-to-upload-2.txt
|
||||
page.setInputFiles("input[type=\\\"file\\\"]", new Path[] {Paths.get("file-to-upload.txt"), Paths.get("file-to-upload-2.txt")});`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Upload file-to-upload.txt, file-to-upload-2.txt
|
||||
page.set_input_files(\"input[type=\\\"file\\\"]\", [\"file-to-upload.txt\", \"file-to-upload-2.txt\"]`);
|
||||
@ -182,6 +201,10 @@ await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", new[] { \"file-to-up
|
||||
// Clear selected files
|
||||
await page.setInputFiles('input[type=\"file\"]', []);`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Clear selected files
|
||||
page.setInputFiles("input[type=\\\"file\\\"]", new Path[0]);`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Clear selected files
|
||||
page.set_input_files(\"input[type=\\\"file\\\"]\", []`);
|
||||
@ -225,6 +248,12 @@ await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", new[] { });`);
|
||||
page.click('text=Download')
|
||||
]);`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Click text=Download
|
||||
Download download = page.waitForDownload(() -> {
|
||||
page.click("text=Download");
|
||||
});`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Click text=Download
|
||||
with page.expect_download() as download_info:
|
||||
@ -265,6 +294,14 @@ await Task.WhenAll(
|
||||
});
|
||||
await page.click('text=click me');`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Click text=click me
|
||||
page.onceDialog(dialog -> {
|
||||
System.out.println(String.format("Dialog message: %s", dialog.message()));
|
||||
dialog.dismiss();
|
||||
});
|
||||
page.click("text=click me");`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Click text=click me
|
||||
page.once(\"dialog\", lambda dialog: dialog.dismiss())
|
||||
@ -359,6 +396,9 @@ await page.ClickAsync(\"text=click me\");`);
|
||||
expect(sources.get('<javascript>').text).toContain(`await page1.fill('input', 'TextA');`);
|
||||
expect(sources.get('<javascript>').text).toContain(`await page2.fill('input', 'TextB');`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`page1.fill("input", "TextA");`);
|
||||
expect(sources.get('<java>').text).toContain(`page2.fill("input", "TextB");`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`page1.fill(\"input\", \"TextA\")`);
|
||||
expect(sources.get('<python>').text).toContain(`page2.fill(\"input\", \"TextB\")`);
|
||||
|
||||
@ -438,6 +478,10 @@ await page.ClickAsync(\"text=click me\");`);
|
||||
name: 'one'
|
||||
}).click('text=Hi, I\\'m frame');`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Click text=Hi, I'm frame
|
||||
page.frame("one").click("text=Hi, I'm frame");`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Click text=Hi, I'm frame
|
||||
page.frame(name=\"one\").click(\"text=Hi, I'm frame\")`);
|
||||
@ -461,6 +505,10 @@ await page.GetFrame(name: \"one\").ClickAsync(\"text=Hi, I'm frame\");`);
|
||||
name: 'two'
|
||||
}).click('text=Hi, I\\'m frame');`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Click text=Hi, I'm frame
|
||||
page.frame("two").click("text=Hi, I'm frame");`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Click text=Hi, I'm frame
|
||||
page.frame(name=\"two\").click(\"text=Hi, I'm frame\")`);
|
||||
@ -484,6 +532,10 @@ await page.GetFrame(name: \"two\").ClickAsync(\"text=Hi, I'm frame\");`);
|
||||
url: 'http://localhost:${server.PORT}/frames/frame.html'
|
||||
}).click('text=Hi, I\\'m frame');`);
|
||||
|
||||
expect(sources.get('<java>').text).toContain(`
|
||||
// Click text=Hi, I'm frame
|
||||
page.frameByUrl("http://localhost:${server.PORT}/frames/frame.html").click("text=Hi, I'm frame");`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
# Click text=Hi, I'm frame
|
||||
page.frame(url=\"http://localhost:${server.PORT}/frames/frame.html\").click(\"text=Hi, I'm frame\")`);
|
||||
|
@ -43,7 +43,6 @@ it('should print the correct context options for custom settings', async ({ brow
|
||||
'--lang=es',
|
||||
'--proxy-server=http://myproxy:3128',
|
||||
'--timezone=Europe/Rome',
|
||||
'--timeout=1000',
|
||||
'--user-agent=hardkodemium',
|
||||
'--viewport-size=1280,720',
|
||||
'codegen',
|
||||
@ -95,7 +94,6 @@ it('should print the correct context options when using a device and additional
|
||||
'--lang=es',
|
||||
'--proxy-server=http://myproxy:3128',
|
||||
'--timezone=Europe/Rome',
|
||||
'--timeout=1000',
|
||||
'--user-agent=hardkodemium',
|
||||
'--viewport-size=1280,720',
|
||||
'codegen',
|
||||
|
86
test/cli/cli-codegen-java.spec.ts
Normal file
86
test/cli/cli-codegen-java.spec.ts
Normal file
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* 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 fs from 'fs';
|
||||
import path from 'path';
|
||||
import { folio } from './cli.fixtures';
|
||||
|
||||
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 ({ runCLI, browserName }) => {
|
||||
const cli = runCLI(['codegen', '--target=java', emptyHTML]);
|
||||
const expectedResult = `import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.*;
|
||||
|
||||
public class Example {
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.${browserName}().launch(new BrowserType.LaunchOptions()
|
||||
.withHeadless(false));
|
||||
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(['--color-scheme=light', 'codegen', '--target=java', emptyHTML]);
|
||||
const expectedResult = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.withColorScheme(ColorScheme.LIGHT));`;
|
||||
await cli.waitFor(expectedResult);
|
||||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options when using a device', async ({ runCLI }) => {
|
||||
const cli = runCLI(['--device=Pixel 2', 'codegen', '--target=java', emptyHTML]);
|
||||
const expectedResult = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.withUserAgent("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")
|
||||
.withViewportSize(411, 731)
|
||||
.withDeviceScaleFactor(2.625)
|
||||
.withIsMobile(true)
|
||||
.withHasTouch(true));`;
|
||||
await cli.waitFor(expectedResult);
|
||||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('should print the correct context options when using a device and additional options', async ({ runCLI }) => {
|
||||
const cli = runCLI(['--color-scheme=light', '--device=iPhone 11', 'codegen', '--target=java', emptyHTML]);
|
||||
const expectedResult = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.withColorScheme(ColorScheme.LIGHT)
|
||||
.withUserAgent("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")
|
||||
.withViewportSize(414, 896)
|
||||
.withDeviceScaleFactor(2)
|
||||
.withIsMobile(true)
|
||||
.withHasTouch(true));`;
|
||||
await cli.waitFor(expectedResult);
|
||||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
||||
it('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');
|
||||
const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, 'codegen', '--target=java', emptyHTML]);
|
||||
const expectedResult1 = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.withStorageStatePath(Paths.get("${loadFileName}")));`;
|
||||
await cli.waitFor(expectedResult1);
|
||||
|
||||
const expectedResult2 = `
|
||||
// ---------------------
|
||||
context.storageState(new BrowserContext.StorageStateOptions().withPath("${saveFileName}"))`;
|
||||
await cli.waitFor(expectedResult2);
|
||||
});
|
Loading…
Reference in New Issue
Block a user