top of page

One-Time Authentication in Playwright Using Global Setup (Complete Beginner Guide)

Introduction

In the previous post, we introduced Playwright hooks. We used beforeAll along with storageState to solve an important real-world problem: avoiding repeated login before every test in a single spec file.


Earlier, each test had to perform login steps as part of its workflow. This made the scripts longer, slower, and harder to maintain. By moving the login logic into beforeAll, we were able to authenticate once, save the session state, and reuse it across tests in the same file. This was a major improvement because our tests could now focus on the actual workflows — such as editing account details or completing a purchase — instead of repeatedly performing setup steps.



As our Playwright automation framework grows, we naturally split our tests into multiple spec files.

For example, in our demo store project, we now have separate spec files for different workflows:

  • Purchase product workflow

  • Account edit workflow


Each of these tests requires one common step before execution: user authentication.

Initially, we implemented login inside each spec file using beforeAll. While this works, it introduces duplication and violates one of the core principles of building a scalable automation framework


Authentication should be handled once and reused across all tests.


Playwright provides a powerful built-in solution for this using Global Setup.


In this post, we will implement global authentication step by step and configure our framework so that login happens only once before all tests run.


Our Current Project Structure

Here is the actual structure of our Playwright project:

PLAYWRIGHT_LEARN
│
├── helpers
 ├── global-setup.ts
│
├── tests 
 ├── globalsetup_purchase_workflow_demostore.spec.ts
 ├── globalsetup_account_edit_workflow_demostore.spec.ts 
 └── other spec files...
├── .env
├── playwright.config.ts
├── package.json
├── state.json

The key components are:

  • global-setup.ts → handles authentication

  • playwright.config.ts → configures Playwright to use global setup

  • .env → stores secure credentials

  • state.json → stores authenticated session


What is Global Setup?


Think of a global setup as preparing your environment before starting work.

Instead of logging in before every test, you log in once and reuse that session.

Playwright executes the global setup before any test begins.

The authentication session is saved to a file named storageState and then reused automatically.


Step 1: Store Credentials Securely Using .env

Create a file named .env in the project root.

Example:

DEMO_USER="anuradha.learn@gmail.com
"DEMO_PASS="Playwright#$"

Important note: Since # is treated as a comment in .env, enclosing the password in quotes ensures it is read correctly.


Step 2: Create Global Setup File

Create the file:

helpers/global-setup.ts

This file performs a login once and saves the session.


Example implementation:

import { chromium, FullConfig, expect } from "@playwright/test";
import dotenv from "dotenv";
dotenv.config();
async function globalSetup(config: FullConfig)
 {  
  const baseURL = "https://qa-cart.com";
  const storageStatePath = "state.json";
  const username = process.env.DEMO_USER!;  
  const password = process.env.DEMO_PASS!;  
  const browser = await chromium.launch({    headless: false  });
  const context = await browser.newContext();  
  const page = await context.newPage();  
  await page.goto(baseURL);  
  await page.locator("input[name='username']").fill(username);  
  await page.locator("input[name='password']").fill(password);  
  await page.getByRole("button", { name: /log in/i }).click();
  await page.goto(`${baseURL}/my-account/`);
  await expect(
    page.getByRole("link", { name: /log out/i })
  ).toBeVisible();
  await context.storageState({ path: storageStatePath });
  await browser.close();}
export default globalSetup;

This script logs in and saves the authentication in state.json.


Step 3: Configure Playwright to Use Global Setup

Open:

playwright.config.ts

Add globalSetup configuration:


This tells Playwright to execute the global setup before running tests.


Step 4: Update Spec Files (No Login Required)


Now your spec files no longer need login code.


Example spec file:

import { test, expect } from "@playwright/test";test("Verify purchase workflow", async ({ page }) => {
  await page.goto("/demoshop");
  await expect(
    page.getByRole("heading", { name: "DemoShop" }))
    .toBeVisible();
});

Playwright automatically loads an authenticated session from state.json.


At this point, our test.spec files are free from test data(handled in json) , config data(handled in playwright config file and sensitive dat - in env file). We do not have any beforeall(login logic) as well, and no context creation too.

Step 5: Execution Flow


When you run:

npx playwright test

Playwright performs the following steps:

  1. Runs global setup

  2. Launches the browser and logs in

  3. Saves authentication state in state.json

  4. Runs all spec files

  5. Each test starts already authenticated


Authentication runs only once.


