Why do we need end-to-end tests?
They control the browser and simulate user actions. For example, I have described custom scripts and want them to be tested with each version of the product. Checking all scripts for all versions manually is more expensive and longer than automatic. There are different tools: Selenium, Puppeteer, Protractor, Cypress and others. Two months ago, a new tool was released – Playwright, which was worked on by Andrey Lushnikov, the developer of Puppeteer. This library completely solves the problem of writing cross-browser tests.
Puppeteer vs. Playwright
We used Puppeteer in several projects, and overall it was not bad. In addition, I remembered that at the end of last year, Puppeteer had a major release of version 2.0.
I wondered why Playwright is better – a new tool that neatly rests on GitHub in a Microsoft organization.
Why is Puppeteer good? It launches a headless browser and uses the DevTools protocol, so the tests are faster and more stable than Selenium automation, and you can write them in pleasant JavaScript. What is good about Playwright? Firstly, it has all the Puppeteer buns. And secondly, it allows you to write cross-browser tests – you can select any browser in the test code: WebKit, Chromium and Firefox.
What does it take to write a cross-browser test on Playwright? I needed npm, a cup of coffee, and 15 and a half minutes.
npm i playwright
const playwright = require('playwright');
(async () => {
for (const browserType of ['chromium', 'firefox', 'webkit']) {
const browser = await playwright[browserType].launch();
const context = await browser.newContext();
const page = await context.newPage('https://elmosoft.net/');
await page.screenshot({ path:
`screenshots/example-${browserType}.png` });
await browser.close();
}
})();
Pupeeter’s code was almost the same:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://elmosoft.net');
await page.screenshot({path: 'screenshots/example.png'});
await browser.close();
})();
The test from the first example will be run in three browsers. True, it is unclear in which particular versions and which are generally available. The documentation says that Playwright will automatically download the driver for the browser. You can also specify a specific browser:
const browser = await chromium.launch(); // Or 'firefox' or 'webkit'.
const context = await browser.newContext();
To select the browser version in Puppeteer, you need to specify the path to the driver. There is no such possibility in PlayWright. As I understand it, the latest version of the engine will always be used, which is already being discussed on GitHub.
Playwright supports WebKit, which means tests in mobile browsers. But all smartphones and tablets have different resolutions – how to choose the ones you need when running tests? I found 76 devices in the deviceDescriptors.ts code, including the popular BlackBerry and JioPhone 2 models. From the useful: you can choose iPhone X and iPhone XS in landscape mode. The test code is almost the same as the test for a regular browser:
const { webkit, devices } = require('playwright');
const deviceType = devices['iPhone 8'];
(async () => {
const browser = await webkit.launch();
const context = await browser.newContext({
viewport: deviceType.viewport,
userAgent: deviceType.userAgent
});
const page = await context.newPage('https://elmosoft.net');
await page.screenshot({ path: `example-${deviceType.name}.png`});
await browser.close();
})();
An interesting feature of Playwright is the ability to change geolocation, it will come in handy when testing maps. When initializing the browser context, you need to specify the coordinates:
const context = await browser.newContext({
viewport: iPhone11.viewport,
userAgent: iPhone11.userAgent,
geolocation: { longitude: 12.492507, latitude: 41.889938 },
permissions: { 'https://google.com/maps': ['geolocation'] }
});
playwright HtML Report
npm init
npm i -D playwright
npm install --save-dev jest
npm install -D jest-playwright-preset
npm install jest-html-reporters --save-dev
Next Step we need to create PageObject:
const { DashboardPage } = require("./DashboardPage");
var config = require('../config');
class SignInPage {
constructor(page) {
this.page = page;
}
async openSignInPage() {
await this.page.goto(config.web.url);
}
async signInAs(user) {
await this.page.fill("css=#form-username", user.username);
await this.page.fill("css=#form-password", user.password);
await this.page.click("css=button[type='submit']");
return new DashboardPage(this.page);
}
}
module.exports = { SignInPage };
class DashboardPage {
constructor(page) {
this.page = page;
}
}
module.exports = { DashboardPage };
Full test should looks like:
const { chromium } = require("playwright");
const { SignInPage } = require("../pageobjectmodels/SignInPage");
const { roles } = require("../enums/roles");
const assert = require("assert");
var config = require("../config");
let browser;
let page;
beforeAll(async () => {
console.log("headless : " + config.web.headless);
console.log("sloMo : " + config.web.sloMo);
browser = await chromium.launch({
headless: config.web.headless == "true",
slowMo: parseInt(config.web.sloMo, 10),
});
});
afterAll(async () => {
await browser.close();
});
beforeEach(async () => {
page = await browser.newPage();
if (config.web.networkSubscription) {
page.on("request", (request) =>
console.log(">>", request.method(), request.url())
);
page.on("response", (response) =>
console.log("<<", response.status(), response.url())
);
}
});
afterEach(async () => {
await page.close();
});
test("An admin is able to see a dashboard", async () => {
const signInPage = new SignInPage(page);
await signInPage.openSignInPage();
const dashboardPage = await signInPage.signInAs(roles.ADMIN);
const dashboard = await dashboardPage.page.$("#dashboard");
assert(dashboard);
});
The linebrowser = await chromium.launch({headless: config.web.headless == "true",
slowMo: parseInt(config.web.sloMo, 10),});
Allows you to configure headless mode and delay.
You can now use the following commands to run tests:
Is it time to move?
It seems that the developers of Playwright are ready to quickly add features and fix bugs. For example, downloading files does not work right now, but developers write that they will fix it and it will be no worse than Puppeteer’s.
If you want to port tests to Playwright, be prepared to start create tickets. It seems that the Playwright team wants to put together all the best practices in this library. In GitHub tickets, I found several ideas for improving the API – for example, adding features like in Cypress.