Suppose you have a web application for which you would like to write browser tests using Playwright. This web application contains a login screen and after successful authentication, all functionality is loaded. This means that in order to test the application, each test requires an authenticated user. Because each test runs in isolation, each test needs to authenticate. So, in each test, you write the steps to open a browser, navigate to the login screen, fill in the username, password, click on submit, and finally wait for the page to be loaded. Running this can take from a couple of seconds for one test to minutes for dozens of tests. Ideally, you want to authenticate only once when running your Playwright tests.
What if you could somehow reuse the logged-in state across your tests? Luckily, there is such a way and it is pretty easy to set it up using Playwright. Read on and discover how you can shave off valuable time by authenticating only once with Playwright.
If you want to dive straight into the solution, you can check out the repository HERE.
Playwright authenticate once steps summary
In a nutshell, the following steps are needed and they are described in detail in their respective sections in this article:
- Store the test account’s username and password in a safe way
- Write the login test function
- Configure the the storage state path
- Write a function that calls the login function and saves the logged in state to the storage state path
- Write the tests that require the logged in state
Step 1: Store the Username and Password
I use dotenv to store and load the username and password. This avoids credentials becoming part of the committed code.
npm i -D dotenv
Next, let’s initialize dotenv in the configuration file of Playwright playwright.config.ts.
Next, create a .env file on your machine in the root of the repo with the following:
Obviously, you need to fill in real values for the username and password from a valid user. In this example, we want to use meetup.com.
Step 2: Write the “Playwright authenticate once” Test
Below, the code for login.ts is shown:
The login function has three arguments. The username and password may be straightforward. The Playwright test library always needs a page object when defining actions for browser pages, such as navigation or interacting with the DOM.
The first action is to navigate to the login page of meetup.com. Next, the username and password fields are filled in from the values that are provided through the arguments of the function. In a later section, we will see how these arguments are provided.
As the last step a DOM element that matches a button with the type "submit" and the text "Log in" is clicked. This click is preceded by a waitForNavigation
function and wrapped in a Promise.all
. This means that, after the button has been clicked, the test waits for the navigation to be completed in order to return. This is useful when a click button causes indirect navigation. In this case, the click causes an API call, which validates the request, returns an OK message, and causes the page to redirect. The waitForNavigation
needs to be specified before the click
function in order to set up the wait.
Step 3: Configure the Storage State
Web applications may use cookie-based or token-based authentication (see here for more information). Playwright uses the storageState
method to store the authenticated state of a user after the user has logged in. This state can be read when creating a new browser context. The path for this file with the stored state can be configured in the playwright.config.ts
:
The storageState
property is where you specify the path of the file where the authenticated information is being stored. The globalSetup
property is the path to your global setup file. This file contains code that runs once before all your tests.
Step 4: Write the “Playwright authenticate once” Setup
To make sure the login test is run before all other tests, Playwright defines a global setup approach. The global-setup.ts file is shown as follows:
The username and password can be read from the environment since the env.config()
method has run and processed your .env file. The storageState
property can be retrieved from the playwright.config.ts
file by reading from the FullConfig
. The headless: false
option in the method chromium.launch
is not needed, but I’ve added it to see what is happening on the screen.
The call to the login function is made by providing the current page object, username, and password because the login function needs a page object for its actions. After the login, the authentication information is stored to the storageState
. At the end of the global setup, the browser needs to be closed. Once this function returns, the tests start running in a different browser instance.
Step 5: Write the tests
In order for the "Playwright authenticate once" functionality to prove its point, we need at least two isolated tests that depend on the logged-in state.
We navigate to two different pages and verify the visibility of certain text on the screen. When we run the tests, the results can be seen below.
The test that runs once and logs in a user:
The test that navigates to the Notifications page:
The test that navigates to the Payment methods page:
The test run finishes in approximately 5 seconds as opposed to 13 seconds when a login is done before each test.
Conclusion
When you are writing browser tests with Playwright for a web application that needs authenticated users for your tests to work, start early with an authenticate-once approach.
Your tests will:
- Run faster
- Be closer to reality (A user logs in once and does all the actions for the duration of the session)
- Be cleaner
- Better equipped for cases in which you want to add users with different roles. You can keep track of those users in the
stateStorage
and feed them to your tests.
Would you like to know more about Playwright? Ready for the next step?
Go check this training: End-to-End Testing with Playwright
Any comments are more than welcome! Drop me a message through LinkedIn or Twitter @rscorradin.