Debugging Tip: Run Global Setup in Visible Mode

While debugging, use:

const browser = await chromium.launch({ headless: false });

This allows you to observe the login visually.

Once confirmed, switch back to headless mode.


Benefits of Global Setup


Using a global setup provides major advantages.

  • Authentication runs only once, improving performance.

  • Spec files remain clean and focused on test logic.

  • Framework becomes easier to maintain and scale.

  • Sensitive credentials remain secure using environment variables.

  • Test execution becomes faster and more reliable.



Upgrading to a better Option


What we achieved, but what we may miss as we deal with report traces results, which may need cleanup as we run the suite every time


Global Setup the Playwright-Recommended Way: Project Dependencies


Instead of running a separate global setup script “outside” the test runner, we create a dedicated setup project that runs before everything else.

This approach integrates beautifully with Playwright Test Runner.


Why Project Dependencies are recommended


When you use the globalSetup config option, Playwright runs it like a “standalone script.”

That means:

  • It won’t show in the HTML report

  • It won’t generate traces

  • fixtures are not available

  • You manually launch browsers


With project dependencies, setup is treated like a real Playwright test project, so:

  • It shows in the report

  • traces and artefacts are available

  • fixtures like { page } and { browser } work

  • config options (headless, retries, etc.) apply automatically


Step-by-step: Use Project Dependencies for One-Time Authentication


We will create:

  1. A setup project that logs in and writes state.json

  2. All other projects/tests will depend on the setup project

  3. Every test will automatically start authenticated using storageState


Step 1: Create a setup test file


Create this file in your tests/ folder:

tests/global.setup.ts


This is a special “setup test” that performs login and saves the auth state.


import { test, expect } from "@playwright/test";
import dotenv from "dotenv";

dotenv.config();

test("global setup: login and save storageState", async ({ page }) => {
  const baseURL = "https://qa-cart.com";
  const storageStatePath = "state.json";

  const username = process.env.DEMO_USER!;
  const password = process.env.DEMO_PASS!;

  await page.goto(baseURL);

  await expect(page.getByRole("heading", { name: /login/i })).toBeVisible();

  await page.locator("input[name='username']").fill(username);
  await page.locator("input[name='password']").fill(password);

  await page.getByRole("button", { name: /log in/i }).click();

  // Verify login on a stable page
  await page.goto(`${baseURL}`);
  await expect(page.getByRole("link", { name: /log out|logout/i }).first()).toBeVisible();

  // Save login session
  await page.context().storageState({ path: storageStatePath });
});

Step 2: Update playwright.config.ts with setup project + dependencies


Now we define multiple “projects”:

  • setup (runs first)

  • chromium/firefox / webkit etc. (depends on setup)

Step 3: Keep your spec files clean (no login code!)


No beforeAll login. No auth duplication—no manual context creation.


In this journey, we moved from duplicating login logic inside every spec file to implementing a professional, scalable authentication architecture using Playwright’s recommended Project Dependencies model.


Let’s recap what we achieved.


Where We Started

Initially, authentication was written inside each spec file using beforeAll.

It worked, but:

  • Login logic was duplicated

  • Maintenance became harder

  • Setup logic was tightly coupled with test logic

  • Scaling across multiple spec files felt messy


What We Built


We implemented:

  • A dedicated global.setup.ts file

  • A setup project configured inside playwright.config.ts

  • Project dependencies to ensure setup runs before workflow tests

  • Authentication session stored using storageState

  • Clean spec files without login logic


Now, authentication runs once before all dependent projects.

Spec files remain focused only on business workflows.



Our Current Flow

When we run:

npx playwright test

Execution happens in this order:

  1. Setup project runs

  2. User logs in

  3. Authentication state is saved

  4. Workflow spec files execute

  5. All tests start already authenticated

Authentication is cleanly separated from test logic.


Final Project Structure

project-root│
├── tests/
│
   ├── global.setup.ts│
   ├── purchase_workflow.spec.ts│
   ├── account_edit.spec.ts│
├── state.json
├── playwright.config.ts
├── .env

This is a scalable, maintainable Playwright test framework structure.


What’s Next


Now that authentication is architected properly, our next steps will focus on:


  • Adding structured reporting enhancements

  • Improving logging

  • Implementing teardown strategies

  • Cleaning up test data after execution - global tear down

  • Adding trace and artifact management





Comments


Never Miss a Post. Subscribe Now!

Thanks for submitting!

©anuradha agarwal

    bottom of page