npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@comatoastuk/screenplay-playwright

v1.4.0

Published

Screenplay pattern implementation using Playwright for test automation

Readme

Screenplay Pattern with Playwright

npm version CI License: MIT

A comprehensive TypeScript implementation of the Screenplay pattern for test automation using Playwright. This framework provides a clean, maintainable, and readable approach to writing automated tests by modeling user interactions as a series of tasks and interactions.

✨ Features:

  • 🎭 Full Screenplay pattern implementation
  • 📸 Built-in visual regression testing
  • 🚀 Multi-browser support (Chromium, Firefox, WebKit)
  • 🔧 TypeScript with strict typing
  • 📦 Ready for CI/CD with automatic baseline management
  • 🎯 Comprehensive test utilities and page objects

🎭 What is the Screenplay Pattern?

The Screenplay pattern is a user-centered approach to writing automated tests. It models the way that real users interact with software applications, focusing on:

  • Actors - Users who interact with the system
  • Abilities - What the actors can do (e.g., browse the web)
  • Tasks - High-level business activities
  • Interactions - Direct interactions with the system
  • Questions - Queries about the current state of the system

🚀 Quick Start

Installation

# Install from npm
npm install @comatoastuk/screenplay-playwright

# Or install from GitHub Packages
npm install @comatoastuk/screenplay-playwright --registry=https://npm.pkg.github.com

Running Tests

# Run all tests
npm test

# Run tests in headed mode
npm run test:headed

# Run tests with debug mode
npm run test:debug

# Open test UI
npm run test:ui

# View test report
npm run test:report

# Visual regression testing
npm run test:visual              # Run visual tests
npm run test:visual:update       # Update visual baselines
npm run test:visual:headed       # Debug visual tests
npm run test:visual:chromium     # Run visual tests on Chromium only

Building the Project

# Build TypeScript
npm run build

# Build and watch for changes
npm run build:watch

# Clean build artifacts
npm run clean

📝 Basic Usage

Creating an Actor

import { Actor, BrowseTheWeb, Cast } from 'screenplay-playwright';

// Create an actor with the ability to browse the web
const actor = Cast.theActorNamed('TestUser').whoCan(
  BrowseTheWeb.using(page, context)
);

Performing Interactions

import { NavigateTo, Click, Type, Wait } from 'screenplay-playwright';

// Basic interactions
await actor.attemptsTo(
  NavigateTo.url('https://example.com'),
  Click.on('#login-button'),
  Type.theText('username').into('#username-field'),
  Wait.forElementToBeVisible('.dashboard')
);

Asking Questions

import { Text, Visibility, PageTitle, CurrentUrl } from 'screenplay-playwright';

// Query the current state
const title = await actor.asks(PageTitle.current());
const isVisible = await actor.asks(Visibility.of('#element'));
const text = await actor.asks(Text.of('.message'));
const url = await actor.asks(CurrentUrl.value());

Performing Tasks

import { Login, Search } from 'screenplay-playwright';

// High-level business tasks
await actor.attemptsTo(
  Login.withCredentials('testuser', 'password').at('https://example.com/login'),

  Search.for('playwright').using({
    input: '#search-input',
    submit: '#search-button',
  })
);

Visual Regression Testing

import {
  TakeScreenshot,
  CompareScreenshot,
  VisualTesting,
  VisualTestConfig,
} from 'screenplay-playwright';

// Configure visual testing
VisualTestConfig.configure({
  threshold: 0.2,
  defaultOptions: {
    fullPage: true,
    animations: 'disabled',
  },
});

// Take screenshots
await actor.attemptsTo(
  NavigateTo.url('https://example.com'),
  TakeScreenshot.named('homepage')
);

// Compare with baselines
await actor.attemptsTo(
  NavigateTo.url('https://example.com'),
  CompareScreenshot.named('homepage').withThreshold(0.1)
);

// Wait for visual stability before capturing
await VisualTesting.waitForVisualStability(actor);

// Complete visual testing workflow
await VisualTesting.performVisualTest(
  actor,
  'homepage-test',
  undefined, // Full page
  0.2 // Threshold
);

🏗️ Framework Structure

src/
├── screenplay/
│   └── interfaces.ts          # Core interfaces
├── actors/
│   └── Actor.ts              # Actor implementation
├── abilities/
│   └── BrowseTheWeb.ts       # Web browsing ability
├── interactions/
│   ├── NavigateTo.ts         # Navigate to URL
│   ├── Click.ts              # Click elements
│   ├── Type.ts               # Type text
│   ├── Wait.ts               # Wait operations
│   ├── Select.ts             # Select dropdown options
│   ├── Scroll.ts             # Scroll operations
│   ├── Hover.ts              # Hover over elements
│   └── visual/               # Visual regression testing
│       ├── TakeScreenshot.ts     # Capture screenshots
│       └── CompareScreenshot.ts  # Compare with baselines
├── questions/
│   ├── Text.ts               # Get element text
│   ├── Visibility.ts         # Check visibility
│   ├── PageTitle.ts          # Get page title
│   ├── CurrentUrl.ts         # Get current URL
│   ├── Attribute.ts          # Get attributes
│   ├── Count.ts              # Count elements
│   ├── Value.ts              # Get input values
│   └── visual/               # Visual testing questions
│       └── VisualMatch.ts        # Visual comparison results
├── tasks/
│   ├── Login.ts              # Login task
│   └── Search.ts             # Search task
├── utils/
│   ├── Cast.ts               # Actor management
│   └── visual/               # Visual testing utilities
│       ├── VisualTestConfig.ts   # Configuration
│       └── VisualTesting.ts      # Testing workflows
├── models/
│   └── PageObjects.ts        # Page object models
└── index.ts                  # Main exports

