2021-09-09 19:34:54 +03:00
|
|
|
|
---
|
2021-09-10 20:40:36 +03:00
|
|
|
|
id: protractor
|
2021-09-09 19:34:54 +03:00
|
|
|
|
title: "Migrating from Protractor"
|
|
|
|
|
---
|
|
|
|
|
|
2022-05-16 06:34:37 +03:00
|
|
|
|
## Migration Principles
|
2021-09-09 19:34:54 +03:00
|
|
|
|
|
|
|
|
|
- No need for "webdriver-manager" / Selenium.
|
|
|
|
|
- Protractor’s [ElementFinder] ⇄ [Playwright Test Locator](./api/class-locator)
|
|
|
|
|
- Protractor’s [`waitForAngular`] ⇄ Playwright Test [auto-waiting](./actionability.md)
|
|
|
|
|
- Don’t forget to await in Playwright Test
|
|
|
|
|
|
|
|
|
|
## Cheat Sheet
|
|
|
|
|
|
|
|
|
|
| Protractor | Playwright Test |
|
|
|
|
|
|---------------------------------------------------|-----------------------------------------|
|
2021-10-11 20:20:51 +03:00
|
|
|
|
| `element(by.buttonText('...'))` | `page.locator('button, input[type="button"], input[type="submit"] >> text="..."')` |
|
2021-09-09 19:34:54 +03:00
|
|
|
|
| `element(by.css('...'))` | `page.locator('...')` |
|
2021-10-11 20:20:51 +03:00
|
|
|
|
| `element(by.cssContainingText('..1..', '..2..'))` | `page.locator('..1.. >> text=..2..')` |
|
|
|
|
|
| `element(by.id('...'))` | `page.locator('#...')` |
|
|
|
|
|
| `element(by.model('...'))` | `page.locator('[ng-model="..."]')` |
|
2021-09-09 19:34:54 +03:00
|
|
|
|
| `element(by.repeater('...'))` | `page.locator('[ng-repeat="..."]')` |
|
2021-10-11 20:20:51 +03:00
|
|
|
|
| `element(by.xpath('...'))` | `page.locator('xpath=...')` |
|
2021-09-09 19:34:54 +03:00
|
|
|
|
| `element.all` | `page.locator` |
|
|
|
|
|
| `browser.get(url)` | `await page.goto(url)` |
|
|
|
|
|
| `browser.getCurrentUrl()` | `page.url()` |
|
|
|
|
|
|
|
|
|
|
## Example
|
|
|
|
|
|
|
|
|
|
Protractor:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
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();
|
|
|
|
|
|
2023-06-27 12:53:53 +03:00
|
|
|
|
const todoList = element.all(by.repeater('todo in todoList.todos'));
|
2021-09-09 19:34:54 +03:00
|
|
|
|
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();
|
2023-06-27 12:53:53 +03:00
|
|
|
|
const completedAmount = element.all(by.css('.done-true'));
|
2021-09-09 19:34:54 +03:00
|
|
|
|
expect(completedAmount.count()).toEqual(2);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Line-by-line migration to Playwright Test:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const { test, expect } = require('@playwright/test'); // 1
|
|
|
|
|
|
2023-06-27 12:53:53 +03:00
|
|
|
|
test.describe('angularjs homepage todo list', () => {
|
|
|
|
|
test('should add a todo', async ({ page }) => { // 2, 3
|
2021-09-09 19:34:54 +03:00
|
|
|
|
await page.goto('https://angularjs.org'); // 4
|
|
|
|
|
|
|
|
|
|
await page.locator('[ng-model="todoList.todoText"]').fill('first test');
|
|
|
|
|
await page.locator('[value="add"]').click();
|
|
|
|
|
|
2023-06-27 12:53:53 +03:00
|
|
|
|
const todoList = page.locator('[ng-repeat="todo in todoList.todos"]'); // 5
|
2021-09-09 19:34:54 +03:00
|
|
|
|
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
|
2022-10-04 03:02:46 +03:00
|
|
|
|
await todoList.nth(2).getByRole('textbox').click();
|
2023-06-27 12:53:53 +03:00
|
|
|
|
const completedAmount = page.locator('.done-true');
|
2021-09-09 19:34:54 +03:00
|
|
|
|
await expect(completedAmount).toHaveCount(2);
|
|
|
|
|
});
|
2023-06-27 12:53:53 +03:00
|
|
|
|
});
|
2021-09-09 19:34:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Migration highlights (see inline comments in the Playwright Test code snippet):
|
|
|
|
|
|
|
|
|
|
1. Each Playwright Test file has explicit import of the `test` and `expect` functions
|
2021-09-13 13:06:47 +03:00
|
|
|
|
1. Test function is marked with `async`
|
|
|
|
|
1. Playwright Test is given a `page` as one of its parameters. This is one of the many [useful fixtures](./api/class-fixtures) in Playwright Test.
|
|
|
|
|
1. Almost all Playwright calls are prefixed with `await`
|
|
|
|
|
1. Locator creation with [`method: Page.locator`] is one of the few methods that is sync.
|
2021-09-09 19:34:54 +03:00
|
|
|
|
|
|
|
|
|
## Polyfilling `waitForAngular`
|
|
|
|
|
|
|
|
|
|
Playwright Test has built-in [auto-waiting](./actionability.md) that makes protractor's [`waitForAngular`] unneeded in general case.
|
|
|
|
|
|
|
|
|
|
However, it might come handy in some edge cases.
|
|
|
|
|
Here's how to polyfill `waitForAngular` function in Playwright Test:
|
|
|
|
|
|
|
|
|
|
1. Make sure you have protractor installed in your package.json
|
2021-09-13 13:06:47 +03:00
|
|
|
|
1. Polyfill function
|
2021-09-09 19:34:54 +03:00
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
async function waitForAngular(page) {
|
2023-06-27 15:39:44 +03:00
|
|
|
|
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, '');
|
2021-09-09 19:34:54 +03:00
|
|
|
|
}
|
|
|
|
|
```
|
2021-12-10 23:00:21 +03:00
|
|
|
|
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+):
|
|
|
|
|
```js
|
|
|
|
|
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) {
|
2023-06-27 12:53:53 +03:00
|
|
|
|
return new Promise(res => testability.whenStable(res));
|
2021-12-10 23:00:21 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-06-27 15:39:44 +03:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
```
|
2021-09-13 13:06:47 +03:00
|
|
|
|
1. Polyfill usage
|
2021-09-09 19:34:54 +03:00
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const page = await context.newPage();
|
|
|
|
|
await page.goto('https://example.org');
|
|
|
|
|
await waitForAngular(page);
|
|
|
|
|
```
|
2022-09-12 18:41:49 +03:00
|
|
|
|
|
2021-09-09 19:34:54 +03:00
|
|
|
|
## Playwright Test Super Powers
|
|
|
|
|
|
|
|
|
|
Once you're on Playwright Test, you get a lot!
|
|
|
|
|
|
|
|
|
|
- Full zero-configuration TypeScript support
|
2022-09-12 18:41:49 +03:00
|
|
|
|
- Run tests across **all web engines** (Chrome, Firefox, Safari) on **any popular operating system** (Windows, macOS, Ubuntu)
|
2021-11-09 18:42:04 +03:00
|
|
|
|
- Full support for multiple origins, [(i)frames](./api/class-frame), [tabs and contexts](./pages)
|
2021-09-09 19:34:54 +03:00
|
|
|
|
- Run tests in parallel across multiple browsers
|
2023-07-27 02:15:07 +03:00
|
|
|
|
- Built-in test [artifact collection](./test-use-options.md#recording-options)
|
2021-09-09 19:34:54 +03:00
|
|
|
|
|
2022-09-12 18:41:49 +03:00
|
|
|
|
You also get all these ✨ awesome tools ✨ that come bundled with Playwright Test:
|
2022-07-27 14:12:01 +03:00
|
|
|
|
- [Playwright Inspector](./debug.md)
|
2023-07-27 02:15:07 +03:00
|
|
|
|
- [Playwright Test Code generation](./codegen-intro.md)
|
|
|
|
|
- [Playwright Tracing](./trace-viewer.md) for post-mortem debugging
|
2021-09-09 19:34:54 +03:00
|
|
|
|
|
|
|
|
|
## Further Reading
|
|
|
|
|
|
|
|
|
|
Learn more about Playwright Test runner:
|
|
|
|
|
|
|
|
|
|
- [Getting Started](./intro)
|
|
|
|
|
- [Fixtures](./test-fixtures)
|
2022-12-03 08:48:37 +03:00
|
|
|
|
- [Locators](./locators)
|
2021-09-09 19:34:54 +03:00
|
|
|
|
- [Assertions](./test-assertions)
|
|
|
|
|
- [Auto-waiting](./actionability)
|
|
|
|
|
|
|
|
|
|
[ElementFinder]: https://www.protractortest.org/#/api?view=ElementFinder
|
|
|
|
|
[`waitForAngular`]: https://www.protractortest.org/#/api?view=ProtractorBrowser.prototype.waitForAngular
|