Skip to content

Session Management

CraftDriver supports Playwright-style session persistence, allowing you to save and restore browser state including cookies and localStorage. This is perfect for:

  • Skipping login in tests - Log in once, reuse session across test runs
  • Sharing auth state - Generate auth state in setup, use in parallel tests
  • Debugging - Capture session state at any point

Session management is the user-facing feature. Under the hood, CraftDriver uses the best available WebDriver transport for the browser you launched.

Saving Session State

typescript
// Save all cookies and localStorage to a file
await browser.saveState('./session.json');

// Save with options
await browser.saveState('./session.json', {
  includeCookies: true, // default: true
  includeLocalStorage: true, // default: true
  includeSessionStorage: false, // default: false
});

The saved file contains:

json
{
  "cookies": [
    {
      "name": "session",
      "value": "abc123",
      "domain": "example.com",
      "path": "/",
      "secure": true,
      "httpOnly": true,
      "sameSite": "lax"
    }
  ],
  "localStorage": {
    "https://example.com": {
      "theme": "dark",
      "userId": "12345"
    }
  }
}

Loading Session State

typescript
// Load state into current browser
await browser.loadState('./session.json');

Working With State Objects

Use the state object directly when you do not want to write a file:

typescript
const state = await browser.storage.getState();
await browser.storage.setState(state);

Launching with Pre-loaded State

The most common pattern - launch a browser with existing session:

typescript
const browser = await Browser.launch({
  browserName: 'chrome',
  storageState: './session.json',
});

// Navigate directly to authenticated page
await browser.navigateTo('https://example.com/dashboard');
// Already logged in!

Direct cookie manipulation is also available:

typescript
// Add a cookie
await browser.storage.addCookie({
  name: 'session',
  value: 'abc123',
  domain: 'localhost',
  path: '/',
  secure: false,
  httpOnly: true,
  sameSite: 'Lax',
  expiry: new Date('2025-12-31'), // or Unix timestamp
});

// Get all cookies
const cookies = await browser.storage.getCookies();

// Get cookies for specific domain
const domainCookies = await browser.storage.getCookies({ domain: 'example.com' });

// Set multiple cookies
await browser.storage.setCookies([
  { name: 'theme', value: 'dark', domain: 'example.com', path: '/' },
]);

// Clear all cookies
await browser.storage.clearCookies();

// Clear cookies by filter
await browser.storage.clearCookies({ name: 'session' });
await browser.storage.clearCookies({ domain: 'example.com' });

Example: Login Once, Reuse Session

Step 1: Generate Auth State (run once)

typescript
// scripts/generate-auth.ts
import { Browser } from 'craftdriver';

async function generateAuth() {
  const browser = await Browser.launch({ browserName: 'chrome' });

  await browser.navigateTo('https://myapp.com/login');
  await browser.fill('#username', process.env.TEST_USER!);
  await browser.fill('#password', process.env.TEST_PASS!);
  await browser.click('#login-btn');

  // Wait for login to complete
  await browser.expect('#dashboard').toBeVisible();

  // Save the authenticated state
  await browser.saveState('./auth.json');

  await browser.quit();
  console.log('Auth state saved to auth.json');
}

generateAuth();

Step 2: Use Auth State in Tests

typescript
// tests/dashboard.test.ts
import { describe, it, beforeEach, afterEach } from 'vitest';
import { Browser } from 'craftdriver';

describe('Dashboard', () => {
  let browser: Browser;

  beforeEach(async () => {
    // Launch with saved auth - already logged in!
    browser = await Browser.launch({
      browserName: 'chrome',
      storageState: './auth.json',
    });
  });

  afterEach(async () => {
    await browser.quit();
  });

  it('shows user profile', async () => {
    // Go directly to authenticated page
    await browser.navigateTo('https://myapp.com/profile');

    // No login needed - session cookie is already set
    await browser.expect('#username').toHaveText('testuser');
  });

  it('can update settings', async () => {
    await browser.navigateTo('https://myapp.com/settings');
    await browser.click('#dark-mode-toggle');
    await browser.expect('#theme').toHaveText('dark');
  });
});

Complete E2E Example

typescript
import { Browser } from 'craftdriver';

async function testLoginPersistence() {
  // First browser: Log in and save state
  let browser = await Browser.launch({ browserName: 'chrome' });

  await browser.navigateTo('http://localhost:3000/login');
  await browser.fill('#username', 'testuser');
  await browser.fill('#password', 'secret123');
  await browser.click('#submit');
  await browser.expect('#welcome').toBeVisible();

  await browser.saveState('./session.json');
  await browser.quit();

  // Second browser: Load state and verify logged in
  browser = await Browser.launch({
    browserName: 'chrome',
    storageState: './session.json',
  });

  await browser.navigateTo('http://localhost:3000/login');

  // Should already be logged in!
  await browser.expect('#welcome').toContainText('testuser');

  await browser.quit();
}

API Reference

Browser Methods

MethodDescription
saveState(path, options?)Save cookies and storage to file
loadState(path)Load cookies and storage from file

StorageStateOptions

OptionTypeDefaultDescription
includeCookiesbooleantrueInclude cookies in saved state
includeLocalStoragebooleantrueInclude localStorage
includeSessionStoragebooleanfalseInclude sessionStorage
originsstring[]allFilter localStorage/sessionStorage capture by origin. Capture is always scoped to the page's current origin — this only decides whether that one origin is included, it does not collect storage from other origins the page hasn't visited. Cookies are unaffected by this option.

SessionStateManager (browser.storage)

MethodDescription
addCookie(cookie)Add a single cookie
getCookies(filter?)Get cookies, optionally filtered
setCookies(cookies)Set multiple cookies
clearCookies(filter?)Clear cookies, optionally filtered
getState(options?)Get current state as object
setState(state)Set state from object
saveState(path, options?)Save state to file
loadState(path)Load state from file

Released under the MIT License.