Back to Repositories

Testing Webpack Asset Processing and Module Integration in Create-React-App

This integration test suite validates webpack plugin functionality in Create React App, focusing on asset handling and module inclusion. It comprehensively tests various file types and their proper bundling behavior through webpack configuration.

Test Coverage Overview

The test suite provides extensive coverage of webpack’s asset handling capabilities.

Key areas tested include:
  • CSS/SCSS/SASS processing and modules
  • Image and SVG handling
  • JSON file inclusion
  • Module linking verification
  • File extension handling
Edge cases include files without extensions and unknown file types.

Implementation Analysis

The testing approach uses Jest’s async/await pattern for DOM manipulation and verification. Each test initializes a fresh DOM environment and validates the correct processing of different asset types.

Technical patterns include:
  • DOM cleanup after each test
  • Regular expression matching for CSS content
  • Base64 validation for images
  • Path verification for static assets

Technical Details

Testing infrastructure includes:
  • Jest as the testing framework
  • Custom DOM initialization utility
  • File fetching mechanism for E2E tests
  • CSS content matching utilities
  • Webpack 5 asset modules configuration

Best Practices Demonstrated

The test suite exemplifies high-quality integration testing practices.

Notable implementations include:
  • Isolated test environments
  • Proper test cleanup
  • Comprehensive asset type coverage
  • Consistent assertion patterns
  • Environment-aware testing logic

facebook/create-react-app

packages/react-scripts/fixtures/kitchensink/template/integration/webpack.test.js

            
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import initDOM, { fetchFile } from './initDOM';
import url from 'url';

const matchCSS = (doc, regexes) => {
  if (process.env.E2E_FILE) {
    const elements = doc.getElementsByTagName('link');
    let href = '';
    for (const elem of elements) {
      if (elem.rel === 'stylesheet') {
        href = elem.href;
      }
    }

    const textContent = fetchFile(url.parse(href));
    for (const regex of regexes) {
      expect(textContent).toMatch(regex);
    }
  } else {
    for (let i = 0; i < regexes.length; ++i) {
      expect(
        doc.getElementsByTagName('style')[i].textContent.replace(/\s/g, '')
      ).toMatch(regexes[i]);
    }
  }
};

describe('Integration', () => {
  describe('webpack plugins', () => {
    let doc;

    afterEach(() => {
      doc && doc.defaultView.close();
      doc = undefined;
    });

    it('css inclusion', async () => {
      doc = await initDOM('css-inclusion');
      matchCSS(doc, [
        /html\{/,
        /#feature-css-inclusion\{background:.+;color:.+}/,
      ]);
    });

    it('css modules inclusion', async () => {
      doc = await initDOM('css-modules-inclusion');
      matchCSS(doc, [
        /.+style_cssModulesInclusion__.+\{background:.+;color:.+}/,
        /.+assets_cssModulesIndexInclusion__.+\{background:.+;color:.+}/,
      ]);
    });

    it('scss inclusion', async () => {
      doc = await initDOM('scss-inclusion');
      matchCSS(doc, [/#feature-scss-inclusion\{background:.+;color:.+}/]);
    });

    it('scss modules inclusion', async () => {
      doc = await initDOM('scss-modules-inclusion');
      matchCSS(doc, [
        /.+scss-styles_scssModulesInclusion.+\{background:.+;color:.+}/,
        /.+assets_scssModulesIndexInclusion.+\{background:.+;color:.+}/,
      ]);
    });

    it('sass inclusion', async () => {
      doc = await initDOM('sass-inclusion');
      matchCSS(doc, [/#feature-sass-inclusion\{background:.+;color:.+}/]);
    });

    it('sass modules inclusion', async () => {
      doc = await initDOM('sass-modules-inclusion');
      matchCSS(doc, [
        /.+sass-styles_sassModulesInclusion.+\{background:.+;color:.+}/,
        /.+assets_sassModulesIndexInclusion.+\{background:.+;color:.+}/,
      ]);
    });

    it('image inclusion', async () => {
      doc = await initDOM('image-inclusion');

      expect(doc.getElementById('feature-image-inclusion').src).toMatch(
        /^data:image\/jpeg;base64.+=$/
      );
    });

    it('no ext inclusion', async () => {
      doc = await initDOM('no-ext-inclusion');

      // Webpack 4 added a default extension ".bin" seems like webpack 5 asset modules do not
      expect(
        doc.getElementById('feature-no-ext-inclusion').getAttribute('href')
      ).toMatch(/\/static\/media\/aFileWithoutExt\.[a-f0-9]+$/);
    });

    it('json inclusion', async () => {
      doc = await initDOM('json-inclusion');

      expect(doc.getElementById('feature-json-inclusion').textContent).toBe(
        'This is an abstract.'
      );
    });

    it('linked modules', async () => {
      doc = await initDOM('linked-modules');

      expect(doc.getElementById('feature-linked-modules').textContent).toBe(
        '2.0.0'
      );
    });

    it('svg inclusion', async () => {
      doc = await initDOM('svg-inclusion');
      expect(doc.getElementById('feature-svg-inclusion').src).toMatch(
        /\/static\/media\/logo\..+\.svg$/
      );
    });

    it('svg component', async () => {
      doc = await initDOM('svg-component');

      expect(doc.getElementById('feature-svg-component').textContent).toBe('');
    });

    it('svg in css', async () => {
      doc = await initDOM('svg-in-css');
      matchCSS(doc, [/\/static\/media\/logo\..+\.svg/]);
    });

    it('unknown ext inclusion', async () => {
      doc = await initDOM('unknown-ext-inclusion');

      expect(
        doc.getElementById('feature-unknown-ext-inclusion').getAttribute('href')
      ).toMatch(/\/static\/media\/aFileWithExt\.[a-f0-9]+\.unknown$/);
    });
  });
});