@elastic/synthetics
Version:
Elastic synthetic monitoring agent
210 lines • 9.2 kB
JavaScript
;
/**
* MIT License
*
* Copyright (c) 2020-present, Elastic NV
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.JavaScriptFormatter = exports.JavaScriptLanguageGenerator = exports.quoteMultiline = void 0;
/**
* Adapted from playwright-core's `server/codegen/javascript.ts` and
* `server/codegen/language.ts` (Apache-2.0, (c) Microsoft Corporation).
*
* Playwright >= 1.61 bundles its server code into `coreBundle.js` and no
* longer ships the codegen classes as importable modules, so the previous
* `playwright-core/lib/server/codegen/javascript` deep import no longer
* resolves. We only need the slice of the generator that
* `SyntheticsGenerator` extends (`_asLocator` + `_generateActionCall`) plus
* `JavaScriptFormatter`; the heavy locator/serialization helpers are still
* reachable through the bundle's exported `iso` namespace, so we reuse those
* directly instead of vendoring them too.
*/
/* eslint-disable @typescript-eslint/no-var-requires */
const { iso } = require('playwright-core/lib/coreBundle');
/* eslint-enable @typescript-eslint/no-var-requires */
const { asLocator, escapeWithQuotes, formatObject } = iso;
function toKeyboardModifiers(modifiers = 0) {
const result = [];
if (modifiers & 1)
result.push('Alt');
if (modifiers & 2)
result.push('ControlOrMeta');
if (modifiers & 4)
result.push('ControlOrMeta');
if (modifiers & 8)
result.push('Shift');
return result;
}
function toClickOptionsForSourceCode(action) {
const modifiers = toKeyboardModifiers(action.modifiers);
const options = {};
if (action.button && action.button !== 'left')
options.button = action.button;
if (modifiers.length)
options.modifiers = modifiers;
// Do not render clickCount === 2 for dblclick.
if (action.clickCount && action.clickCount > 2)
options.clickCount = action.clickCount;
if (action.position)
options.position = action.position;
return options;
}
function formatOptions(value, hasArguments) {
const keys = Object.keys(value).filter(key => value[key] !== undefined);
if (!keys.length)
return '';
return (hasArguments ? ', ' : '') + formatObject(value);
}
function quote(text = '') {
return escapeWithQuotes(text, "'");
}
function quoteMultiline(text, indent = ' ') {
const escape = (t) => t.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
const lines = text.split('\n');
if (lines.length === 1)
return '`' + escape(text) + '`';
return ('`\n' +
lines
.map(line => indent + escape(line).replace(/\${/g, '\\${'))
.join('\n') +
`\n${indent}\``);
}
exports.quoteMultiline = quoteMultiline;
class JavaScriptLanguageGenerator {
_isTest;
constructor(isTest) {
this._isTest = isTest;
}
_asLocator(selector) {
return asLocator('javascript', selector);
}
_generateActionCall(subject, actionInContext) {
const action = actionInContext.action;
switch (action.name) {
case 'openPage':
throw Error('Not reached');
case 'closePage':
return `await ${subject}.close();`;
case 'click': {
let method = 'click';
if (action.clickCount === 2)
method = 'dblclick';
const options = toClickOptionsForSourceCode(action);
const optionsString = formatOptions(options, false);
return `await ${subject}.${this._asLocator(action.selector)}.${method}(${optionsString});`;
}
case 'hover':
return `await ${subject}.${this._asLocator(action.selector)}.hover(${formatOptions({ position: action.position }, false)});`;
case 'check':
return `await ${subject}.${this._asLocator(action.selector)}.check();`;
case 'uncheck':
return `await ${subject}.${this._asLocator(action.selector)}.uncheck();`;
case 'fill':
return `await ${subject}.${this._asLocator(action.selector)}.fill(${quote(action.text)});`;
case 'setInputFiles': {
const files = action.files ?? [];
return `await ${subject}.${this._asLocator(action.selector)}.setInputFiles(${formatObject(files.length === 1 ? files[0] : files)});`;
}
case 'press': {
const modifiers = toKeyboardModifiers(action.modifiers);
const shortcut = [...modifiers, action.key].join('+');
return `await ${subject}.${this._asLocator(action.selector)}.press(${quote(shortcut)});`;
}
case 'navigate':
return `await ${subject}.goto(${quote(action.url)});`;
case 'select': {
const opts = action.options ?? [];
return `await ${subject}.${this._asLocator(action.selector)}.selectOption(${formatObject(opts.length === 1 ? opts[0] : opts)});`;
}
case 'assertText':
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? 'toContainText' : 'toHaveText'}(${quote(action.text)});`;
case 'assertChecked':
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)})${action.checked ? '' : '.not'}.toBeChecked();`;
case 'assertVisible':
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)}).toBeVisible();`;
case 'assertValue': {
const assertion = action.value
? `toHaveValue(${quote(action.value)})`
: `toBeEmpty()`;
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
}
case 'assertSnapshot': {
const commentIfNeeded = this._isTest ? '' : '// ';
return `${commentIfNeeded}await expect(${subject}.${this._asLocator(action.selector)}).toMatchAriaSnapshot(${quoteMultiline(action.ariaSnapshot ?? '', `${commentIfNeeded} `)});`;
}
default:
return '';
}
}
}
exports.JavaScriptLanguageGenerator = JavaScriptLanguageGenerator;
class JavaScriptFormatter {
_baseIndent;
_baseOffset;
_lines = [];
constructor(offset = 0) {
this._baseIndent = ' '.repeat(2);
this._baseOffset = ' '.repeat(offset);
}
prepend(text) {
const trim = isMultilineString(text)
? (line) => line
: (line) => line.trim();
this._lines = text.trim().split('\n').map(trim).concat(this._lines);
}
add(text) {
const trim = isMultilineString(text)
? (line) => line
: (line) => line.trim();
this._lines.push(...text.trim().split('\n').map(trim));
}
newLine() {
this._lines.push('');
}
format() {
let spaces = '';
let previousLine = '';
return this._lines
.map((line) => {
if (line === '')
return line;
if (line.startsWith('}') || line.startsWith(']'))
spaces = spaces.substring(this._baseIndent.length);
const extraSpaces = /^(for|while|if|try).*\(.*\)$/.test(previousLine)
? this._baseIndent
: '';
previousLine = line;
const callCarryOver = line.startsWith('.set');
line =
spaces + extraSpaces + (callCarryOver ? this._baseIndent : '') + line;
if (line.endsWith('{') || line.endsWith('['))
spaces += this._baseIndent;
return this._baseOffset + line;
})
.join('\n');
}
}
exports.JavaScriptFormatter = JavaScriptFormatter;
function isMultilineString(text) {
return text.match(/`[\S\s]*`/)?.[0].includes('\n');
}
//# sourceMappingURL=codegen.js.map