📚 Core Concepts

Actors

Actors represent users of your system. They have abilities and can perform activities:

const alice = Cast.theActorNamed('Alice').whoCan(
  BrowseTheWeb.using(page, context)
);

// Actors can remember information
alice.remember('userRole', 'admin');
const role = alice.recall('userRole');

Abilities

Abilities define what actors can do. The main ability is BrowseTheWeb:

const browseTheWeb = BrowseTheWeb.using(page, context);
actor.whoCan(browseTheWeb);

Interactions

Direct interactions with the system:

// Available interactions
Click.on('#button');
Type.theText('text').into('#input');
Select.option('value').from('#dropdown');
Hover.over('#element');
Scroll.toElement('#target');
Wait.forElementToBeVisible('#element');
NavigateTo.url('https://example.com');

// Visual regression testing interactions
TakeScreenshot.named('screenshot-name');
TakeScreenshot.ofElement('#specific-element');
CompareScreenshot.named('baseline-name');
CompareScreenshot.ofElement('#element').withThreshold(0.1);

Questions

Query the current state of the system:

// Available questions
Text.of('#element');
Visibility.of('#element');
Value.of('#input');
Count.of('.items');
Attribute.of('#element').named('class');
PageTitle.current();
CurrentUrl.value();

// Visual testing questions
VisualMatch.between('screenshot1.png', 'screenshot2.png');
VisualMatch.forBaseline('baseline-name').withThreshold(0.1);

Tasks

High-level business activities composed of multiple interactions:

// Pre-built tasks
Login.withCredentials('user', 'pass').at('/login');
Search.for('query').using({ input: '#search', submit: '#go' });

// Custom tasks
class CompleteProfile implements Task {
  async performAs(actor: Actor): Promise<void> {
    await actor.attemptsTo(
      Click.on('#profile-link'),
      Type.theText('John Doe').into('#full-name'),
      Select.option('Developer').from('#role'),
      Click.on('#save-button')
    );
  }

  toString(): string {
    return 'complete the user profile';
  }
}

🎯 Advanced Usage

Visual Regression Testing

The framework includes comprehensive visual regression testing capabilities:

import {
  VisualTestConfig,
  VisualTesting,
  TakeScreenshot,
  CompareScreenshot,
  VisualMatch,
} from 'screenplay-playwright';

// Configure visual testing globally
VisualTestConfig.configure({
  threshold: 0.2,
  defaultOptions: {
    fullPage: true,
    animations: 'disabled',
    caret: 'hide',
  },
});

// Basic screenshot workflow
await actor.attemptsTo(
  NavigateTo.url('https://example.com'),
  VisualTesting.waitForVisualStability(actor), // Wait for animations to complete
  TakeScreenshot.named('homepage-baseline')
);

// Compare screenshots with custom threshold
await actor.attemptsTo(
  NavigateTo.url('https://example.com'),
  CompareScreenshot.named('homepage-baseline').withThreshold(0.1)
);

// Element-specific visual testing
await actor.attemptsTo(
  CompareScreenshot.ofElement('#header').named('header-comparison')
);

// Complete visual testing workflow with utilities
await VisualTesting.performVisualTest(
  actor,
  'responsive-homepage',
  '#main-content', // Specific element (optional)
  0.15 // Custom threshold
);

// Ask questions about visual differences
const hasVisualChanges = await actor.asks(
  VisualMatch.forBaseline('homepage').withThreshold(0.05)
);

Visual Testing in CI/CD

The framework automatically handles baseline management in CI environments:

  • First CI run: Creates baseline screenshots (expected "failures")
  • Subsequent runs: Compares against established baselines
  • Local development: Creates baselines as needed
  • Global setup/teardown: Automatic baseline generation mode detection
# Local development
npm run test:visual              # Run with existing baselines
npm run test:visual:update       # Update baselines after UI changes

# CI automatically handles baseline creation and comparison

See Visual Testing CI Guide for detailed setup.

Page Object Models

Use the provided page object models for better organization:

import { Form, Navigation, Table } from 'screenplay-playwright';

const loginForm = Form.located('#login-form').withFields({
  username: '#username',
  password: '#password',
  submit: '#login-btn',
});

const mainNav = Navigation.located('.main-nav').withLinks({
  home: '#home-link',
  profile: '#profile-link',
  settings: '#settings-link',
});

