Skip to main content

Overview

sunpeak supports both unit testing and end-to-end testing against the ChatGPT Simulator. The setup differs depending on whether you’re using the full framework or just the library.
The sunpeak framework includes pre-configured testing out of the box.TL;DR: Run pnpm test for unit tests and pnpm test:e2e for end-to-end tests.

Testing Commands

pnpm test
command
Run unit tests with Vitest.
pnpm test:e2e
command
Run end-to-end tests with Playwright against the ChatGPT Simulator.

Unit Testing

The framework uses Vitest with configuration in vitest.config.ts. The test environment includes:
  • jsdom: Browser-like environment for React component testing
  • @testing-library/react: For rendering and testing components
  • @testing-library/jest-dom: Additional matchers for assertions
All configuration is in src/test/setup.ts.

End-to-End Testing

E2E tests use Playwright and are located in tests/e2e/ with the naming convention *.spec.ts.The dev server runs automatically when you execute pnpm test:e2e—Playwright starts it before running tests.
# Run all e2e tests
pnpm test:e2e

# Run with UI mode for debugging
pnpm test:e2e --ui

# Run a specific test file
pnpm test:e2e tests/e2e/albums.spec.ts

Writing E2E Tests

Use the createSimulatorUrl utility to generate type-safe URLs for e2e tests:
import { test, expect } from '@playwright/test';
import { createSimulatorUrl } from 'sunpeak/chatgpt';

test('should render in light mode', async ({ page }) => {
  await page.goto(createSimulatorUrl({
    simulation: 'albums-show',
    theme: 'light',
  }));

  const albumCard = page.locator('button:has-text("Summer Slice")');
  await expect(albumCard).toBeVisible();
});

test('should render in fullscreen mode', async ({ page }) => {
  await page.goto(createSimulatorUrl({
    simulation: 'albums-show',
    theme: 'dark',
    displayMode: 'fullscreen',
  }));

  // Test fullscreen-specific behavior
});

URL Parameters

The createSimulatorUrl function accepts parameters for configuring theme, display mode, device type, safe area insets, and more. See the ChatGPTSimulator API Reference for the complete list.

Example E2E Test Structure

A typical e2e test file tests a resource across different modes:
import { test, expect } from '@playwright/test';
import { createSimulatorUrl } from 'sunpeak/chatgpt';

test.describe('Albums Resource', () => {
  test.describe('Light Mode', () => {
    test('should render album cards with correct styles', async ({ page }) => {
      await page.goto(createSimulatorUrl({
        simulation: 'albums-show',
        theme: 'light',
      }));

      const albumCard = page.locator('button:has-text("Summer Slice")');
      await expect(albumCard).toBeVisible();

      // Verify styles
      const styles = await albumCard.evaluate((el) => {
        const computed = window.getComputedStyle(el);
        return {
          cursor: computed.cursor,
          borderRadius: computed.borderRadius,
        };
      });

      expect(styles.cursor).toBe('pointer');
      expect(styles.borderRadius).toBe('12px');
    });
  });

  test.describe('Dark Mode', () => {
    test('should render with dark theme', async ({ page }) => {
      await page.goto(createSimulatorUrl({
        simulation: 'albums-show',
        theme: 'dark',
      }));

      // Test dark mode specific behavior
    });
  });

  test.describe('Fullscreen Mode', () => {
    test('should render in fullscreen', async ({ page }) => {
      await page.goto(createSimulatorUrl({
        simulation: 'albums-show',
        theme: 'light',
        displayMode: 'fullscreen',
      }));

      // Test fullscreen specific behavior
    });
  });
});

Testing Best Practices

Test one thing per test case. Clear tests are maintainable tests.
// Good
it('displays error message when API fails', () => {})

// Bad
it('test 1', () => {})
Test what users see and interact with, not implementation details:
// Good
expect(screen.getByText('Submit')).toBeInTheDocument();

// Bad
expect(component.props.buttonLabel).toBe('Submit');
Use createSimulatorUrl to test your resources in different configurations:
  • Light and dark themes
  • Inline, PiP, and fullscreen display modes
  • Different device types (mobile, tablet, desktop)
Use afterEach to reset state between tests:
afterEach(() => {
  // Clean up mocks, reset state, etc.
});

Additional Quality Tools (Optional)

pnpm add -D eslint @typescript-eslint/eslint-plugin
Add "lint": "eslint . --ext .ts,.tsx" to package.json scripts.
TypeScript is already installed. Add to package.json scripts:
"typecheck": "tsc --noEmit"
pnpm add -D prettier
Add "format": "prettier --write ." to package.json scripts.

Dive Deeper

Vitest Docs

Learn more about Vitest for unit testing.

Playwright Docs

Learn more about Playwright for end-to-end testing.