Best JavaScript code snippet using playwright-internal
javascript.js
Source: javascript.js
1"use strict";2Object.defineProperty(exports, "__esModule", {3 value: true4});5exports.JavaScriptLanguageGenerator = exports.JavaScriptFormatter = void 0;6var _language = require("./language");7var _recorderActions = require("./recorderActions");8var _utils = require("./utils");9var _deviceDescriptors = _interopRequireDefault(require("../../deviceDescriptors"));10var _stringUtils = require("../../../utils/stringUtils");11function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }12/**13 * Copyright (c) Microsoft Corporation.14 *15 * Licensed under the Apache License, Version 2.0 (the "License");16 * you may not use this file except in compliance with the License.17 * You may obtain a copy of the License at18 *19 * http://www.apache.org/licenses/LICENSE-2.020 *21 * Unless required by applicable law or agreed to in writing, software22 * distributed under the License is distributed on an "AS IS" BASIS,23 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.24 * See the License for the specific language governing permissions and25 * limitations under the License.26 */27class JavaScriptLanguageGenerator {28 constructor(isTest) {29 this.id = void 0;30 this.fileName = void 0;31 this.highlighter = 'javascript';32 this._isTest = void 0;33 this.id = isTest ? 'test' : 'javascript';34 this.fileName = isTest ? 'Playwright Test' : 'JavaScript';35 this._isTest = isTest;36 }37 generateAction(actionInContext) {38 const {39 action,40 pageAlias41 } = actionInContext;42 const formatter = new JavaScriptFormatter(2);43 formatter.newLine();44 formatter.add('// ' + (0, _recorderActions.actionTitle)(action));45 if (action.name === 'openPage') {46 if (this._isTest) return '';47 formatter.add(`const ${pageAlias} = await context.newPage();`);48 if (action.url && action.url !== 'about:blank' && action.url !== 'chrome://newtab/') formatter.add(`await ${pageAlias}.goto(${quote(action.url)});`);49 return formatter.format();50 }51 const subject = actionInContext.isMainFrame ? pageAlias : actionInContext.frameName ? `${pageAlias}.frame(${formatObject({52 name: actionInContext.frameName53 })})` : `${pageAlias}.frame(${formatObject({54 url: actionInContext.frameUrl55 })})`;56 const signals = (0, _language.toSignalMap)(action);57 if (signals.dialog) {58 formatter.add(` ${pageAlias}.once('dialog', dialog => {59 console.log(\`Dialog message: $\{dialog.message()}\`);60 dialog.dismiss().catch(() => {});61 });`);62 }63 const emitPromiseAll = signals.waitForNavigation || signals.popup || signals.download;64 if (emitPromiseAll) {65 // Generate either await Promise.all([]) or66 // const [popup1] = await Promise.all([]).67 let leftHandSide = '';68 if (signals.popup) leftHandSide = `const [${signals.popup.popupAlias}] = `;else if (signals.download) leftHandSide = `const [download] = `;69 formatter.add(`${leftHandSide}await Promise.all([`);70 } // Popup signals.71 if (signals.popup) formatter.add(`${pageAlias}.waitForEvent('popup'),`); // Navigation signal.72 if (signals.waitForNavigation) formatter.add(`${pageAlias}.waitForNavigation(/*{ url: ${quote(signals.waitForNavigation.url)} }*/),`); // Download signals.73 if (signals.download) formatter.add(`${pageAlias}.waitForEvent('download'),`);74 const prefix = signals.popup || signals.waitForNavigation || signals.download ? '' : 'await ';75 const actionCall = this._generateActionCall(action);76 const suffix = signals.waitForNavigation || emitPromiseAll ? '' : ';';77 formatter.add(`${prefix}${subject}.${actionCall}${suffix}`);78 if (emitPromiseAll) {79 formatter.add(`]);`);80 } else if (signals.assertNavigation) {81 if (this._isTest) formatter.add(` await expect(${pageAlias}).toHaveURL(${quote(signals.assertNavigation.url)});`);else formatter.add(` // assert.equal(${pageAlias}.url(), ${quote(signals.assertNavigation.url)});`);82 }83 return formatter.format();84 }85 _generateActionCall(action) {86 switch (action.name) {87 case 'openPage':88 throw Error('Not reached');89 case 'closePage':90 return 'close()';91 case 'click':92 {93 let method = 'click';94 if (action.clickCount === 2) method = 'dblclick';95 const modifiers = (0, _utils.toModifiers)(action.modifiers);96 const options = {};97 if (action.button !== 'left') options.button = action.button;98 if (modifiers.length) options.modifiers = modifiers;99 if (action.clickCount > 2) options.clickCount = action.clickCount;100 if (action.position) options.position = action.position;101 const optionsString = formatOptions(options);102 return `${method}(${quote(action.selector)}${optionsString})`;103 }104 case 'check':105 return `check(${quote(action.selector)})`;106 case 'uncheck':107 return `uncheck(${quote(action.selector)})`;108 case 'fill':109 return `fill(${quote(action.selector)}, ${quote(action.text)})`;110 case 'setInputFiles':111 return `setInputFiles(${quote(action.selector)}, ${formatObject(action.files.length === 1 ? action.files[0] : action.files)})`;112 case 'press':113 {114 const modifiers = (0, _utils.toModifiers)(action.modifiers);115 const shortcut = [...modifiers, action.key].join('+');116 return `press(${quote(action.selector)}, ${quote(shortcut)})`;117 }118 case 'navigate':119 return `goto(${quote(action.url)})`;120 case 'select':121 return `selectOption(${quote(action.selector)}, ${formatObject(action.options.length > 1 ? action.options : action.options[0])})`;122 }123 }124 generateHeader(options) {125 if (this._isTest) return this.generateTestHeader(options);126 return this.generateStandaloneHeader(options);127 }128 generateFooter(saveStorage) {129 if (this._isTest) return this.generateTestFooter(saveStorage);130 return this.generateStandaloneFooter(saveStorage);131 }132 generateTestHeader(options) {133 const formatter = new JavaScriptFormatter();134 const useText = formatContextOptions(options.contextOptions, options.deviceName);135 formatter.add(`136 import { test, expect${options.deviceName ? ', devices' : ''} } from '@playwright/test';137${useText ? '\ntest.use(' + useText + ');\n' : ''}138 test('test', async ({ page }) => {`);139 return formatter.format();140 }141 generateTestFooter(saveStorage) {142 return `\n});`;143 }144 generateStandaloneHeader(options) {145 const formatter = new JavaScriptFormatter();146 formatter.add(`147 const { ${options.browserName}${options.deviceName ? ', devices' : ''} } = require('playwright');148 (async () => {149 const browser = await ${options.browserName}.launch(${formatObjectOrVoid(options.launchOptions)});150 const context = await browser.newContext(${formatContextOptions(options.contextOptions, options.deviceName)});`);151 return formatter.format();152 }153 generateStandaloneFooter(saveStorage) {154 const storageStateLine = saveStorage ? `\n await context.storageState({ path: ${quote(saveStorage)} });` : '';155 return `\n // ---------------------${storageStateLine}156 await context.close();157 await browser.close();158})();`;159 }160}161exports.JavaScriptLanguageGenerator = JavaScriptLanguageGenerator;162function formatOptions(value) {163 const keys = Object.keys(value);164 if (!keys.length) return '';165 return ', ' + formatObject(value);166}167function formatObject(value, indent = ' ') {168 if (typeof value === 'string') return quote(value);169 if (Array.isArray(value)) return `[${value.map(o => formatObject(o)).join(', ')}]`;170 if (typeof value === 'object') {171 const keys = Object.keys(value);172 if (!keys.length) return '{}';173 const tokens = [];174 for (const key of keys) tokens.push(`${key}: ${formatObject(value[key])}`);175 return `{\n${indent}${tokens.join(`,\n${indent}`)}\n}`;176 }177 return String(value);178}179function formatObjectOrVoid(value, indent = ' ') {180 const result = formatObject(value, indent);181 return result === '{}' ? '' : result;182}183function formatContextOptions(options, deviceName) {184 const device = deviceName && _deviceDescriptors.default[deviceName];185 if (!device) return formatObjectOrVoid(options); // Filter out all the properties from the device descriptor.186 let serializedObject = formatObjectOrVoid((0, _language.sanitizeDeviceOptions)(device, options)); // When there are no additional context options, we still want to spread the device inside.187 if (!serializedObject) serializedObject = '{\n}';188 const lines = serializedObject.split('\n');189 lines.splice(1, 0, `...devices[${quote(deviceName)}],`);190 return lines.join('\n');191}192class JavaScriptFormatter {193 constructor(offset = 0) {194 this._baseIndent = void 0;195 this._baseOffset = void 0;196 this._lines = [];197 this._baseIndent = ' '.repeat(2);198 this._baseOffset = ' '.repeat(offset);199 }200 prepend(text) {201 this._lines = text.trim().split('\n').map(line => line.trim()).concat(this._lines);202 }203 add(text) {204 this._lines.push(...text.trim().split('\n').map(line => line.trim()));205 }206 newLine() {207 this._lines.push('');208 }209 format() {210 let spaces = '';211 let previousLine = '';212 return this._lines.map(line => {213 if (line === '') return line;214 if (line.startsWith('}') || line.startsWith(']')) spaces = spaces.substring(this._baseIndent.length);215 const extraSpaces = /^(for|while|if|try).*\(.*\)$/.test(previousLine) ? this._baseIndent : '';216 previousLine = line;217 const callCarryOver = line.startsWith('.set');218 line = spaces + extraSpaces + (callCarryOver ? this._baseIndent : '') + line;219 if (line.endsWith('{') || line.endsWith('[')) spaces += this._baseIndent;220 return this._baseOffset + line;221 }).join('\n');222 }223}224exports.JavaScriptFormatter = JavaScriptFormatter;225function quote(text) {226 return (0, _stringUtils.escapeWithQuotes)(text, '\'');...
csharp.js
Source: csharp.js
1"use strict";2Object.defineProperty(exports, "__esModule", {3 value: true4});5exports.CSharpLanguageGenerator = void 0;6var _language = require("./language");7var _recorderActions = require("./recorderActions");8var _utils = require("./utils");9var _stringUtils = require("../../../utils/stringUtils");10var _deviceDescriptors = _interopRequireDefault(require("../../deviceDescriptors"));11function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }12/**13 * Copyright (c) Microsoft Corporation.14 *15 * Licensed under the Apache License, Version 2.0 (the "License");16 * you may not use this file except in compliance with the License.17 * You may obtain a copy of the License at18 *19 * http://www.apache.org/licenses/LICENSE-2.020 *21 * Unless required by applicable law or agreed to in writing, software22 * distributed under the License is distributed on an "AS IS" BASIS,23 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.24 * See the License for the specific language governing permissions and25 * limitations under the License.26 */27class CSharpLanguageGenerator {28 constructor() {29 this.id = 'csharp';30 this.fileName = 'C#';31 this.highlighter = 'csharp';32 }33 generateAction(actionInContext) {34 const {35 action,36 pageAlias37 } = actionInContext;38 const formatter = new CSharpFormatter(8);39 formatter.newLine();40 formatter.add('// ' + (0, _recorderActions.actionTitle)(action));41 if (action.name === 'openPage') {42 formatter.add(`var ${pageAlias} = await context.NewPageAsync();`);43 if (action.url && action.url !== 'about:blank' && action.url !== 'chrome://newtab/') formatter.add(`await ${pageAlias}.GotoAsync(${quote(action.url)});`);44 return formatter.format();45 }46 const subject = actionInContext.isMainFrame ? pageAlias : actionInContext.frameName ? `${pageAlias}.Frame(${quote(actionInContext.frameName)})` : `${pageAlias}.FrameByUrl(${quote(actionInContext.frameUrl)})`;47 const signals = (0, _language.toSignalMap)(action);48 if (signals.dialog) {49 formatter.add(` void ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler(object sender, IDialog dialog)50 {51 Console.WriteLine($"Dialog message: {dialog.Message}");52 dialog.DismissAsync();53 ${pageAlias}.Dialog -= ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler;54 }55 ${pageAlias}.Dialog += ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler;`);56 }57 const lines = [];58 const actionCall = this._generateActionCall(action, actionInContext.isMainFrame);59 if (signals.waitForNavigation) {60 lines.push(`await ${pageAlias}.RunAndWaitForNavigationAsync(async () =>`);61 lines.push(`{`);62 lines.push(` await ${subject}.${actionCall};`);63 lines.push(`}/*, new ${actionInContext.isMainFrame ? 'Page' : 'Frame'}WaitForNavigationOptions`);64 lines.push(`{`);65 lines.push(` UrlString = ${quote(signals.waitForNavigation.url)}`);66 lines.push(`}*/);`);67 } else {68 lines.push(`await ${subject}.${actionCall};`);69 }70 if (signals.download) {71 lines.unshift(`var download${signals.download.downloadAlias} = await ${pageAlias}.RunAndWaitForDownloadAsync(async () =>\n{`);72 lines.push(`});`);73 }74 if (signals.popup) {75 lines.unshift(`var ${signals.popup.popupAlias} = await ${pageAlias}.RunAndWaitForPopupAsync(async () =>\n{`);76 lines.push(`});`);77 }78 for (const line of lines) formatter.add(line);79 if (signals.assertNavigation) formatter.add(` // Assert.AreEqual(${quote(signals.assertNavigation.url)}, ${pageAlias}.Url);`);80 return formatter.format();81 }82 _generateActionCall(action, isPage) {83 switch (action.name) {84 case 'openPage':85 throw Error('Not reached');86 case 'closePage':87 return 'CloseAsync()';88 case 'click':89 {90 let method = 'Click';91 if (action.clickCount === 2) method = 'DblClick';92 const modifiers = (0, _utils.toModifiers)(action.modifiers);93 const options = {};94 if (action.button !== 'left') options.button = action.button;95 if (modifiers.length) options.modifiers = modifiers;96 if (action.clickCount > 2) options.clickCount = action.clickCount;97 if (action.position) options.position = action.position;98 if (!Object.entries(options).length) return `${method}Async(${quote(action.selector)})`;99 const optionsString = formatObject(options, ' ', (isPage ? 'Page' : 'Frame') + method + 'Options');100 return `${method}Async(${quote(action.selector)}, ${optionsString})`;101 }102 case 'check':103 return `CheckAsync(${quote(action.selector)})`;104 case 'uncheck':105 return `UncheckAsync(${quote(action.selector)})`;106 case 'fill':107 return `FillAsync(${quote(action.selector)}, ${quote(action.text)})`;108 case 'setInputFiles':109 return `SetInputFilesAsync(${quote(action.selector)}, ${formatObject(action.files)})`;110 case 'press':111 {112 const modifiers = (0, _utils.toModifiers)(action.modifiers);113 const shortcut = [...modifiers, action.key].join('+');114 return `PressAsync(${quote(action.selector)}, ${quote(shortcut)})`;115 }116 case 'navigate':117 return `GotoAsync(${quote(action.url)})`;118 case 'select':119 return `SelectOptionAsync(${quote(action.selector)}, ${formatObject(action.options)})`;120 }121 }122 generateHeader(options) {123 const formatter = new CSharpFormatter(0);124 formatter.add(`125 using Microsoft.Playwright;126 using System;127 using System.Threading.Tasks;128 class Program129 {130 public static async Task Main()131 {132 using var playwright = await Playwright.CreateAsync();133 await using var browser = await playwright.${toPascal(options.browserName)}.LaunchAsync(${formatObject(options.launchOptions, ' ', 'BrowserTypeLaunchOptions')});134 var context = await browser.NewContextAsync(${formatContextOptions(options.contextOptions, options.deviceName)});`);135 return formatter.format();136 }137 generateFooter(saveStorage) {138 const storageStateLine = saveStorage ? `\n await context.StorageStateAsync(new BrowserContextStorageStateOptions\n {\n Path = ${quote(saveStorage)}\n });\n` : '';139 return `${storageStateLine} }140}\n`;141 }142}143exports.CSharpLanguageGenerator = CSharpLanguageGenerator;144function formatObject(value, indent = ' ', name = '') {145 if (typeof value === 'string') {146 if (['permissions', 'colorScheme', 'modifiers', 'button'].includes(name)) return `${getClassName(name)}.${toPascal(value)}`;147 return quote(value);148 }149 if (Array.isArray(value)) return `new[] { ${value.map(o => formatObject(o, indent, name)).join(', ')} }`;150 if (typeof value === 'object') {151 const keys = Object.keys(value);152 if (!keys.length) return name ? `new ${getClassName(name)}` : '';153 const tokens = [];154 for (const key of keys) {155 const property = getPropertyName(key);156 tokens.push(`${property} = ${formatObject(value[key], indent, key)},`);157 }158 if (name) return `new ${getClassName(name)}\n{\n${indent}${tokens.join(`\n${indent}`)}\n${indent}}`;159 return `{\n${indent}${tokens.join(`\n${indent}`)}\n${indent}}`;160 }161 if (name === 'latitude' || name === 'longitude') return String(value) + 'm';162 return String(value);163}164function getClassName(value) {165 switch (value) {166 case 'viewport':167 return 'ViewportSize';168 case 'proxy':169 return 'ProxySettings';170 case 'permissions':171 return 'ContextPermission';172 case 'modifiers':173 return 'KeyboardModifier';174 case 'button':175 return 'MouseButton';176 default:177 return toPascal(value);178 }179}180function getPropertyName(key) {181 switch (key) {182 case 'storageState':183 return 'StorageStatePath';184 case 'viewport':185 return 'ViewportSize';186 default:187 return toPascal(key);188 }189}190function toPascal(value) {191 return value[0].toUpperCase() + value.slice(1);192}193function formatContextOptions(options, deviceName) {194 const device = deviceName && _deviceDescriptors.default[deviceName];195 if (!device) {196 if (!Object.entries(options).length) return '';197 return formatObject(options, ' ', 'BrowserNewContextOptions');198 }199 options = (0, _language.sanitizeDeviceOptions)(device, options);200 if (!Object.entries(options).length) return `playwright.Devices[${quote(deviceName)}]`;201 return formatObject(options, ' ', `BrowserNewContextOptions(playwright.Devices[${quote(deviceName)}])`);202}203class CSharpFormatter {204 constructor(offset = 0) {205 this._baseIndent = void 0;206 this._baseOffset = void 0;207 this._lines = [];208 this._baseIndent = ' '.repeat(4);209 this._baseOffset = ' '.repeat(offset);210 }211 prepend(text) {212 this._lines = text.trim().split('\n').map(line => line.trim()).concat(this._lines);213 }214 add(text) {215 this._lines.push(...text.trim().split('\n').map(line => line.trim()));216 }217 newLine() {218 this._lines.push('');219 }220 format() {221 let spaces = '';222 let previousLine = '';223 return this._lines.map(line => {224 if (line === '') return line;225 if (line.startsWith('}') || line.startsWith(']') || line.includes('});') || line === ');') spaces = spaces.substring(this._baseIndent.length);226 const extraSpaces = /^(for|while|if).*\(.*\)$/.test(previousLine) ? this._baseIndent : '';227 previousLine = line;228 line = spaces + extraSpaces + line;229 if (line.endsWith('{') || line.endsWith('[') || line.endsWith('(')) spaces += this._baseIndent;230 if (line.endsWith('));')) spaces = spaces.substring(this._baseIndent.length);231 return this._baseOffset + line;232 }).join('\n');233 }234}235function quote(text) {236 return (0, _stringUtils.escapeWithQuotes)(text, '\"');...
python.js
Source: python.js
1"use strict";2Object.defineProperty(exports, "__esModule", {3 value: true4});5exports.PythonLanguageGenerator = void 0;6var _language = require("./language");7var _recorderActions = require("./recorderActions");8var _utils = require("./utils");9var _stringUtils = require("../../../utils/stringUtils");10var _deviceDescriptors = _interopRequireDefault(require("../../deviceDescriptors"));11function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }12/**13 * Copyright (c) Microsoft Corporation.14 *15 * Licensed under the Apache License, Version 2.0 (the "License");16 * you may not use this file except in compliance with the License.17 * You may obtain a copy of the License at18 *19 * http://www.apache.org/licenses/LICENSE-2.020 *21 * Unless required by applicable law or agreed to in writing, software22 * distributed under the License is distributed on an "AS IS" BASIS,23 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.24 * See the License for the specific language governing permissions and25 * limitations under the License.26 */27class PythonLanguageGenerator {28 constructor(isAsync) {29 this.id = 'python';30 this.fileName = 'Python';31 this.highlighter = 'python';32 this._awaitPrefix = void 0;33 this._asyncPrefix = void 0;34 this._isAsync = void 0;35 this.id = isAsync ? 'python-async' : 'python';36 this.fileName = isAsync ? 'Python Async' : 'Python';37 this._isAsync = isAsync;38 this._awaitPrefix = isAsync ? 'await ' : '';39 this._asyncPrefix = isAsync ? 'async ' : '';40 }41 generateAction(actionInContext) {42 const {43 action,44 pageAlias45 } = actionInContext;46 const formatter = new PythonFormatter(4);47 formatter.newLine();48 formatter.add('# ' + (0, _recorderActions.actionTitle)(action));49 if (action.name === 'openPage') {50 formatter.add(`${pageAlias} = ${this._awaitPrefix}context.new_page()`);51 if (action.url && action.url !== 'about:blank' && action.url !== 'chrome://newtab/') formatter.add(`${this._awaitPrefix}${pageAlias}.goto(${quote(action.url)})`);52 return formatter.format();53 }54 const subject = actionInContext.isMainFrame ? pageAlias : actionInContext.frameName ? `${pageAlias}.frame(${formatOptions({55 name: actionInContext.frameName56 }, false)})` : `${pageAlias}.frame(${formatOptions({57 url: actionInContext.frameUrl58 }, false)})`;59 const signals = (0, _language.toSignalMap)(action);60 if (signals.dialog) formatter.add(` ${pageAlias}.once("dialog", lambda dialog: dialog.dismiss())`);61 const actionCall = this._generateActionCall(action);62 let code = `${this._awaitPrefix}${subject}.${actionCall}`;63 if (signals.popup) {64 code = `${this._asyncPrefix}with ${pageAlias}.expect_popup() as popup_info {65 ${code}66 }67 ${signals.popup.popupAlias} = ${this._awaitPrefix}popup_info.value`;68 }69 if (signals.download) {70 code = `${this._asyncPrefix}with ${pageAlias}.expect_download() as download_info {71 ${code}72 }73 download = ${this._awaitPrefix}download_info.value`;74 }75 if (signals.waitForNavigation) {76 code = `77 # ${this._asyncPrefix}with ${pageAlias}.expect_navigation(url=${quote(signals.waitForNavigation.url)}):78 ${this._asyncPrefix}with ${pageAlias}.expect_navigation() {79 ${code}80 }`;81 }82 formatter.add(code);83 if (signals.assertNavigation) formatter.add(` # assert ${pageAlias}.url == ${quote(signals.assertNavigation.url)}`);84 return formatter.format();85 }86 _generateActionCall(action) {87 switch (action.name) {88 case 'openPage':89 throw Error('Not reached');90 case 'closePage':91 return 'close()';92 case 'click':93 {94 let method = 'click';95 if (action.clickCount === 2) method = 'dblclick';96 const modifiers = (0, _utils.toModifiers)(action.modifiers);97 const options = {};98 if (action.button !== 'left') options.button = action.button;99 if (modifiers.length) options.modifiers = modifiers;100 if (action.clickCount > 2) options.clickCount = action.clickCount;101 if (action.position) options.position = action.position;102 const optionsString = formatOptions(options, true);103 return `${method}(${quote(action.selector)}${optionsString})`;104 }105 case 'check':106 return `check(${quote(action.selector)})`;107 case 'uncheck':108 return `uncheck(${quote(action.selector)})`;109 case 'fill':110 return `fill(${quote(action.selector)}, ${quote(action.text)})`;111 case 'setInputFiles':112 return `set_input_files(${quote(action.selector)}, ${formatValue(action.files.length === 1 ? action.files[0] : action.files)})`;113 case 'press':114 {115 const modifiers = (0, _utils.toModifiers)(action.modifiers);116 const shortcut = [...modifiers, action.key].join('+');117 return `press(${quote(action.selector)}, ${quote(shortcut)})`;118 }119 case 'navigate':120 return `goto(${quote(action.url)})`;121 case 'select':122 return `select_option(${quote(action.selector)}, ${formatValue(action.options.length === 1 ? action.options[0] : action.options)})`;123 }124 }125 generateHeader(options) {126 const formatter = new PythonFormatter();127 if (this._isAsync) {128 formatter.add(`129import asyncio130from playwright.async_api import Playwright, async_playwright131async def run(playwright: Playwright) -> None {132 browser = await playwright.${options.browserName}.launch(${formatOptions(options.launchOptions, false)})133 context = await browser.new_context(${formatContextOptions(options.contextOptions, options.deviceName)})`);134 } else {135 formatter.add(`136from playwright.sync_api import Playwright, sync_playwright137def run(playwright: Playwright) -> None {138 browser = playwright.${options.browserName}.launch(${formatOptions(options.launchOptions, false)})139 context = browser.new_context(${formatContextOptions(options.contextOptions, options.deviceName)})`);140 }141 return formatter.format();142 }143 generateFooter(saveStorage) {144 if (this._isAsync) {145 const storageStateLine = saveStorage ? `\n await context.storage_state(path=${quote(saveStorage)})` : '';146 return `\n # ---------------------${storageStateLine}147 await context.close()148 await browser.close()149async def main() -> None:150 async with async_playwright() as playwright:151 await run(playwright)152asyncio.run(main())153`;154 } else {155 const storageStateLine = saveStorage ? `\n context.storage_state(path=${quote(saveStorage)})` : '';156 return `\n # ---------------------${storageStateLine}157 context.close()158 browser.close()159with sync_playwright() as playwright:160 run(playwright)161`;162 }163 }164}165exports.PythonLanguageGenerator = PythonLanguageGenerator;166function formatValue(value) {167 if (value === false) return 'False';168 if (value === true) return 'True';169 if (value === undefined) return 'None';170 if (Array.isArray(value)) return `[${value.map(formatValue).join(', ')}]`;171 if (typeof value === 'string') return quote(value);172 if (typeof value === 'object') return JSON.stringify(value);173 return String(value);174}175function toSnakeCase(name) {176 const toSnakeCaseRegex = /((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))/g;177 return name.replace(toSnakeCaseRegex, `_$1`).toLowerCase();178}179function formatOptions(value, hasArguments) {180 const keys = Object.keys(value);181 if (!keys.length) return '';182 return (hasArguments ? ', ' : '') + keys.map(key => `${toSnakeCase(key)}=${formatValue(value[key])}`).join(', ');183}184function formatContextOptions(options, deviceName) {185 const device = deviceName && _deviceDescriptors.default[deviceName];186 if (!device) return formatOptions(options, false);187 return `**playwright.devices[${quote(deviceName)}]` + formatOptions((0, _language.sanitizeDeviceOptions)(device, options), true);188}189class PythonFormatter {190 constructor(offset = 0) {191 this._baseIndent = void 0;192 this._baseOffset = void 0;193 this._lines = [];194 this._baseIndent = ' '.repeat(4);195 this._baseOffset = ' '.repeat(offset);196 }197 prepend(text) {198 this._lines = text.trim().split('\n').map(line => line.trim()).concat(this._lines);199 }200 add(text) {201 this._lines.push(...text.trim().split('\n').map(line => line.trim()));202 }203 newLine() {204 this._lines.push('');205 }206 format() {207 let spaces = '';208 const lines = [];209 this._lines.forEach(line => {210 if (line === '') return lines.push(line);211 if (line === '}') {212 spaces = spaces.substring(this._baseIndent.length);213 return;214 }215 line = spaces + line;216 if (line.endsWith('{')) {217 spaces += this._baseIndent;218 line = line.substring(0, line.length - 1).trimEnd() + ':';219 }220 return lines.push(this._baseOffset + line);221 });222 return lines.join('\n');223 }224}225function quote(text) {226 return (0, _stringUtils.escapeWithQuotes)(text, '\"');...
java.js
Source: java.js
1"use strict";2Object.defineProperty(exports, "__esModule", {3 value: true4});5exports.JavaLanguageGenerator = void 0;6var _language = require("./language");7var _recorderActions = require("./recorderActions");8var _utils = require("./utils");9var _deviceDescriptors = _interopRequireDefault(require("../../deviceDescriptors"));10var _javascript = require("./javascript");11var _stringUtils = require("../../../utils/stringUtils");12function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }13/**14 * Copyright (c) Microsoft Corporation.15 *16 * Licensed under the Apache License, Version 2.0 (the "License");17 * you may not use this file except in compliance with the License.18 * You may obtain a copy of the License at19 *20 * http://www.apache.org/licenses/LICENSE-2.021 *22 * Unless required by applicable law or agreed to in writing, software23 * distributed under the License is distributed on an "AS IS" BASIS,24 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.25 * See the License for the specific language governing permissions and26 * limitations under the License.27 */28class JavaLanguageGenerator {29 constructor() {30 this.id = 'java';31 this.fileName = 'Java';32 this.highlighter = 'java';33 }34 generateAction(actionInContext) {35 const {36 action,37 pageAlias38 } = actionInContext;39 const formatter = new _javascript.JavaScriptFormatter(6);40 formatter.newLine();41 formatter.add('// ' + (0, _recorderActions.actionTitle)(action));42 if (action.name === 'openPage') {43 formatter.add(`Page ${pageAlias} = context.newPage();`);44 if (action.url && action.url !== 'about:blank' && action.url !== 'chrome://newtab/') formatter.add(`${pageAlias}.navigate(${quote(action.url)});`);45 return formatter.format();46 }47 const subject = actionInContext.isMainFrame ? pageAlias : actionInContext.frameName ? `${pageAlias}.frame(${quote(actionInContext.frameName)})` : `${pageAlias}.frameByUrl(${quote(actionInContext.frameUrl)})`;48 const signals = (0, _language.toSignalMap)(action);49 if (signals.dialog) {50 formatter.add(` ${pageAlias}.onceDialog(dialog -> {51 System.out.println(String.format("Dialog message: %s", dialog.message()));52 dialog.dismiss();53 });`);54 }55 const actionCall = this._generateActionCall(action, actionInContext.isMainFrame);56 let code = `${subject}.${actionCall};`;57 if (signals.popup) {58 code = `Page ${signals.popup.popupAlias} = ${pageAlias}.waitForPopup(() -> {59 ${code}60 });`;61 }62 if (signals.download) {63 code = `Download download = ${pageAlias}.waitForDownload(() -> {64 ${code}65 });`;66 }67 if (signals.waitForNavigation) {68 code = `69 // ${pageAlias}.waitForNavigation(new Page.WaitForNavigationOptions().setUrl(${quote(signals.waitForNavigation.url)}), () ->70 ${pageAlias}.waitForNavigation(() -> {71 ${code}72 });`;73 }74 formatter.add(code);75 if (signals.assertNavigation) formatter.add(`// assert ${pageAlias}.url().equals(${quote(signals.assertNavigation.url)});`);76 return formatter.format();77 }78 _generateActionCall(action, isPage) {79 switch (action.name) {80 case 'openPage':81 throw Error('Not reached');82 case 'closePage':83 return 'close()';84 case 'click':85 {86 let method = 'click';87 if (action.clickCount === 2) method = 'dblclick';88 const modifiers = (0, _utils.toModifiers)(action.modifiers);89 const options = {};90 if (action.button !== 'left') options.button = action.button;91 if (modifiers.length) options.modifiers = modifiers;92 if (action.clickCount > 2) options.clickCount = action.clickCount;93 if (action.position) options.position = action.position;94 const optionsText = formatClickOptions(options, isPage);95 return `${method}(${quote(action.selector)}${optionsText ? ', ' : ''}${optionsText})`;96 }97 case 'check':98 return `check(${quote(action.selector)})`;99 case 'uncheck':100 return `uncheck(${quote(action.selector)})`;101 case 'fill':102 return `fill(${quote(action.selector)}, ${quote(action.text)})`;103 case 'setInputFiles':104 return `setInputFiles(${quote(action.selector)}, ${formatPath(action.files.length === 1 ? action.files[0] : action.files)})`;105 case 'press':106 {107 const modifiers = (0, _utils.toModifiers)(action.modifiers);108 const shortcut = [...modifiers, action.key].join('+');109 return `press(${quote(action.selector)}, ${quote(shortcut)})`;110 }111 case 'navigate':112 return `navigate(${quote(action.url)})`;113 case 'select':114 return `selectOption(${quote(action.selector)}, ${formatSelectOption(action.options.length > 1 ? action.options : action.options[0])})`;115 }116 }117 generateHeader(options) {118 const formatter = new _javascript.JavaScriptFormatter();119 formatter.add(`120 import com.microsoft.playwright.*;121 import com.microsoft.playwright.options.*;122 import java.util.*;123 public class Example {124 public static void main(String[] args) {125 try (Playwright playwright = Playwright.create()) {126 Browser browser = playwright.${options.browserName}().launch(${formatLaunchOptions(options.launchOptions)});127 BrowserContext context = browser.newContext(${formatContextOptions(options.contextOptions, options.deviceName)});`);128 return formatter.format();129 }130 generateFooter(saveStorage) {131 const storageStateLine = saveStorage ? `\n context.storageState(new BrowserContext.StorageStateOptions().setPath(${quote(saveStorage)}));\n` : '';132 return `${storageStateLine} }133 }134}`;135 }136}137exports.JavaLanguageGenerator = JavaLanguageGenerator;138function formatPath(files) {139 if (Array.isArray(files)) {140 if (files.length === 0) return 'new Path[0]';141 return `new Path[] {${files.map(s => 'Paths.get(' + quote(s) + ')').join(', ')}}`;142 }143 return `Paths.get(${quote(files)})`;144}145function formatSelectOption(options) {146 if (Array.isArray(options)) {147 if (options.length === 0) return 'new String[0]';148 return `new String[] {${options.map(s => quote(s)).join(', ')}}`;149 }150 return quote(options);151}152function formatLaunchOptions(options) {153 const lines = [];154 if (!Object.keys(options).length) return '';155 lines.push('new BrowserType.LaunchOptions()');156 if (typeof options.headless === 'boolean') lines.push(` .setHeadless(false)`);157 if (options.channel) lines.push(` .setChannel(${quote(options.channel)})`);158 return lines.join('\n');159}160function formatContextOptions(contextOptions, deviceName) {161 const lines = [];162 if (!Object.keys(contextOptions).length && !deviceName) return '';163 const device = deviceName ? _deviceDescriptors.default[deviceName] : {};164 const options = { ...device,165 ...contextOptions166 };167 lines.push('new Browser.NewContextOptions()');168 if (options.acceptDownloads) lines.push(` .setAcceptDownloads(true)`);169 if (options.bypassCSP) lines.push(` .setBypassCSP(true)`);170 if (options.colorScheme) lines.push(` .setColorScheme(ColorScheme.${options.colorScheme.toUpperCase()})`);171 if (options.deviceScaleFactor) lines.push(` .setDeviceScaleFactor(${options.deviceScaleFactor})`);172 if (options.geolocation) lines.push(` .setGeolocation(${options.geolocation.latitude}, ${options.geolocation.longitude})`);173 if (options.hasTouch) lines.push(` .setHasTouch(${options.hasTouch})`);174 if (options.isMobile) lines.push(` .setIsMobile(${options.isMobile})`);175 if (options.locale) lines.push(` .setLocale(${quote(options.locale)})`);176 if (options.proxy) lines.push(` .setProxy(new Proxy(${quote(options.proxy.server)}))`);177 if (options.storageState) lines.push(` .setStorageStatePath(Paths.get(${quote(options.storageState)}))`);178 if (options.timezoneId) lines.push(` .setTimezoneId(${quote(options.timezoneId)})`);179 if (options.userAgent) lines.push(` .setUserAgent(${quote(options.userAgent)})`);180 if (options.viewport) lines.push(` .setViewportSize(${options.viewport.width}, ${options.viewport.height})`);181 return lines.join('\n');182}183function formatClickOptions(options, isPage) {184 const lines = [];185 if (options.button) lines.push(` .setButton(MouseButton.${options.button.toUpperCase()})`);186 if (options.modifiers) lines.push(` .setModifiers(Arrays.asList(${options.modifiers.map(m => `KeyboardModifier.${m.toUpperCase()}`).join(', ')}))`);187 if (options.clickCount) lines.push(` .setClickCount(${options.clickCount})`);188 if (options.position) lines.push(` .setPosition(${options.position.x}, ${options.position.y})`);189 if (!lines.length) return '';190 lines.unshift(`new ${isPage ? 'Page' : 'Frame'}.ClickOptions()`);191 return lines.join('\n');192}193function quote(text) {194 return (0, _stringUtils.escapeWithQuotes)(text, '\"');...
language.js
Source: language.js
...26 if (JSON.stringify(device[property]) !== JSON.stringify(options[property])) cleanedOptions[property] = options[property];27 }28 return cleanedOptions;29}30function toSignalMap(action) {31 let waitForNavigation;32 let assertNavigation;33 let popup;34 let download;35 let dialog;36 for (const signal of action.signals) {37 if (signal.name === 'navigation' && signal.isAsync) waitForNavigation = signal;else if (signal.name === 'navigation' && !signal.isAsync) assertNavigation = signal;else if (signal.name === 'popup') popup = signal;else if (signal.name === 'download') download = signal;else if (signal.name === 'dialog') dialog = signal;38 }39 return {40 waitForNavigation,41 assertNavigation,42 popup,43 download,44 dialog...
Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 const signal = await page.waitForSignal('signal');7 console.log(signal);8 await browser.close();9})();
Using AI Code Generation
1const { chromium } = require('playwright');2const { toSignalMap } = require('playwright/lib/server/supplements/recorder/recorderSupplement');3(async () => {4 const browser = await chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 await page.click('text=Docs');8 const signalMap = toSignalMap(page);9 console.log(signalMap);10 await browser.close();11})();12Map(1) {13 'page' => Map(2) {14 'goto' => Map(1) {15 }16 },17 'click' => Map(1) {18 'text=Docs' => Map(0) {}19 }20 }21}22const { chromium } = require('playwright');23const { toSignalMap } = require('playwright/lib/server/supplements/recorder/recorderSupplement');24(async () => {25 const browser = await chromium.launch();26 const context = await browser.newContext();27 const page = await context.newPage();28 await page.click('text=Docs');29 const signalMap = toSignalMap(page);30 const actionSignal = signalMap.get('page').get('click').get('text=Docs').get('waitUntil');31 console.log(actionSignal);32 await browser.close();33})();
Using AI Code Generation
1const { toSignalMap } = require('playwright/lib/utils/utils');2const { chromium } = require('playwright');3(async () => {4 const browser = await chromium.launch();5 const page = await browser.newPage();6 await page.screenshot({ path: 'google.png' });7 await browser.close();8})();9const toSignalMap = (signal) => {10 if (signal == 'SIGINT') return 'SIGINT';11 if (signal == 'SIGTERM') return 'SIGTERM';12 return 'SIGINT';13};14module.exports = {15};
Using AI Code Generation
1const { toSignalMap } = require('playwright/lib/utils/utils');2const { chromium } = require('playwright');3const browser = await chromium.launch();4const context = await browser.newContext();5const page = await context.newPage();6await page.waitForSelector('input');7const signalMap = toSignalMap(page);8console.log(signalMap);9await browser.close();10const { chromium } = require('playwright');11const browser = await chromium.launch();12const context = await browser.newContext();13const page = await context.newPage();14await page.waitForSelector('input');15const signalMap = page._eventEmitter._events;16console.log(signalMap);17await browser.close();
Using AI Code Generation
1const { InternalAPI } = require('playwright/lib/server/api');2const { BrowserContext } = require('playwright/lib/server/browserContext');3const { Page } = require('playwright/lib/server/page');4const { SignalMap } = require('playwright/lib/server/signalMap');5const { toSignalMap } = InternalAPI;6const signalMap = toSignalMap(new Page(new BrowserContext(null), null));7signalMap.set('foo', 'bar');8signalMap.set('foo1', 'bar1');9signalMap.set('foo2', 'bar2');10console.log(signalMap.get('foo'));11console.log(signalMap.get('foo1'));12console.log(signalMap.get('foo2'));13const { InternalAPI } = require('playwright/lib/server/api');14const { BrowserContext } = require('playwright/lib/server/browserContext');15const { Page } = require('playwright/lib/server/page');16const { SignalMap } = require('playwright/lib/server/signalMap');17const { toSignalMap } = InternalAPI;18const signalMap = toSignalMap(new Page(new BrowserContext(null), null));19signalMap.set('foo', 'bar');20signalMap.set('foo1', 'bar1');21signalMap.set('foo2', 'bar2');22console.log(signalMap.get('foo'));23console.log(signalMap.get('foo1'));24console.log(signalMap.get('foo2'));25const { InternalAPI } = require('playwright/lib/server/api');26const { BrowserContext } = require('playwright/lib/server/browserContext');27const { Page } = require('playwright/lib/server/page');28const { SignalMap } = require('playwright/lib/server/signalMap');29const { toSignalMap } = InternalAPI;30const signalMap = toSignalMap(new Page(new BrowserContext(null), null));31signalMap.set('foo', 'bar');32signalMap.set('foo1', 'bar1');33signalMap.set('foo2', 'bar2');34console.log(signalMap.get('foo'));35console.log(signalMap.get('foo1'));36console.log(signalMap.get('foo2'));37const { InternalAPI } = require('playwright/lib
Using AI Code Generation
1const { toSignalMap } = require('playwright/lib/server/browserType');2const { toSignalMap } = require('playwright/lib/server/browserType');3const { toSignalMap } = require('playwright/lib/server/browserType');4const { toSignalMap } = require('playwright/lib/server/browserType');5const { toSignalMap } = require('playwright/lib/server/browserType');6const { toSignalMap } = require('playwright/lib/server/browserType');7const { toSignalMap } = require('playwright/lib/server/browserType');8const { toSignalMap } = require('playwright/lib/server/browserType');9const { toSignalMap } = require('playwright/lib/server/browserType');10const { toSignalMap } = require('playwright/lib/server/browserType');11const { toSignalMap } = require('playwright/lib/server/browserType');12const { toSignalMap } = require('playwright/lib/server/browserType');13const { toSignalMap } = require('playwright/lib/server/browserType');14const { toSignalMap } = require('playwright/lib/server/browserType');15const { toSignalMap } = require('playwright/lib/server/browserType');
Using AI Code Generation
1const { toSignalMap } = require('playwright/lib/server/supplements/recorder/recorderSupplement');2const { signals } = require('playwright/lib/server/supplements/recorder/signals');3const { js } = require('playwright/lib/server/supplements/recorder/js');4const { signalsToJs } = require('playwright/lib/server/supplements/recorder/signalsToJs');5const signalMap = toSignalMap(signals);6const jsMap = js(signalMap);7const signalsToJsMap = signalsToJs(jsMap);8console.log(signalsToJsMap);9{10 'page.click': 'page.click("text=Get Started");',11 'page.click #2': 'page.click("text=Docs");',12 'page.click #3': 'page.click("text=API");',13 'page.click #4': 'page.click("text=Playwright");',14 'page.click #5': 'page.click("text=Playwright");',15 'page.click #6': 'page.click("text=Playwright");',16 'page.click #7': 'page.click("text=Playwright");',17 'page.click #8': 'page.click("text=Playwright");',18 'page.click #9': 'page.click("text=Playwright");',19 'page.click #10': 'page.click("text=Playwright");',20 'page.click #11': 'page.click("text=Playwright");',21 'page.click #12': 'page.click("text=Playwright");',22 'page.click #13': 'page.click("text=Playwright");',23 'page.click #14': 'page.click("text=Playwright");',24 'page.click #15': 'page.click("text=Playwright");',25 'page.click #16': 'page.click("text=Playwright");',26 'page.click #17': 'page.click("text=Playwright");',27 'page.click #18': 'page.click("text=Playwright");',28 'page.click #19': 'page.click("text=Playwright");',29 'page.click #20': 'page.click("text=Playwright");',30 'page.click #21': 'page.click("text=Playwright");',
Using AI Code Generation
1const { toSignalMap } = require('playwright-core/lib/server/supplements/recorder/recorderSupplement.js');2const signalMap = toSignalMap('click', 'div');3const { toSignalMap } = require('@playwright/test/lib/server/supplements/recorder/recorderSupplement.js');4const signalMap = toSignalMap('click', 'div');5const { toSignalMap } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');6const signalMap = toSignalMap('click', 'div');7const { toSignalMap } = require('@playwright/test/lib/server/supplements/recorder/recorderSupplement.js');8const signalMap = toSignalMap('click', 'div');9const { toSignalMap } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');10const signalMap = toSignalMap('click', 'div');11import { toSignalMap } from '@playwright/test/lib/server/supplements/recorder/recorderSupplement.js';12const signalMap = toSignalMap('click', 'div');13import { toSignalMap } from 'playwright/lib/server/supplements/recorder/recorderSupplement.js';14const signalMap = toSignalMap('click', 'div');15import { toSignalMap } from '@playwright/test/lib/server/supplements/recorder/recorderSupplement.js';16const signalMap = toSignalMap('click', 'div');17import { toSignalMap } from 'playwright/lib/server/supplements/recorder/recorderSupplement.js';18const signalMap = toSignalMap('click', 'div');19import { toSignalMap
Using AI Code Generation
1const { Page } = require ( 'playwright' ) ;2module . exports = async function ( page ) {3let map = await page . _delegate . toSignalMap ( ) ;4console . log ( map ) ;5} ;6const { chromium } = require ( 'playwright' ) ;7const test = require ( './test.js' ) ;8describe ( 'test' , ( ) => {9it ( 'should work' , async ( ) => {10const browser = await chromium . launch ( ) ;11const page = await browser . newPage ( ) ;12await test ( page ) ;13await browser . close ( ) ;14} ) ;15} ) ;16at Object . exports . test ( C:\Users\shubham\Documents\Playwright\test.js: 6 : 25 )17at processTicksAndRejections ( internal / process / task_queues . js : 93 : 5 )18at async Context . it ( C:\Users\shubham\Documents\Playwright\test.spec.js: 8 : 5 )
Jest + Playwright - Test callbacks of event-based DOM library
firefox browser does not start in playwright
Is it possible to get the selector from a locator object in playwright?
How to run a list of test suites in a single file concurrently in jest?
Running Playwright in Azure Function
firefox browser does not start in playwright
This question is quite close to a "need more focus" question. But let's try to give it some focus:
Does Playwright has access to the cPicker object on the page? Does it has access to the window object?
Yes, you can access both cPicker and the window object inside an evaluate call.
Should I trigger the events from the HTML file itself, and in the callbacks, print in the DOM the result, in some dummy-element, and then infer from that dummy element text that the callbacks fired?
Exactly, or you can assign values to a javascript variable:
const cPicker = new ColorPicker({
onClickOutside(e){
},
onInput(color){
window['color'] = color;
},
onChange(color){
window['result'] = color;
}
})
And then
it('Should call all callbacks with correct arguments', async() => {
await page.goto(`http://localhost:5000/tests/visual/basic.html`, {waitUntil:'load'})
// Wait until the next frame
await page.evaluate(() => new Promise(requestAnimationFrame))
// Act
// Assert
const result = await page.evaluate(() => window['color']);
// Check the value
})
Check out the latest blogs from LambdaTest on this topic:
Native apps are developed specifically for one platform. Hence they are fast and deliver superior performance. They can be downloaded from various app stores and are not accessible through browsers.
One of the essential parts when performing automated UI testing, whether using Selenium or another framework, is identifying the correct web elements the tests will interact with. However, if the web elements are not located correctly, you might get NoSuchElementException in Selenium. This would cause a false negative result because we won’t get to the actual functionality check. Instead, our test will fail simply because it failed to interact with the correct element.
Smartphones have changed the way humans interact with technology. Be it travel, fitness, lifestyle, video games, or even services, it’s all just a few touches away (quite literally so). We only need to look at the growing throngs of smartphone or tablet users vs. desktop users to grasp this reality.
As part of one of my consulting efforts, I worked with a mid-sized company that was looking to move toward a more agile manner of developing software. As with any shift in work style, there is some bewilderment and, for some, considerable anxiety. People are being challenged to leave their comfort zones and embrace a continuously changing, dynamic working environment. And, dare I say it, testing may be the most ‘disturbed’ of the software roles in agile development.
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!