PageSourceSearch

https://playwright.dev/assets/js/252b8cc0.5d219c3d.js

js playwright.dev collected 2026-09-24 06:55:56 UTC 12,362 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkplaywright_dev=self.webpackChunkplaywright_dev||[]).push([["4583"],{30499(e,t,r){r.r(t),r.d(t,{metadata:()=>n,default:()=>h,frontMatter:()=>l,contentTitle:()=>o,toc:()=>c,assets:()=>a});var n=JSON.parse('{"id":"protractor","title":"Migrating from Protractor","description":"Migration Principles","source":"@site/docs/protractor.mdx","sourceDirName":".","slug":"/protractor","permalink":"/docs/next/protractor","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"id":"protractor","title":"Migrating from Protractor"},"sidebar":"docs","previous":{"title":"WebView2","permalink":"/docs/next/webview2"},"next":{"title":"Migrating from Puppeteer","permalink":"/docs/next/puppeteer"}}'),i=r(74848),s=r(28453);r(13554),r(41647),r(83137);let l={id:"protractor",title:"Migrating from Protractor"},o,a={},c=[{value:"Migration Principles",id:"migration-principles",level:2},{value:"Cheat Sheet",id:"cheat-sheet",level:2},{value:"Example",id:"example",level:2},{value:"Polyfilling <code>waitForAngular</code>",id:"polyfilling-waitforangular",level:2},{value:"Playwright Test Super Powers",id:"playwright-test-super-powers",level:2},{value:"Further Reading",id:"further-reading",level:2}];function d(e){let t={a:"a",code:"code",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.h2,{id:"migration-principles",children:"Migration Principles"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:'No need for "webdriver-manager" / Selenium.'}),"\n",(0,i.jsxs)(t.li,{children:["Protractor\u2019s ",(0,i.jsx)(t.a,{href:"https://www.protractortest.org/#/api?view=ElementFinder",children:"ElementFinder"})," \u21C4 ",(0,i.jsx)(t.a,{href:"./api/class-locator",children:"Playwright Test Locator"})]}),"\n",(0,i.jsxs)(t.li,{children:["Protractor\u2019s ",(0,i.jsx)(t.a,{href:"https://www.protractortest.org/#/api?view=ProtractorBrowser.prototype.waitForAngular",children:(0,i.jsx)(t.code,{children:"waitForAngular"})})," \u21C4 Playwright Test ",(0,i.jsx)(t.a,{href:"/docs/next/actionability",children:"auto-waiting"})]}),"\n",(0,i.jsx)(t.li,{children:"Don\u2019t forget to await in Playwright Test"}),"\n"]}),"\n",(0,i.jsx)(t.h2,{id:"cheat-sheet",children:"Cheat Sheet"}),"\n",(0,i.jsxs)(t.table,{children:[(0,i.jsx)(t.thead,{children:(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.th,{children:"Protractor"}),(0,i.jsx)(t.th,{children:"Playwright Test"})]})}),(0,i.jsxs)(t.tbody,{children:[(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"element(by.buttonText('...'))"})}),(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:'page.locator(\'button, input[type="button"], input[type="submit"] >> text="..."\')'})})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"element(by.css('...'))"})}),(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"page.locator('...')"})})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"element(by.cssContainingText('..1..', '..2..'))"})}),(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"page.locator('..1.. >> text=..2..')"})})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"element(by.id('...'))"})}),(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"page.locator('#...')"})})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"element(by.model('...'))"})}),(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"page.locator('[ng-model=\"...\"]')"})})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"element(by.repeater('...'))"})}),(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"page.locator('[ng-repeat=\"...\"]')"})})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"element(by.xpath('...'))"})}),(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"page.locator('xpath=...')"})})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"element.all"})}),(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"page.locator"})})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"browser.get(url)"})}),(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"await page.goto(url)"})})]}),(0,i.jsxs)(t.tr,{children:[(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"browser.getCurrentUrl()"})}),(0,i.jsx)(t.td,{children:(0,i.jsx)(t.code,{children:"page.url()"})})]})]})]}),"\n",(0,i.jsx)(t.h2,{id:"example",children:"Example"}),"\n",(0,i.jsx)(t.p,{children:"Protractor:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-js",children:"describe('angularjs homepage todo list', function() {\
1n  it('should add a todo', function() {\n    browser.get('https://angularjs.org');\n\n    element(by.model('todoList.todoText')).sendKeys('first test');\n    element(by.css('[value=\"add\"]')).click();\n\n    const todoList = element.all(by.repeater('todo in todoList.todos'));\n    expect(todoList.count()).toEqual(3);\n    expect(todoList.get(2).getText()).toEqual('first test');\n\n    // You wrote your first test, cross it off the list\n    todoList.get(2).element(by.css('input')).click();\n    const completedAmount = element.all(by.css('.done-true'));\n    expect(completedAmount.count()).toEqual(2);\n  });\n});\n"})}),"\n",(0,i.jsx)(t.p,{children:"Line-by-line migration to Playwright Test:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-js",children:"const { test, expect } = require('@playwright/test'); // 1\n\ntest.describe('angularjs homepage todo list', () => {\n  test('should add a todo', async ({ page }) => { // 2, 3\n    await page.goto('https://angularjs.org'); // 4\n\n    await page.locator('[ng-model=\"todoList.todoText\"]').fill('first test');\n    await page.locator('[value=\"add\"]').click();\n\n    const todoList = page.locator('[ng-repeat=\"todo in todoList.todos\"]'); // 5\n    await expect(todoList).toHaveCount(3);\n    await expect(todoList.nth(2)).toHaveText('first test', {\n      useInnerText: true,\n    });\n\n    // You wrote your first test, cross it off the list\n    await todoList.nth(2).getByRole('textbox').click();\n    const completedAmount = page.locator('.done-true');\n    await expect(completedAmount).toHaveCount(2);\n  });\n});\n"})}),"\n",(0,i.jsx)(t.p,{children:"Migration highlights (see inline comments in the Playwright Test code snippet):"}),"\n",(0,i.jsxs)(t.ol,{children:["\n",(0,i.jsxs)(t.li,{children:["Each Playwright Test file has explicit import of the ",(0,i.jsx)(t.code,{children:"test"})," and ",(0,i.jsx)(t.code,{children:"expect"})," functions"]}),"\n",(0,i.jsxs)(t.li,{children:["Test function is marked with ",(0,i.jsx)(t.code,{children:"async"})]}),"\n",(0,i.jsxs)(t.li,{children:["Playwright Test is given a ",(0,i.jsx)(t.code,{children:"page"})," as one of its parameters. This is one of the many ",(0,i.jsx)(t.a,{href:"./api/class-fixtures",children:"useful fixtures"})," in Playwright Test."]}),"\n",(0,i.jsxs)(t.li,{children:["Almost all Playwright calls are prefixed with ",(0,i.jsx)(t.code,{children:"await"})]}),"\n",(0,i.jsxs)(t.li,{children:["Locator creation with ",(0,i.jsx)(t.a,{href:"/docs/next/api/class-page#page-locator",children:"page.locator()"})," is one of the few methods that is sync."]}),"\n"]}),"\n",(0,i.jsxs)(t.h2,{id:"polyfilling-waitforangular",children:["Polyfilling ",(0,i.jsx)(t.code,{children:"waitForAngular"})]}),"\n",(0,i.jsxs)(t.p,{children:["Playwright Test has built-in ",(0,i.jsx)(t.a,{href:"/docs/next/actionability",children:"auto-waiting"})," that makes protractor's ",(0,i.jsx)(t.a,{href:"https://www.protractortest.org/#/api?view=ProtractorBrowser.prototype.waitForAngular",children:(0,i.jsx)(t.code,{children:"waitForAngular"})})," unneeded in general case."]}),"\n",(0,i.jsxs)(t.p,{children:["However, it might come handy in some edge cases. Here's how to polyfill ",(0,i.jsx)(t.code,{children:"waitForAngular"})," function in Playwright Test:"]}),"\n",(0,i.jsxs)(t.ol,{children:["\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsx)(t.p,{children:"Make sure you have protractor installed in your package.json"}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsx)(t.p,{children:"Polyfill function"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-js",children:"async function waitForAngular(page) {\n  const clientSideScripts = require('protractor/built/clientsidescripts.js');\n\n  async function executeScriptAsync(page, script, ...scriptArgs) {\n    await page.evaluate(`\n      new Promise((resolve, reject) => {\n        const callback = (errMessage) => {\n          if (errMessage)\n            reject(new Error(errMessage));\n          else\n            resolve();\n        };\n        (function() {${script}}).apply(null, [...${JSON.stringify(scriptArgs)}, callback]);\n      })\n    `);\n  }\n\n  await executeScriptAsync(page, clientSideScripts.waitForAngular, '');\n}\n"})}),"\n",(0,i.jsx)(t.p,{children:"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+):"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-js",children:"async function waitForAngular(page) {\n  await page.evaluate(async () => {\n    // @ts-expect-error\n    if (window.getAllAngularTestabilities) {\n      // @ts-expect-error\n      await Promise.all(window.getAllAngularTestabilities().map(whenStable));\n      // @ts-expect-error\n      async function whenStable(testability) {\n        return new Promise(res => testability.whenStable(res));\n      }\n    }\n  });\n}\n"})}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsx)(t.p,{children:"Polyfill usage"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-js",children:"const page = await context.newPage();\nawait page.goto('https://example.org');
1\nawait waitForAngular(page);\n"})}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(t.h2,{id:"playwright-test-super-powers",children:"Playwright Test Super Powers"}),"\n",(0,i.jsx)(t.p,{children:"Once you're on Playwright Test, you get a lot!"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:"Full zero-configuration TypeScript support"}),"\n",(0,i.jsxs)(t.li,{children:["Run tests across ",(0,i.jsx)(t.strong,{children:"all web engines"})," (Chrome, Firefox, Safari) on ",(0,i.jsx)(t.strong,{children:"any popular operating system"})," (Windows, macOS, Ubuntu)"]}),"\n",(0,i.jsxs)(t.li,{children:["Full support for multiple origins, ",(0,i.jsx)(t.a,{href:"./api/class-frame",children:"(i)frames"}),", ",(0,i.jsx)(t.a,{href:"./pages",children:"tabs and contexts"})]}),"\n",(0,i.jsx)(t.li,{children:"Run tests in parallel across multiple browsers"}),"\n",(0,i.jsxs)(t.li,{children:["Built-in test ",(0,i.jsx)(t.a,{href:"/docs/next/test-use-options#recording-options",children:"artifact collection"})]}),"\n"]}),"\n",(0,i.jsx)(t.p,{children:"You also get all these \u2728 awesome tools \u2728 that come bundled with Playwright Test:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"/docs/next/debug",children:"Playwright Inspector"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"/docs/next/codegen-intro",children:"Playwright Test Code generation"})}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.a,{href:"/docs/next/trace-viewer",children:"Playwright Tracing"})," for post-mortem debugging"]}),"\n"]}),"\n",(0,i.jsx)(t.h2,{id:"further-reading",children:"Further Reading"}),"\n",(0,i.jsx)(t.p,{children:"Learn more about Playwright Test runner:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"./intro",children:"Getting Started"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"./test-fixtures",children:"Fixtures"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"./locators",children:"Locators"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"./test-assertions",children:"Assertions"})}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"./actionability",children:"Auto-waiting"})}),"\n"]})]})}function h(e={}){let{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.