Migrating from Protractor to Playwright

Migrating from Protractor to Playwright

Implementing the new framework in playwright will be easy things to do. But the challenges come to the picture when the outdated framework from Protractor need to be re-written or we can say need to be migrated to playwright.

 

There are two ways that we can migrate from existing protractor framework to playwright

1>   We need to go through each page object and test file and need to re-write in playwright format.

2>   The existing protractor script can be migrated to new the playwright framework.

 

Let’s start with 1st approach:-

1.1>        Create class block under that create the variables for each locators.

1.2>        Create constructor for the class and pass the page object into that.

1.3>        Replace the locator (css , xpath etc ) to page.locator and pass the locator value within the double or single quotes.

1.4>        Call the variable into the test file by creating the object reference of the page file.

Conclusion :- its very time taking job to convert each locator type playwright format as each and every lines need to be updated and on top of that need to create the constructor for each class in every page object files.

 

Start with 2nd Approach :-

Here we will be migrating the existing protractor script into Playwright script

Migration Principles :-

Cheat Sheet

No alt text provided for this image

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();

    var 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();
    var completedAmount = element.all(by.css('.done-true'));
    expect(completedAmount.count()).toEqual(2);
  });
});
        


Line-by-line migration to Playwright Test:


test.describe('angularjs homepage todo list', function() 
  test('should add a todo', async function({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();

    var 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();
    var completedAmount = page.locator('.done-true');
    await expect(completedAmount).toHaveCount(2);
  });
}        

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
  2. Test function is marked with async
  3. Playwright Test is given a page as one of its parameters. This is one of the many useful fixtures in Playwright Test.
  4. Almost all Playwright calls are prefixed with await
  5. Locator creation with page.locator(selector[, options]) is one of the few methods that is sync.

Commands

The migration can be done directly by executing the following command in the e2e root folder:

npx @amadeus-it-group/protractor-to-playwright@latest --dst='relative/path/to/e2e-playwright-folder' --tsconfig='relative/path/to/tsconfig.conf.ts'        

Basically, it takes these arguments:

  • cwd: Current directory to consider.Current directory by default,
  • src: Root folder of the e2e protractor tests. Current directory by default,
  • dst: Destination folder of the converted files. By default, the files are replaced at the same location,
  • tsconfig: tsconfig file, relative to the current directory. Whereas this parameter is not mandatory, it is advised to fill it if your e2e project is setup with a tsconfig.
  • file: optional glob pattern of the files to convert. By default '**/*.{ts,js}'.
  • test: optional parameter to convert the it keywords to test or test.step. You can prefer to use the steps if your successive tests in your files are not independant. It can take the following values:
  • test (default): 'it' are replaced by 'test', the {page} parameter is inserted and the setPage(page) is called at the first line to save the page globally.
  • step: 'it' are replaced by 'await test.step'. You will have to finish the conversion by wrapping your test.step in a test.

You can get the help on the usage with npx @amadeus-it-group/protractor-to-playwright@latest –help

Architecture

A file 'playwright-utils' is added at the root of destination folder. It contains useful functions to help the conversion.

In protractor, there is a notion of current page from where you can do actions. In Playwright, this page is given in the callback function parameter of a test. In order to reproduce the Protractor behaviour, the setPage function (of playwright-utils) must be used at the start of each test to store the current page, which can be retrieved at any time with the getPage function. The test object of playwright-utils must be used as well, in order to clear the current page at the end of the test.

Migration strategy

You can adopt different migration strategies depending on your project. This is an example of how it can be done:

  • Create a new branch for the migration,
  • Create the e2e-playwright folder, and setup a Playwright configuration.
  • Go to your project folder and open a command line,
  • Run npx @amadeus-it-group/protractor-to-playwright@latest --src=e2e --dst='e2e-playwright' --tsconfig='e2e/tsconfig.conf.ts' (e2e contains the protractor suite, e2e-playwright the folder for Playwright, e2e/tsconfig.conf.ts is the tsconfig for your protractor tests),
  • Check the results:
  • Check the console output (also saved in playwright-migration.log) to see some not transformed code.
  • Check the Typescript errors of the new files.
  • Check the work to be finished on your side (see the next section for more explanation).

The new code will not work directly and will require manual changes. At this point, either you detect that something can be improved in the tool, so you can contact us or open a PR, or the migration can be finished on your side in a reasonable time. At any time, you can use the 'file' parameter to migrate only a set of files.

At this point you can either wait for a new release with fixes or finish the work on your side (and you will not need the migration tool anymore

Code that will need a review/refactor by the application team

  • The test functions (replacement of 'it') are run with isolation, meaning that a new browser page is recreated for each test. After the conversion, you should check that:
  • beforeAll and afterAll would probably need to be converted in beforeEach and afterEach.
  • Your tests are independent. If it's not the case, they will need to be converted in 'test.step' inside a test (you can use the test parameter to convert them globally, if it's easier to finish the conversion).

For example, after the migration, you can have:


test('test 1', async({page}) => 

        ...

    });

 

    test('test 2', async({page}) => {

        // Test 2 depends on the actions done in test 1

        ...

    });        

As test 2 depends on test 1, you will need to convert the code this way:

import { setPage } from "../../playwright-utils"; // Path to be adapte

    ...

    test('Global description', async ({page}) => {

        setPage(page);

 

        await test.step('test 1', async () => {

            ...

        });

 

        await test.step('test 2', async () => {

            // Test 2 depends on the actions done in test 1

            ...

        });

    });        

  • ElementArrayFinder.filter : This function returns a ElementArrayFinder, which can be chained with usual protractor methods. It will probably need to be refactored.
  • ExpectedConditions : even if a lot of these utility methods have been translated, the code would probably not need it and be refactored.

For example, in the following code:

   const button = this.page.locator('#buttonId');

   await browser.wait(ExpectedConditions.presenceOf(button));

   await button.click();

The line browser.wait is not needed at all, as Playwright provides all this waiting actions in the button.click.

Or:

   await browser.wait(ExpectedConditions.titleIs('My title'));

Must be replaced by:

   await expect(page).toHaveTitle('My title')

  • ExpectedConditions.alertIsPresent : alerts are managed with events in playwright. Need to be done differently.
  • ExpectedConditions.elementToBeClickable : usually useless, as Playwright manage it internally.
  • by.model(...) and by.repeater(...) are converted into [ng-model="..."] and [ng-repeat*="..."] selectors, but the logic in Protractor is more complex (see here for ng-model and here for ng-repeat). You'll have to adapt them if needed.

Do you working example for this? Mentioned utility is quite outdated & not working.

Like
Reply

To view or add a comment, sign in

More articles by Rohit Kumar

Others also viewed

Explore content categories