停更了近 3 年,终于又有空写个小文章,关于 Playwright,一个 Microsoft 开源的跨浏览器自动化工具,类似 Puppeteer 的 Chromium + Firefox + WebKit 版。
Step by step guide to use Playwright.
Project Setup
Create a as simple as we can project manually:
1 | mkdir zhagana |
Install Playwright and TypeScript via npm, which may take some minutes to download browser binaries:
1 | npm i playwright |
TypeScript Configuration
Playwright for JavaScript and TypeScript is generally available. But we still need some configuration for TypeScript. Create a tsconfig.json
file with the following content:
1 | { |
VS Code Launcher and Debuger
Click the RUN button on the left menu then create a launch.json. Select Node.js from the drop down if you have other debugger extensions.
Make sure you have "preLaunchTask": "tsc: build - tsconfig.json"
and "outFiles": ["${workspaceFolder}/build/**/*.js"]
in launch.json.
1 | { |
Coding
Screenshot
We will start by taking a screenshot of the page. This is code from their documentation, but transfer into TypeScript
1 | import { webkit } from 'playwright' |
Press F5
to run our project, and we will get the out/whatsmyuseragent.png file like this
Now, let’s make it happen in 3 browsers:
1 | import { Browser, BrowserType, chromium, firefox, webkit } from 'playwright' |
Here we use the screenshot
function to take the place of main function and use Promise.all
to handle 3 browsers in parallel. After a few seconds, we will get 3 screenshots:
- out/ua-chromium.png with
HeadlessChrome
- out/ua-firefox.png with
Firefox
- out/ua-webkit.png with
AppleWebKit ... Safari
Emulation - Mobile Device
Next step, we will simulate browser behavior on a mobile device and navigate to Google Maps.
1 | import { Browser, BrowserType, devices, chromium, firefox, webkit } from 'playwright' |
Since firefox does not support mobile, we reduce our browsers to chromium and webkit only:
1 | (async () => { |
F5
again we will get 2 png file in out directory:
chromium | webkit |
---|---|
Maps came out, but seems not complete loaded. So we need .waitForNavigation()
after page.goto()
:
1 | await page.goto('https://www.google.com/maps'); |
But, wait… there is a blocker comes up: Google Maps want us to download App but we just want to STAY ON WEB.
Input - Mouse Click
From devtools we can get the selector of this promo: .ml-promotion-nonlu-blocking-promo
, use page.waitForSelector()
instead of page.waitForNavigation()
to catch the promotion:
1 | await page.goto('https://www.google.com/maps'); |
So let’s click the STAY ON WEB button on the page! From devtools we can also get the selector of this button: button.ml-promotion-action-button.ml-promotion-no-button
, use page.click()
to trigger the click event:
1 | // click STAY ON WEB |
As the invisible animation last for 0.3s, we need to wait for more than 300ms after button clicked, before we capture the screenshot.
1 | // wait for more than 300 millisecond for browser to response with the events |
Emulation - Geolocation
Now we have the map in our current location (may be base on IP address) but we also have the ability to simulate to a different place. We can “fly” to town Tewo by
reating a context with “geolocation” permissions granted:
1 | const context = await browser.newContext({ |
If you don’t konw the longitude and latitude of your “perfect place”, just search it in Google Maps then you can get it from the browser URL.
Click the Your Location button to navigate to our emulated geolocation.
1 | // click `your location` to navi to current location |
Re-run our project we will find us located in Tewo Post Bureau.
Input - Text Input
After these simulations, we can start to control the page with more playwright APIs, just like what we click the page just now.
First, fill in the search bar with our target place, like Zhagana.
1 | await page.click('div.ml-searchbox-button-textarea'); |
Second, press Enter
to search.
1 | // press Enter to start searching |
After that, we will get the target place with a red point, and there should be a Directions button at the bottom of the page.
Third, click Directions and google will provide us the navigation route.
1 | // click Directions |
Put them all together, with output path string as a result.
1 | async function screenshot(browserType: BrowserType<Browser>): Promise<string> { |
Okay! Here comes out the two maps screenshots:
chromium | webkit |
---|---|
Image Diff
The 2 screenshots look exactly the same, but we still want to use some tools to check. Pixelmatch is a simple and fast JavaScript pixel-level image comparison library. Create a function to compare two file A and B.
1 | async function diff(fileA: string, fileB: string) { |
And call this function after we generated the two screenshots:
1 |
|
Bingo! Google Maps did a great job in the two different browser with almost the same behavior. The only different are font weight and also the navigate route weight.
All source code can be found in Github.
Postscript
Zhagana is a wonderful place in Tiewu County, Gannan (Tibetan Autonomous Prefecture), Gansu province, China. Zhagana means “Rock Box” in Tibetan language, which is fitting as it is surrounded by large rocky spires on all sides.