mirror of
https://github.com/microsoft/playwright.git
synced 2026-09-14 14:08:10 +08:00
311 lines
12 KiB
TypeScript
311 lines
12 KiB
TypeScript
/**
|
|
* 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 { asLocator } from '../locatorGenerators';
|
|
import { escapeWithQuotes } from '../stringUtils';
|
|
import { expectSignalAction, toClickOptionsForSourceCode, toKeyboardModifiers, toSignalMap } from './language';
|
|
import { deviceDescriptors } from '../deviceDescriptors';
|
|
import { JavaScriptFormatter } from './javascript';
|
|
|
|
import type { BrowserContextOptions } from 'playwright-core';
|
|
import type * as types from './types';
|
|
import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types';
|
|
import type * as actions from './actions';
|
|
|
|
type JavaLanguageMode = 'library' | 'junit';
|
|
|
|
export class JavaLanguageGenerator implements LanguageGenerator {
|
|
id: string;
|
|
groupName = 'Java';
|
|
name: string;
|
|
highlighter = 'java' as Language;
|
|
_mode: JavaLanguageMode;
|
|
private _pageAliases = new Map<string, string>();
|
|
|
|
constructor(mode: JavaLanguageMode) {
|
|
if (mode === 'library') {
|
|
this.name = 'Library';
|
|
this.id = 'java';
|
|
} else if (mode === 'junit') {
|
|
this.name = 'JUnit';
|
|
this.id = 'java-junit';
|
|
} else {
|
|
throw new Error(`Unknown Java language mode: ${mode}`);
|
|
}
|
|
this._mode = mode;
|
|
}
|
|
|
|
reset() {
|
|
this._pageAliases.clear();
|
|
}
|
|
|
|
private _pageAlias(pageGuid: string): string {
|
|
let alias = this._pageAliases.get(pageGuid);
|
|
if (!alias) {
|
|
alias = 'page' + (this._pageAliases.size || '');
|
|
this._pageAliases.set(pageGuid, alias);
|
|
}
|
|
return alias;
|
|
}
|
|
|
|
generateAction(actionInContext: actions.ActionInContext, options: LanguageGeneratorOptions): string {
|
|
const action = actionInContext.action;
|
|
// Resolve before the early return, so that pages are named in the order they are opened.
|
|
const pageAlias = this._pageAlias(actionInContext.pageGuid);
|
|
const offset = this._mode === 'junit' ? 4 : 6;
|
|
const formatter = new JavaScriptFormatter(offset);
|
|
|
|
if (this._mode !== 'library' && (action.name === 'openPage' || action.name === 'closePage'))
|
|
return '';
|
|
|
|
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(${quote(action.url)});`);
|
|
return formatter.format();
|
|
}
|
|
|
|
const subject = pageAlias;
|
|
const signals = toSignalMap(actionInContext);
|
|
|
|
if (signals.dialog) {
|
|
formatter.add(` ${pageAlias}.onceDialog(dialog -> {
|
|
System.out.println(String.format("Dialog message: %s", dialog.message()));
|
|
dialog.dismiss();
|
|
});`);
|
|
}
|
|
|
|
let code = this._generateActionCall(subject, actionInContext);
|
|
|
|
if (signals.popup) {
|
|
const popupAlias = this._pageAlias(signals.popup.popupPageGuid);
|
|
code = `Page ${popupAlias} = ${pageAlias}.waitForPopup(() -> {
|
|
${code}
|
|
});`;
|
|
}
|
|
|
|
if (signals.download) {
|
|
code = `Download download${signals.download.downloadAlias} = ${pageAlias}.waitForDownload(() -> {
|
|
${code}
|
|
});`;
|
|
}
|
|
|
|
formatter.add(code);
|
|
|
|
if (options.generateExpectSignal && signals.expect)
|
|
formatter.add(this.generateAction(expectSignalAction(actionInContext, signals.expect), options));
|
|
|
|
return formatter.format();
|
|
}
|
|
|
|
private _generateActionCall(subject: string, actionInContext: actions.ActionInContext): string {
|
|
const action = actionInContext.action;
|
|
switch (action.name) {
|
|
case 'openPage':
|
|
throw Error('Not reached');
|
|
case 'closePage':
|
|
return `${subject}.close();`;
|
|
case 'click': {
|
|
let method = 'click';
|
|
if (action.clickCount === 2)
|
|
method = 'dblclick';
|
|
const options = toClickOptionsForSourceCode(action);
|
|
const optionsText = formatClickOptions(options);
|
|
return `${subject}.${this._asLocator(action.selector)}.${method}(${optionsText});`;
|
|
}
|
|
case 'hover': {
|
|
const optionsText = action.position ? `new Locator.HoverOptions().setPosition(${action.position.x}, ${action.position.y})` : '';
|
|
return `${subject}.${this._asLocator(action.selector)}.hover(${optionsText});`;
|
|
}
|
|
case 'check':
|
|
return `${subject}.${this._asLocator(action.selector)}.check();`;
|
|
case 'uncheck':
|
|
return `${subject}.${this._asLocator(action.selector)}.uncheck();`;
|
|
case 'fill':
|
|
return `${subject}.${this._asLocator(action.selector)}.fill(${quote(action.text)});`;
|
|
case 'setInputFiles':
|
|
return `${subject}.${this._asLocator(action.selector)}.setInputFiles(${formatPath(action.files.length === 1 ? action.files[0] : action.files)});`;
|
|
case 'press': {
|
|
const modifiers = toKeyboardModifiers(action.modifiers);
|
|
const shortcut = [...modifiers, action.key].join('+');
|
|
return `${subject}.${this._asLocator(action.selector)}.press(${quote(shortcut)});`;
|
|
}
|
|
case 'navigate':
|
|
return `${subject}.navigate(${quote(action.url)});`;
|
|
case 'select':
|
|
return `${subject}.${this._asLocator(action.selector)}.selectOption(${formatSelectOption(action.options.length === 1 ? action.options[0] : action.options)});`;
|
|
case 'assertText':
|
|
return `assertThat(${subject}.${this._asLocator(action.selector)}).${action.substring ? 'containsText' : 'hasText'}(${quote(action.text)});`;
|
|
case 'assertChecked':
|
|
return `assertThat(${subject}.${this._asLocator(action.selector)})${action.checked ? '' : '.not()'}.isChecked();`;
|
|
case 'assertVisible':
|
|
return `assertThat(${subject}.${this._asLocator(action.selector)}).isVisible();`;
|
|
case 'assertValue': {
|
|
const assertion = action.value ? `hasValue(${quote(action.value)})` : `isEmpty()`;
|
|
return `assertThat(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
|
}
|
|
case 'assertSnapshot':
|
|
return `assertThat(${subject}.${this._asLocator(action.selector)}).matchesAriaSnapshot(${quote(action.ariaSnapshot)});`;
|
|
}
|
|
}
|
|
|
|
private _asLocator(selector: string) {
|
|
return asLocator('java', selector);
|
|
}
|
|
|
|
generateHeader(options: LanguageGeneratorOptions): string {
|
|
const formatter = new JavaScriptFormatter();
|
|
if (this._mode === 'junit') {
|
|
formatter.add(`
|
|
import com.microsoft.playwright.junit.UsePlaywright;
|
|
import com.microsoft.playwright.Page;
|
|
import com.microsoft.playwright.options.*;
|
|
|
|
${options.contextOptions.recordHar ? `import java.nio.file.Paths;\n` : ''}import org.junit.jupiter.api.*;
|
|
import static com.microsoft.playwright.assertions.PlaywrightAssertions.*;
|
|
|
|
@UsePlaywright
|
|
public class TestExample {
|
|
@Test
|
|
void test(Page page) {`);
|
|
if (options.contextOptions.recordHar) {
|
|
const url = options.contextOptions.recordHar.urlFilter;
|
|
const recordHarOptions = typeof url === 'string' ? `, new Page.RouteFromHAROptions()
|
|
.setUrl(${quote(url)})` : '';
|
|
formatter.add(` page.routeFromHAR(Paths.get(${quote(options.contextOptions.recordHar.path)})${recordHarOptions});`);
|
|
}
|
|
return formatter.format();
|
|
}
|
|
formatter.add(`
|
|
import com.microsoft.playwright.*;
|
|
import com.microsoft.playwright.options.*;
|
|
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
|
${options.contextOptions.recordHar ? `import java.nio.file.Paths;\n` : ''}import java.util.*;
|
|
|
|
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)});`);
|
|
if (options.contextOptions.recordHar) {
|
|
const url = options.contextOptions.recordHar.urlFilter;
|
|
const recordHarOptions = typeof url === 'string' ? `, new BrowserContext.RouteFromHAROptions()
|
|
.setUrl(${quote(url)})` : '';
|
|
formatter.add(` context.routeFromHAR(Paths.get(${quote(options.contextOptions.recordHar.path)})${recordHarOptions});`);
|
|
}
|
|
return formatter.format();
|
|
}
|
|
|
|
generateFooter(saveStorage: string | undefined): string {
|
|
const storageStateLine = saveStorage ? `\n context.storageState(new BrowserContext.StorageStateOptions().setPath(${quote(saveStorage)}));\n` : '';
|
|
if (this._mode === 'junit') {
|
|
return `${storageStateLine} }
|
|
}`;
|
|
}
|
|
return `${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).filter(key => options[key] !== undefined).length)
|
|
return '';
|
|
lines.push('new BrowserType.LaunchOptions()');
|
|
if (options.channel)
|
|
lines.push(` .setChannel(${quote(options.channel)})`);
|
|
if (typeof options.headless === 'boolean')
|
|
lines.push(` .setHeadless(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.acceptDownloads)
|
|
lines.push(` .setAcceptDownloads(true)`);
|
|
if (options.bypassCSP)
|
|
lines.push(` .setBypassCSP(true)`);
|
|
if (options.colorScheme)
|
|
lines.push(` .setColorScheme(ColorScheme.${options.colorScheme.toUpperCase()})`);
|
|
if (options.deviceScaleFactor)
|
|
lines.push(` .setDeviceScaleFactor(${options.deviceScaleFactor})`);
|
|
if (options.geolocation)
|
|
lines.push(` .setGeolocation(${options.geolocation.latitude}, ${options.geolocation.longitude})`);
|
|
if (options.hasTouch)
|
|
lines.push(` .setHasTouch(${options.hasTouch})`);
|
|
if (options.isMobile)
|
|
lines.push(` .setIsMobile(${options.isMobile})`);
|
|
if (options.locale)
|
|
lines.push(` .setLocale(${quote(options.locale)})`);
|
|
if (options.proxy)
|
|
lines.push(` .setProxy(new Proxy(${quote(options.proxy.server)}))`);
|
|
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)
|
|
lines.push(` .setTimezoneId(${quote(options.timezoneId)})`);
|
|
if (options.userAgent)
|
|
lines.push(` .setUserAgent(${quote(options.userAgent)})`);
|
|
if (options.viewport)
|
|
lines.push(` .setViewportSize(${options.viewport.width}, ${options.viewport.height})`);
|
|
return lines.join('\n');
|
|
}
|
|
|
|
function formatClickOptions(options: types.MouseClickOptions) {
|
|
const lines = [];
|
|
if (options.button)
|
|
lines.push(` .setButton(MouseButton.${options.button.toUpperCase()})`);
|
|
if (options.modifiers)
|
|
lines.push(` .setModifiers(Arrays.asList(${options.modifiers.map(m => `KeyboardModifier.${m.toUpperCase()}`).join(', ')}))`);
|
|
if (options.clickCount)
|
|
lines.push(` .setClickCount(${options.clickCount})`);
|
|
if (options.position)
|
|
lines.push(` .setPosition(${options.position.x}, ${options.position.y})`);
|
|
if (!lines.length)
|
|
return '';
|
|
lines.unshift(`new Locator.ClickOptions()`);
|
|
return lines.join('\n');
|
|
}
|
|
|
|
function quote(text: string) {
|
|
return escapeWithQuotes(text, '\"');
|
|
}
|