We will learn how to capture and reuse Salesforce authentication using Playwright’s storageState
Playwright has a built‑in concept called storage state. It allows you to capture the current browser session (cookies, local storage, etc.) and save it to a JSON file. Later, you can start new test runs that automatically load this state, so the browser launches in a logged‑in condition.
For a Salesforce org protected by SSO and MFA, this translates to:
What the Setup Looks Like
I created a small setup script whose only job is to perform the login and write the storage file. At a high level, the script:
This script is intentionally separate from the main test suite. It is run manually when we need to generate or refresh the storage state for a given environment. Once the file is saved, the standard tests do not contain any login code.
In the Playwright configuration, I then specify:
Alternatively, for some suites, I attach the storage state at the test file level using test.use({ storageState: '...' }). In both cases, the principle is the same: every test starts with an already authenticated browser session.
What Worked Well
During the POC, this approach proved to be:
In both Dev and UAT, the same storage file (per environment) consistently authenticated the user until Salesforce decided to expire the session. There was no need to chase dynamic locators on SSO pages or handle edge cases like verification codes.

Limitations and How We Handle Them
Storage state is not a silver bullet. It comes with a few clear limitations:
To manage expiry, we introduced a simple practice: if tests suddenly start redirecting to the login page, regenerate the storage file for that environment. Because the regeneration process is quick and occurs only occasionally, the overhead is acceptable.
Why Storage State Became the Recommended Option
Compared to the other approaches I evaluated, storage state hit a good balance between effort and reliability:
The other methods (JWT, persistent profile, CLI) each have their place, especially for API and backend automation, but for UI login in Playwright, storage state turned out to be the most practical solution.
In the next posts of this series, I will go deeper into why the JWT‑based Connected App, persistent user data directory, and Salesforce CLI approaches did not fit our UI login requirements, and where they still make sense in the overall automation strategy.