Skip to main content

从 Protractor 迁移

迁移原则

Migration Principles

备忘单

Cheat Sheet

ProtractorPlaywright 测试
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.allpage.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):

  1. 每个 Playwright 测试文件都显式导入 testexpect 函数

    Each Playwright Test file has explicit import of the test and expect functions

  2. 测试功能标有 async

    Test function is marked with async

  3. 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.

  4. 几乎所有 Playwright 调用都以 await 为前缀

    Almost all Playwright calls are prefixed with await

  5. 使用 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:

  1. 确保你的 package.json 中安装了 Protractor

    Make sure you have protractor installed in your package.json

  2. 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));
    }
    }
    });
    }
  3. 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)

  • 全面支持多来源、(i)frames选项卡和上下文

    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:

进一步阅读

Further Reading

了解有关 Playwright 测试运行程序的更多信息:

Learn more about Playwright Test runner: