从 Protractor 迁移
迁移原则
¥Migration Principles
-
不需要 "webdriver-manager"/硒。
¥No need for "webdriver-manager" / Selenium.
-
量角器 ElementFinder ⇄ Playwright 测试定位器
¥Protractor’s ElementFinder ⇄ Playwright Test Locator
-
Protractor
waitForAngular
⇄ Playwright 测试 auto-waiting¥Protractor’s
waitForAngular
⇄ Playwright Test auto-waiting -
不要忘记等待 Playwright 测试
¥Don’t forget to await in Playwright Test
备忘单
¥Cheat Sheet
Protractor | Playwright 测试 |
---|---|
element(by.buttonText('...')) | page.locator('button, input[type="button"], input[type="submit"] >> text="..."') |
element(by.css('...')) | page.locator('...') |
element(by.cssContainingText('..1..', '..2..')) | page.locator('..1.. >> text=..2..') |
element(by.id('...')) | page.locator('#...') |
element(by.model('...')) | page.locator('[ng-model="..."]') |
element(by.repeater('...')) | page.locator('[ng-repeat="..."]') |
element(by.xpath('...')) | page.locator('xpath=...') |
element.all | page.locator |
browser.get(url) | await page.goto(url) |
browser.getCurrentUrl() | page.url() |
示例
¥Example
Protractor:
describe('angularjs homepage todo list', function() {
it('should add a todo', function() {
browser.get('https://angularjs.org');
element(by.model('todoList.todoText')).sendKeys('first test');
element(by.css('[value="add"]')).click();
const todoList = element.all(by.repeater('todo in todoList.todos'));
expect(todoList.count()).toEqual(3);
expect(todoList.get(2).getText()).toEqual('first test');
// You wrote your first test, cross it off the list
todoList.get(2).element(by.css('input')).click();
const completedAmount = element.all(by.css('.done-true'));
expect(completedAmount.count()).toEqual(2);
});
});
逐行迁移到 Playwright 测试:
¥Line-by-line migration to Playwright Test:
const { test, expect } = require('@playwright/test'); // 1
test.describe('angularjs homepage todo list', () => {
test('should add a todo', async ({ page }) => { // 2, 3
await page.goto('https://angularjs.org'); // 4
await page.locator('[ng-model="todoList.todoText"]').fill('first test');
await page.locator('[value="add"]').click();
const todoList = page.locator('[ng-repeat="todo in todoList.todos"]'); // 5
await expect(todoList).toHaveCount(3);
await expect(todoList.nth(2)).toHaveText('first test', {
useInnerText: true,
});
// You wrote your first test, cross it off the list
await todoList.nth(2).getByRole('textbox').click();
const completedAmount = page.locator('.done-true');
await expect(completedAmount).toHaveCount(2);
});
});
迁移亮点(请参阅 Playwright 测试代码片段中的内联注释):
¥Migration highlights (see inline comments in the Playwright Test code snippet):
-
每个 Playwright 测试文件都显式导入
test
和expect
函数¥Each Playwright Test file has explicit import of the
test
andexpect
functions -
测试功能标有
async
¥Test function is marked with
async
-
Playwright 测试被赋予
page
作为其参数之一。这是 Playwright 测试中众多 有用的夹具 之一。¥Playwright Test is given a
page
as one of its parameters. This is one of the many useful fixtures in Playwright Test. -
几乎所有 Playwright 调用都以
await
为前缀¥Almost all Playwright calls are prefixed with
await
-
使用 page.locator() 创建定位器是少数同步方法之一。
¥Locator creation with page.locator() is one of the few methods that is sync.
填充 waitForAngular
¥Polyfilling waitForAngular
Playwright Test 内置了 auto-waiting,一般情况下不需要 Protractor 的 waitForAngular
。
¥Playwright Test has built-in auto-waiting that makes protractor's waitForAngular
unneeded in general case.
然而,在某些边缘情况下它可能会派上用场。以下是如何在 Playwright Test 中填充 waitForAngular
函数:
¥However, it might come handy in some edge cases. Here's how to polyfill waitForAngular
function in Playwright Test:
-
确保你的 package.json 中安装了 Protractor
¥Make sure you have protractor installed in your package.json
-
Polyfill 函数
¥Polyfill function
async function waitForAngular(page) {
const clientSideScripts = require('protractor/built/clientsidescripts.js');
async function executeScriptAsync(page, script, ...scriptArgs) {
await page.evaluate(`
new Promise((resolve, reject) => {
const callback = (errMessage) => {
if (errMessage)
reject(new Error(errMessage));
else
resolve();
};
(function() {${script}}).apply(null, [...${JSON.stringify(scriptArgs)}, callback]);
})
`);
}
await executeScriptAsync(page, clientSideScripts.waitForAngular, '');
}如果你不想保留版本 Protractor,你还可以使用此函数使用这种更简单的方法(仅适用于 Angular 2+):
¥If you don't want to keep a version protractor around, you can also use this simpler approach using this function (only works for Angular 2+):
async function waitForAngular(page) {
await page.evaluate(async () => {
// @ts-expect-error
if (window.getAllAngularTestabilities) {
// @ts-expect-error
await Promise.all(window.getAllAngularTestabilities().map(whenStable));
// @ts-expect-error
async function whenStable(testability) {
return new Promise(res => testability.whenStable(res));
}
}
});
} -
Polyfill 的使用
¥Polyfill usage
const page = await context.newPage();
await page.goto('https://example.org');
await waitForAngular(page);
Playwright 测试超能力
¥Playwright Test Super Powers
一旦参加 Playwright 测试,你就会收获很多!
¥Once you're on Playwright Test, you get a lot!
-
完全零配置 TypeScript 支持
¥Full zero-configuration TypeScript support
-
在任何流行操作系统(Windows、macOS、Ubuntu)上跨所有 Web 引擎(Chrome、Firefox、Safari)运行测试
¥Run tests across all web engines (Chrome, Firefox, Safari) on any popular operating system (Windows, macOS, Ubuntu)
-
¥Full support for multiple origins, (i)frames, tabs and contexts
-
跨多个浏览器并行运行测试
¥Run tests in parallel across multiple browsers
-
内置测试 制品集合
¥Built-in test artifact collection
你还可以获得所有这些 ✨ 很棒的工具 ✨ 与 Playwright 测试打包在一起:
¥You also get all these ✨ awesome tools ✨ that come bundled with Playwright Test:
-
Playwright 追踪 用于事后调试
¥Playwright Tracing for post-mortem debugging
进一步阅读
¥Further Reading
了解有关 Playwright 测试运行程序的更多信息:
¥Learn more about Playwright Test runner: