Quick starter – Web automation using Playwright

Playwright is a (relatively) new kid on the block, joining several others kids already on the block, that are the JavaScript based automation frameworks.

I learnt about it from a mention on Twitter and thought to give it a whirl.

This post is purely meant to be a sketchy guide on getting started with it ( with less focus on it’s overall architecture & philosophy, however, importantly, for that start with the docs here )

Toolkit for this post –

  • JavaScript as the programming language. Playwright has a Python based library too !
  • VS Code as IDE
  • Chrome as the browser
  • OS = macOS

Installation and set up –

  • Install playwright framework using npm on your terminal –> npm i -D playwright
  • Check Node.js has been installed successfully and is in your path –> node command on your terminal should return a prompt like below. Ctrl+C twice to exit.
  • Lets create a simple .js file that we will use to write our test and run it using Node.js
  • Ensure Google Chrome’s canary build is installed at default location –> /Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary

Build code for your test –

  • Test is — Visit a contemporary e-commerce web site, browse for some items, then login user a username & password combination , but the combo is incorrect, assert on the error message displayed
  • Code pattern followed is async & await
  • Declare that you will use chromium based browser for your testing –>
  • Declare that you will use inbuilt assertions in your test –>
  • Write a snippet to launch an instance of Chrome, headful, by providing a path to the chrome canary and navigate to the webapp under test.
  • Selecting elements is a breeze and no need to write waits ! Can select elements based on text easily
  • Find our dynamic error message on a failed login ,get it’s content and perform a rudimentary assertion on it
  • Run ! –> node <file_name>.js

Putting it all together …

const { chromium } = require('playwright');
const { assert } = require('console');
(async () => {
// launch a chrome instance from the path provided , not in headless mode and with the slowMo value set for slower playback
const browser = await chromium.launch({headless: false,executablePath: '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',slowMo: 500});
// playwright has the concept of giving context to your browser object –> https://playwright.dev/#version=v1.4.0&path=docs%2Fcore-concepts.md&q=browser-contexts
const context = await browser.newContext();
// from a context spawn your page object, the primary medium to perform broswer automation
const page = await context.newPage();
// lets head over to the home page of out website
await page.goto('https://theiconic.com.au&#39;)
// oh,dealing with a pesky pop up is easy peasey,did not have to write waits etc,just had to enter text of the button to be used as a selector !
//playwright's inbuilt auto-wait capability — https://playwright.dev/#version=v1.4.0&path=docs%2Factionability.md&q=
await page.click('text=No Thanks')
// perform navigation to another page of the app using text as a selector.
// More on selectors here – https://playwright.dev/#version=v1.4.0&path=docs%2Fselectors.md&q=
await page.click('text=#stayhome')
// head over to the login page
await page.goto('https://www.theiconic.com.au/customer/account/login/&#39;)
// select an element by id and click on it
await page.click('id=LoginForm_email')
// or just directly filling it with text
await page.fill('id=LoginForm_email','zookeeper@nationalzoo.com')
// another element found easily by id and text entered
await page.fill('id=LoginForm_password','tununutunu')
// find by id and click the login button
await page.click('id=LoginForm_submit')
// lets find the text contents of the selector below , just have pass the selector to the page.textContent method
const login_error = await page.textContent('#form-account-login > div:nth-child(2) > div:nth-child(2) > div');
// perform a simple assertion
assert(login_error=='The email address or password you entered is incorrect. Please try again.1')
// we are done 🙂
await browser.close();
view raw playwright.js hosted with ❤ by GitHub

Initial Playwright experience

  • Found writing the test very intuitive
  • Loved not having to write explicit waits
  • Stoked about the straightforwardness of dealing with selectors

Will definitely explore Playwright more !

Lessons learnt from a POC to automate Salesforce Lightning UI

My recent client work has been on testing a migration(data & business processes) to Salesforce CRM platform.

As part of Test execution, I took the initiative to build a POC to exercise automation of Salesforce both by interacting with the Lightning UI and the APEX Salesforce API interface.

This post is to share the hurdles I faced and lessons I learnt in building the POC for UI automation.

1. Choice of tools – Cypress.io & Selenium WebDriver

I exercised two tools sets that I am experienced with for UI automation – Cypress.io and Selenium Webdriver API (using Python) .

I could not go far wth Cypress, as it has limited support for iframes ( by design) , covered in this open issue thread.

Basically, as soon as I automated the login process, hit an error where Cypress terminated with an error “Whoops there is no test to run”


I tried some of the workarounds mentioned in the thread that worked for some folks, but not success.

So, once I exhausted my time box for Cypress I moved onto Selenium Webdriver.

2. Bypassing Email verification after login

The first hurdle I hit was the email 2FA that was set on the Salesforce sandbox environment that I was testing.

If I would have been automating at the API layer, there are various secure ways (e.g. API tokens) to authenticate but with email verification from the UI,Salesforce bases it on IP addresses. So, to work around that I to create another test user & profile that had either my IP whitelisted or basically no-IP filtered out.

Instructions from here were helpful –> https://github.com/neozenith/testcafe-sfdc-poc

3. Explicit waits & dealing with dynamic UI ids

Goes without saying that we have to explicitly wait for elements to load, to create robust tests, I had to write heaps of explicit waits to the interact with the Lightning UI (as it is a very “busy” UI)

Another interesting quirk I found , was ,even though some elements that I wanted to interact with has unique ids that I could as a selector, but as I found later through flakiness in my tests, that those ids especially for modal dialogs were being generated dynamically, most likely per login.


this piece of code although using id, was flaky because webdriver could not find the same id across sessions. The value “11:1355;a” would change to “11:3435;a” on subsequent test runs for the same modal dialog box.

So, instead I went with a different approach to not use id for the dynamic modal dialogs, instead search by XPath in this case and awaiting for element to be clickable

That worked and finally I was able to complete a basic UI automation flow of logging in , interacting with dynamic elements, adding user input and asserting some test conditions 🙂