Back to Repositories

Testing Webpack Message Formatting Implementation in Create-React-App

This test suite validates webpack message formatting functionality in Create React App, focusing on various error cases and build-time messages. It ensures proper error handling and user-friendly message display for common development issues.

Test Coverage Overview

The test suite provides comprehensive coverage of webpack message formatting scenarios.

Key areas tested include:
  • Babel syntax errors
  • CSS syntax errors
  • Export-related issues (unknown, aliased, missing defaults)
  • Package dependencies
  • ESLint warnings and errors
  • File system cases (missing files, case sensitivity)
  • Sass import handling

Implementation Analysis

The testing approach uses async/await patterns with Jest for executing build scripts and validating output messages. Each test case copies a specific fixture file to the test environment, runs the build process, and verifies stdout/stderr output against snapshots.

The implementation handles platform-specific path formatting and includes cross-platform compatibility checks.

Technical Details

Testing tools and setup:
  • Jest as the testing framework
  • fs-extra for file operations
  • Custom test setup utilities
  • Snapshot testing for output validation
  • Platform-specific path normalization
  • Shared test setup configuration

Best Practices Demonstrated

The test suite exemplifies several testing best practices including isolation of test cases, comprehensive error scenario coverage, and platform-specific handling.

Notable practices:
  • Consistent test case structure
  • Proper error message formatting
  • Platform-agnostic path handling
  • Snapshot testing for output verification
  • Clear test case naming and organization

facebook/create-react-app

test/fixtures/webpack-message-formatting/index.test.js

            
'use strict';

const testSetup = require('../__shared__/test-setup');

const fs = require('fs-extra');
const path = require('path');

test('formats babel syntax error', async () => {
  fs.copySync(
    path.join(__dirname, 'src', 'AppBabel.js'),
    path.join(testSetup.testDirectory, 'src', 'App.js')
  );

  const { stdout, stderr } = await testSetup.scripts.build();
  expect({ stdout, stderr }).toMatchSnapshot();
});

test('formats css syntax error', async () => {
  fs.copySync(
    path.join(__dirname, 'src', 'AppCss.js'),
    path.join(testSetup.testDirectory, 'src', 'App.js')
  );

  const { stdout, stderr } = await testSetup.scripts.build();
  expect({ stdout, stderr }).toMatchSnapshot();
});

test('formats unknown export', async () => {
  fs.copySync(
    path.join(__dirname, 'src', 'AppUnknownExport.js'),
    path.join(testSetup.testDirectory, 'src', 'App.js')
  );

  const { stdout, stderr } = await testSetup.scripts.build();
  expect({ stdout, stderr }).toMatchSnapshot();
});

test('formats aliased unknown export', async () => {
  fs.copySync(
    path.join(__dirname, 'src', 'AppAliasUnknownExport.js'),
    path.join(testSetup.testDirectory, 'src', 'App.js')
  );

  const { stdout, stderr } = await testSetup.scripts.build();
  expect({ stdout, stderr }).toMatchSnapshot();
});

test('formats no default export', async () => {
  fs.copySync(
    path.join(__dirname, 'src', 'AppNoDefault.js'),
    path.join(testSetup.testDirectory, 'src', 'App.js')
  );

  const { stdout, stderr } = await testSetup.scripts.build();
  expect({ stdout, stderr }).toMatchSnapshot();
});

test('formats missing package', async () => {
  fs.copySync(
    path.join(__dirname, 'src', 'AppMissingPackage.js'),
    path.join(testSetup.testDirectory, 'src', 'App.js')
  );

  let { stdout, stderr } = await testSetup.scripts.build();
  if (process.platform === 'win32') {
    stderr = stderr.replace('.\\src\\App.js', './src/App.js');
  }
  expect({ stdout, stderr }).toMatchSnapshot();
});

test('formats eslint warning', async () => {
  fs.copySync(
    path.join(__dirname, 'src', 'AppLintWarning.js'),
    path.join(testSetup.testDirectory, 'src', 'App.js')
  );

  let { stdout, stderr } = await testSetup.scripts.build();
  const sizeIndex = stdout.indexOf('File sizes after gzip');
  if (sizeIndex !== -1) {
    stdout = stdout.substring(0, sizeIndex);
  }
  expect({ stdout, stderr }).toMatchSnapshot();
});

test('formats eslint error', async () => {
  fs.copySync(
    path.join(__dirname, 'src', 'AppLintError.js'),
    path.join(testSetup.testDirectory, 'src', 'App.js')
  );

  const { stdout, stderr } = await testSetup.scripts.build();
  expect({ stdout, stderr }).toMatchSnapshot();
});

test('helps when users tries to use sass', async () => {
  fs.copySync(
    path.join(__dirname, 'src', 'AppSass.js'),
    path.join(testSetup.testDirectory, 'src', 'App.js')
  );

  const { stdout, stderr } = await testSetup.scripts.build();
  expect(stdout).toBeFalsy();
  // TODO: Snapshots differ between Node 10/12 as the call stack log output has changed.
  expect(stderr).toContain(
    'To import Sass files, you first need to install sass.'
  );
});

test('formats file not found error', async () => {
  fs.copySync(
    path.join(__dirname, 'src', 'AppUnknownFile.js'),
    path.join(testSetup.testDirectory, 'src', 'App.js')
  );

  let { stdout, stderr } = await testSetup.scripts.build();
  if (process.platform === 'win32') {
    stderr = stderr
      .replace('.\\src\\App.js', './src/App.js')
      .replace('.\\src', './src');
  }
  expect({ stdout, stderr }).toMatchSnapshot();
});

test('formats case sensitive path error', async () => {
  fs.copySync(
    path.join(__dirname, 'src', 'AppIncorrectCase.js'),
    path.join(testSetup.testDirectory, 'src', 'App.js')
  );

  const { stderr } = await testSetup.scripts.start({ smoke: true });
  if (process.platform === 'darwin') {
    // eslint-disable-next-line jest/no-conditional-expect
    expect(stderr).toMatch(
      `Cannot find file: 'export5.js' does not match the corresponding name on disk: './src/Export5.js'.`
    );
  } else {
    // eslint-disable-next-line jest/no-conditional-expect
    expect(stderr).not.toEqual(''); // TODO: figure out how we can test this on Linux/Windows
    // I believe getting this working requires we tap into enhanced-resolve
    // pipeline, which is debt we don't want to take on right now.
  }
});

test('formats out of scope error', async () => {
  fs.copySync(
    path.join(__dirname, 'src', 'AppOutOfScopeImport.js'),
    path.join(testSetup.testDirectory, 'src', 'App.js')
  );

  let { stdout, stderr } = await testSetup.scripts.build();
  if (process.platform === 'win32') {
    stderr = stderr.replace('.\\src\\App.js', './src/App.js');
  }
  expect({ stdout, stderr }).toMatchSnapshot();
});