Skip to main content

Overview

The Web Stories plugin includes comprehensive test coverage across multiple test types:
  • PHP Unit Tests - Testing PHP backend functionality
  • JavaScript Unit Tests - Testing React components and utilities
  • Integration Tests - Browser-based integration tests with Karma
  • E2E Tests - End-to-end tests with Puppeteer

PHP Unit Tests

PHP tests use PHPUnit and the WordPress PHPUnit Test Suite.

Running PHP Tests

npm run test:php

PHP Test Structure

PHP tests are organized in:
  • tests/phpunit/unit/ - Unit tests
  • tests/phpunit/integration/ - Integration tests
Configuration files:
  • phpunit.xml.dist - Unit tests configuration
  • phpunit-integration.xml.dist - Integration tests (single site)
  • phpunit-integration-multisite.xml.dist - Integration tests (multisite)

Writing PHP Tests

namespace Google\Web_Stories\Tests;

class Story_Post_Type_Test extends \WP_UnitTestCase {
  
  public function test_post_type_registration() {
    $post_type = get_post_type_object( 'web-story' );
    $this->assertNotNull( $post_type );
    $this->assertEquals( 'web-story', $post_type->name );
  }
}
The project uses WP Test Utils for improved cross-version compatibility with PHPUnit.

JavaScript Unit Tests

JavaScript tests use Jest with Testing Library.

Running JavaScript Tests

npm run test:js

JavaScript Test Configuration

Tests are configured in tests/js/jest.config.js. Key testing libraries:
  • Jest - Test runner and assertion library
  • @testing-library/react - React component testing
  • @testing-library/react-hooks - React hooks testing
  • jest-dom - Custom DOM matchers
  • jest-extended - Additional matchers
Both jest-dom and jest-extended have a toBeEmpty assertion. If you want the one from jest-dom, use toBeEmptyNode instead.

Writing JavaScript Tests

File location: If a component resides in foo/bar/baz.js, place tests in foo/bar/tests/baz.js. Test naming convention:
import { sum } from '../sum';

describe('sum', () => {
  it('adds 1 + 2 to equal 3', () => {
    expect(sum(1, 2)).toBe(3);
  });
});
Testing React components:
import { render, screen } from '@testing-library/react';
import { Button } from '../button';

describe('Button', () => {
  it('renders with correct text', () => {
    render(<Button>Click me</Button>);
    expect(screen.getByRole('button')).toHaveTextContent('Click me');
  });
});

Custom Matchers

The project includes custom AMP validation matchers:
  • toBeValidAMP
  • toBeValidAMPStoryPage
  • toBeValidAMPStoryElement

Test Utils

Custom test utilities are available in testUtils folders:
  • renderWithTheme - Render components with theme provider
  • Component-specific test helpers
See packages/*/src/testUtils/ for available utilities.

Code Coverage

Generate and view code coverage:
npm run test:js:coverage
The coverage report will automatically open in your browser at build/logs/lcov-report/index.html.

Integration Tests (Karma)

Integration tests use Karma with Jasmine to run tests in real browsers.

Running Karma Tests

npm run test:karma:story-editor -- --headless --viewport=1600:1000

Karma Configuration

  • karma-story-editor.config.cjs - Story editor tests
  • karma-dashboard.config.cjs - Dashboard tests
Tests are located in:
  • packages/story-editor/src/karma/ - Story editor integration tests
  • packages/dashboard/src/karma/ - Dashboard integration tests

Custom Karma Matchers

Available custom matchers:
  • toBeEmpty
  • toHaveFocus
  • toHaveStyle
  • toHaveTextContent
  • toHaveProperty
  • toBeOneOf
  • toHaveNoViolations (accessibility testing)

Debugging Karma Tests

1

Focus the test

Change it to fit for the test you want to debug:
fit('should do something', async () => {
  // test code
});
2

Add pause command

Add await karmaPause() where you want to pause:
fit('should do something', async () => {
  // test setup
  await karmaPause();
  // remaining test code
});
3

Run in watch mode

npm run test:karma:story-editor:watch
4

Open debug view

Click “DEBUG” button in the Karma window to open http://localhost:9876/debug.html
5

Debug in DevTools

Open DevTools and execute karmaResume() in the console to continue

E2E Tests (Puppeteer)

End-to-end tests use Puppeteer with Jest to test the full user experience.

Running E2E Tests

npm run test:e2e

E2E Test Configuration

Configuration: packages/e2e-tests/src/jest.config.js Tests location: packages/e2e-tests/src/specs/ Test utilities: packages/e2e-test-utils/src/
E2E tests run serially (--runInBand) to avoid conflicts, as they share the same WordPress instance.

Using Custom WordPress URL

By default, E2E tests use the Docker environment. To use a different WordPress installation:
WP_BASE_URL=https://web-stories.local npm run test:e2e

Debugging E2E Tests

Non-headless mode:
PUPPETEER_HEADLESS=false npm run test:e2e
Slow down interactions:
PUPPETEER_SLOWMO=200 npm run test:e2e
Node debugger:
npm run test:e2e:debug
Debug browser context:
PUPPETEER_DEVTOOLS=true PUPPETEER_HEADLESS=false npm run test:e2e:debug

Writing E2E Tests

import {
  createNewStory,
  insertStoryTitle,
  publishStory,
} from '@web-stories-wp/e2e-test-utils';

describe('Story Creation', () => {
  it('should create a new story', async () => {
    await createNewStory();
    await insertStoryTitle('My Test Story');
    await publishStory();
    
    await expect(page).toMatchElement('.editor-post-publish-panel__header-published');
  });
});

E2E Test Utilities

Common utilities from @web-stories-wp/e2e-test-utils:
  • createNewStory() - Create a new story
  • insertStoryTitle() - Set story title
  • publishStory() - Publish the story
  • uploadMedia() - Upload media files
  • And many more…
See packages/e2e-test-utils/src/ for all available utilities.

Test Plugins

Utility plugins for testing specific scenarios can be added to packages/e2e-tests/src/plugins/ and activated during tests.

Common Testing Mistakes

JavaScript/E2E:
  • Missing await on statements/assertions
  • Not waiting for elements to be present and visible before interacting
  • Race conditions in async operations
General:
  • Overusing snapshot tests instead of specific assertions
  • Not cleaning up test state between tests
  • Testing implementation details instead of behavior

Useful Testing Resources

JavaScript Testing

E2E Testing

Next Steps

Contributing

Learn how to contribute your changes

Architecture

Understand the project structure

Build docs developers (and LLMs) love