typescript 109 lines · 4 tabs

Playwright smoke test for auth flow

Shared by codesnips Jan 2026
4 tabs
import { defineConfig, devices } from '@playwright/test';
import path from 'node:path';

export const authFile = path.join(__dirname, '.auth/user.json');

export default defineConfig({
  testDir: './tests',
  timeout: 30_000,
  retries: process.env.CI ? 2 : 0,
  reporter: process.env.CI ? 'github' : 'list',
  use: {
    baseURL: process.env.BASE_URL ?? 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
  projects: [
    {
      name: 'setup',
      testMatch: /.*\.setup\.ts/,
    },
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        storageState: authFile,
      },
      dependencies: ['setup'],
    },
  ],
});
4 files · typescript Explain with highlit

This snippet shows a compact, production-shaped Playwright setup for smoke-testing a login flow. The central idea is to authenticate exactly once, persist the resulting browser storage (cookies plus localStorage) to disk, and let every subsequent spec reuse that authenticated context instead of clicking through the login form again. This is Playwright's storageState pattern, and it turns a slow, flaky per-test login into a single cheap file read.

The playwright.config.ts tab wires this together with project dependencies. A dedicated setup project matches *.setup.ts files and runs first; the real chromium project declares dependencies: ['setup'] and points use.storageState at the JSON file the setup produced. Because the setup is expressed as a project rather than a global hook, it participates in the trace, retry, and reporting machinery like any other test, which makes failures during authentication debuggable rather than opaque.

The LoginPage page object encapsulates every selector and interaction for the login screen behind intention-revealing methods. Locators are created with role- and label-based queries (getByLabel, getByRole) so the tests assert on accessible semantics rather than brittle CSS. The login method fills credentials and waits for a **/dashboard navigation via waitForURL, which is the meaningful signal that authentication actually succeeded.

In auth.setup.ts, the setup test drives the page object once, reads credentials from the environment so no secrets are hardcoded, asserts a post-login element is visible, and then calls context.storageState({ path }) to serialize the session. Writing to a stable authFile path is what the config later consumes.

The dashboard.smoke.spec.ts tab is the payoff: it never touches the login form. Because the project injects the saved storage state, page.goto('/dashboard') lands already authenticated, so the spec focuses purely on smoke assertions like the user menu and a heading being visible.

The main trade-off is staleness — the stored state can expire, so short-lived tokens may require re-running setup or shortening its cache lifetime. It is also worth keeping the auth file out of version control and regenerating it per CI run.


Related snips

ruby
class CommentsController < ApplicationController
  before_action :set_post

  def create
    @comment = @post.comments.build(comment_params)

System test: asserting Turbo Stream responses

rails hotwire turbo
by codesnips 4 tabs
ruby
payload = {
  sub: user.id,
  iss: 'https://auth.example.com',
  aud: 'codesnips-api',
  exp: 15.minutes.from_now.to_i,
  iat: Time.now.to_i,

JWT issuance and verification without common footguns

jwt authentication api
by Kai Nakamura 2 tabs
typescript
export interface RetryOptions {
  retries: number;
  baseMs: number;
  maxMs: number;
  signal?: AbortSignal;
  onRetry?: (attempt: number, delay: number, err: unknown) => void;

Exponential backoff with jitter for retries

typescript reliability retry
by codesnips 2 tabs
typescript
import axios, { AxiosError } from 'axios'
import { v4 as uuidv4 } from 'uuid'

const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000/api/v1',
  timeout: 15000,

Axios API client with interceptors

react axios api
by Maya Patel 1 tab
typescript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'

interface FilterState {
  search: string
  category: string | null

Zustand for lightweight state management

react zustand state-management
by Maya Patel 2 tabs
typescript
import { randomBytes, createHash } from "crypto";
import jwt from "jsonwebtoken";
import { RefreshTokenStore } from "./store";

const ACCESS_SECRET = process.env.ACCESS_SECRET!;
const ACCESS_TTL = "15m";

JWT access + refresh token rotation (conceptual)

security node jwt
by codesnips 3 tabs

Share this code

Here's the card — post it anywhere.

Playwright smoke test for auth flow — share card
Link copied