Back to Repositories

Testing React Playground Component Compilation and Editor Functionality in facebook/react

This test suite validates the React Playground’s end-to-end functionality, focusing on code compilation, editor interactions, and handling of different code patterns. It ensures the playground correctly processes TypeScript and Flow syntax while maintaining proper component rendering.

Test Coverage Overview

The test suite provides comprehensive coverage of the React Playground’s core functionalities.

Key areas tested include:
  • Editor initialization and page loading
  • Hash-based code compilation
  • Reset functionality
  • Various code pattern compilations including memo usage
  • TypeScript and Flow syntax support

Implementation Analysis

The testing approach utilizes Playwright for end-to-end testing, implementing a systematic verification of the playground’s features. The tests employ snapshot testing for output validation and incorporate screenshot captures for visual regression testing.

Technical patterns include:
  • Store encoding for state management
  • Code formatting with Prettier
  • Dynamic test case generation
  • Async/await patterns for reliable testing

Technical Details

Testing tools and configuration:
  • Playwright test runner
  • Monaco Editor integration
  • Prettier for code formatting
  • Custom store encoding utilities
  • Screenshot capture configuration
  • Snapshot testing implementation

Best Practices Demonstrated

The test suite exemplifies high-quality testing practices through structured organization and robust validation methods.

Notable practices include:
  • Modular test case organization
  • Comprehensive error handling
  • Visual regression testing
  • Parameterized test cases
  • Clean test isolation

facebook/react

compiler/apps/playground/__tests__/e2e/page.spec.ts

            
/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import {expect, test} from '@playwright/test';
import {encodeStore, type Store} from '../../lib/stores';
import {format} from 'prettier';

function formatPrint(data: Array<string>): Promise<string> {
  return format(data.join(''), {parser: 'babel'});
}

const TEST_CASE_INPUTS = [
  {
    name: 'module-scope-use-memo',
    input: `
'use memo';
export default function TestComponent({ x }) {
  return <Button>{x}</Button>;
}`,
  },
  {
    name: 'module-scope-use-no-memo',
    input: `
'use no memo';
export default function TestComponent({ x }) {
  return <Button>{x}</Button>;
}`,
  },
  {
    name: 'use-memo',
    input: `
function TestComponent({ x }) {
  'use memo';
  return <Button>{x}</Button>;
}
const TestComponent2 = ({ x }) => {
  'use memo';
  return <Button>{x}</Button>;
};`,
  },
  {
    name: 'use-no-memo',
    input: `
const TestComponent = function() {
  'use no memo';
  return <Button>{x}</Button>;
};
const TestComponent2 = ({ x }) => {
  'use no memo';
  return <Button>{x}</Button>;
};`,
  },
  {
    name: 'todo-function-scope-does-not-beat-module-scope',
    input: `
'use no memo';
function TestComponent({ x }) {
  'use memo';
  return <Button>{x}</Button>;
}`,
  },
  {
    name: 'parse-typescript',
    input: `
function Foo() {
  const x = foo() as number;
  return <div>{x}</div>;
}
`,
    noFormat: true,
  },
  {
    name: 'parse-flow',
    input: `
// @flow
function useFoo(propVal: {+baz: number}) {
  return <div>{(propVal.baz as number)}</div>;
}
    `,
    noFormat: true,
  },
];

test('editor should open successfully', async ({page}) => {
  await page.goto(`/`, {waitUntil: 'networkidle'});
  await page.screenshot({
    fullPage: true,
    path: 'test-results/00-fresh-page.png',
  });
});

test('editor should compile from hash successfully', async ({page}) => {
  const store: Store = {
    source: `export default function TestComponent({ x }) {
      return <Button>{x}</Button>;
    }
    `,
  };
  const hash = encodeStore(store);
  await page.goto(`/#${hash}`, {waitUntil: 'networkidle'});

  // User input from hash compiles
  await page.screenshot({
    fullPage: true,
    path: 'test-results/01-compiles-from-hash.png',
  });
  const text =
    (await page.locator('.monaco-editor').nth(1).allInnerTexts()) ?? [];
  const output = await formatPrint(text);

  expect(output).not.toEqual('');
  expect(output).toMatchSnapshot('01-user-output.txt');
});

test('reset button works', async ({page}) => {
  const store: Store = {
    source: `export default function TestComponent({ x }) {
      return <Button>{x}</Button>;
    }
    `,
  };
  const hash = encodeStore(store);
  await page.goto(`/#${hash}`, {waitUntil: 'networkidle'});

  // Reset button works
  page.on('dialog', dialog => dialog.accept());
  await page.getByRole('button', {name: 'Reset'}).click();
  await page.screenshot({
    fullPage: true,
    path: 'test-results/02-reset-button-works.png',
  });
  const text =
    (await page.locator('.monaco-editor').nth(1).allInnerTexts()) ?? [];
  const output = await formatPrint(text);

  expect(output).not.toEqual('');
  expect(output).toMatchSnapshot('02-default-output.txt');
});

TEST_CASE_INPUTS.forEach((t, idx) =>
  test(`playground compiles: ${t.name}`, async ({page}) => {
    const store: Store = {
      source: t.input,
    };
    const hash = encodeStore(store);
    await page.goto(`/#${hash}`, {waitUntil: 'networkidle'});
    await page.screenshot({
      fullPage: true,
      path: `test-results/03-0${idx}-${t.name}.png`,
    });

    const text =
      (await page.locator('.monaco-editor').nth(1).allInnerTexts()) ?? [];
    let output: string;
    if (t.noFormat) {
      output = text.join('');
    } else {
      output = await formatPrint(text);
    }

    expect(output).not.toEqual('');
    expect(output).toMatchSnapshot(`${t.name}-output.txt`);
  }),
);