const dataTable = Table.located('#data-table')
  .withRowSelector('tbody tr')
  .withCellSelector('td');

Custom Interactions

Create your own interactions:

import { Interaction, Actor } from 'screenplay-playwright';

class DoubleClick implements Interaction {
  constructor(private selector: string) {}

  static on(selector: string) {
    return new DoubleClick(selector);
  }

  async performAs(actor: Actor): Promise<void> {
    const browseTheWeb = actor.abilityTo(BrowseTheWeb);
    await browseTheWeb.page.dblclick(this.selector);
  }

  toString(): string {
    return `double click on ${this.selector}`;
  }
}

Custom Questions

Create your own questions:

import { Question, Actor } from 'screenplay-playwright';

class CSSProperty implements Question<string> {
  constructor(
    private selector: string,
    private property: string
  ) {}

  static of(selector: string, property: string) {
    return new CSSProperty(selector, property);
  }

  async answeredBy(actor: Actor): Promise<string> {
    const browseTheWeb = actor.abilityTo(BrowseTheWeb);
    const element = browseTheWeb.page.locator(this.selector);
    return await element.evaluate(
      (el, prop) => getComputedStyle(el).getPropertyValue(prop),
      this.property
    );
  }

  toString(): string {
    return `the ${this.property} CSS property of ${this.selector}`;
  }
}

🧪 Testing Examples

Comprehensive examples are available in the test files:

  • tests/example.spec.ts - Basic Screenplay pattern usage examples
  • tests/visual-regression.spec.ts - Visual regression testing examples
  • Framework setup and configuration examples
  • Custom interactions and questions
  • Page object model implementations
  • CI/CD integration patterns

Example Test Structure

import { test } from '@playwright/test';
import {
  Actor,
  BrowseTheWeb,
  NavigateTo,
  Click,
  VisualTesting,
} from '@comatoastuk/screenplay-playwright';

test.describe('Example Tests', () => {
  let actor: Actor;

  test.beforeEach(async ({ page, context }) => {
    actor = new Actor('TestUser').whoCan(BrowseTheWeb.using(page, context));
  });

  test('should demonstrate basic interactions', async () => {
    await actor.attemptsTo(
      NavigateTo.url('https://example.com'),
      Click.on('#button')
    );
  });

  test('should perform visual regression testing', async () => {
    await actor.attemptsTo(NavigateTo.url('https://example.com'));

    await VisualTesting.performVisualTest(actor, 'homepage');
  });
});

📋 Available Scripts

Core Scripts

  • npm run build - Compile TypeScript to JavaScript
  • npm run build:watch - Compile with watch mode
  • npm test - Run Playwright tests
  • npm run test:headed - Run tests in headed mode
  • npm run test:debug - Run tests in debug mode
  • npm run test:ui - Open Playwright UI
  • npm run test:report - Show test report

Visual Testing Scripts

  • npm run test:visual - Run visual regression tests
  • npm run test:visual:update - Update baseline screenshots
  • npm run test:visual:headed - Debug visual tests in headed mode
  • npm run test:visual:chromium - Run visual tests on Chromium only

Development Scripts

  • npm run lint - Run ESLint
  • npm run lint:fix - Fix ESLint issues
  • npm run format - Format code with Prettier
  • npm run clean - Clean build artifacts
  • npm run docs - Generate TypeScript documentation

🚀 CI/CD Workflows

This project includes comprehensive GitHub Actions workflows with automatic semantic versioning:

Continuous Integration

  • Linting - ESLint code quality checks
  • Building - TypeScript compilation
  • Testing - Multi-browser testing (Chromium, Firefox, WebKit)
  • Security - CodeQL analysis and dependency review
  • Package Analysis - Bundle size monitoring

Automated Release & Versioning

  • Semantic Versioning - Automatic version bumps based on conventional commits
  • Release Notes - Auto-generated changelogs with emoji categorization
  • npm Publishing - Automatic publishing to npmjs.org
  • GitHub Packages - Publishing to GitHub Package Registry
  • GitHub Releases - Automated releases with assets and notes

Conventional Commits

Use semantic commit messages for automatic versioning:

feat: add new interaction     # → Minor release (0.1.0)
fix: resolve selector issue   # → Patch release (0.0.1)
feat!: redesign Actor API     # → Major release (1.0.0)

# Interactive commit helper (recommended)
npm run commit

This launches an interactive commit helper that guides you through creating properly formatted conventional commits, ensuring consistent formatting and automatic semantic versioning.

Setup Requirements

  1. Add NPM_TOKEN to repository secrets for publishing
  2. Enable GitHub Pages for documentation deployment
  3. Use conventional commit messages for automatic versioning

See Contributing Guide for detailed development workflow. See Commitizen Guide for interactive commit help. See Semantic Versioning Guide for detailed commit message format. See GitHub Actions Setup Guide for detailed configuration. See Visual Testing Setup Guide for visual regression testing setup. See Visual Testing CI Guide for CI/CD integration.

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality
  5. Run the linter and tests
  6. Submit a pull request

📄 License

MIT

🔗 Resources