cypress: webpack v5 error when module contains dynamic import: "Automatic publicPath is not supported in this browser"

Current behavior

When using webpack v5 and a test contains a dynamic import (or imports a module that contains a dynamic import) the test will fail with the following error: Automatic publicPath is not supported in this browser.

  Running:  example.test.js                                                                 (1 of 1)


  1) An uncaught error was detected outside of a test

  0 passing (214ms)
  1 failing

  1) An uncaught error was detected outside of a test:
     Error: The following error originated from your test code, not from Cypress.

  > Automatic publicPath is not supported in this browser

When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.

Cypress could not associate this error to any specific test.

We dynamically generated a new test to display this failure.
      at eval (http://localhost:61013/__cypress/tests?p=cypress/integration/example.test.js:155:34)
      at eval (http://localhost:61013/__cypress/tests?p=cypress/integration/example.test.js:158:13)
      at eval (http://localhost:61013/__cypress/tests?p=cypress/integration/example.test.js:261:12)
      at eval (<anonymous>)

Desired behavior

No error.

Test code to reproduce

Full reduced test case: https://github.com/OliverJAsh/cypress-webpack-5-public-path-error

This might seem like a contrived example because I have reduced it down from a much larger use case.

cypress/integration/lazy.js:

export const a = 1;

cypress/integration/example.test.js:

() => import('./lazy');

describe("foo", () => {
    it("works", () => {});
});

cypress/plugins/index.js:

const webpackPreprocessor = require("@cypress/webpack-preprocessor");

module.exports = (on, config) => {
    on(
        "file:preprocessor",
        webpackPreprocessor({
            webpackOptions: {},
        })
    );
};

cypress.json:

{}

package.json:

{
  "dependencies": {
    "@cypress/webpack-preprocessor": "^5.9.1",
    "cypress": "^8.5.0",
    "webpack": "^5.58.1"
  }
}

Cypress Version

8.5.0

Other

I was able to workaround the error by setting publicPath: '' inside of here:

https://github.com/cypress-io/cypress/blob/2c6cd9e09923c853ad96df3efc3496e27a7c1b3e/npm/webpack-preprocessor/index.ts#L204

I’m not really sure I understand why this issue happens nor how this fixes the issue, but FWIW this fix was recommended here: https://stackoverflow.com/questions/64294706/webpack5-automatic-publicpath-is-not-supported-in-this-browser?rq=1.

The issue does not reproduce when using webpack v4.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 3
  • Comments: 15 (4 by maintainers)

Commits related to this issue

Most upvoted comments

Is there any update on this? Seems like it’s breaking even when I’m not using mini-css-extract-plugin.

@Circle6 copying this over from another issue. Add the following code into your plugins/index.js file

  const publicPath = ' ';
  let outputOptions;
  Object.defineProperty(webpackOptions, 'output', {
    get: () => {
      return { ...outputOptions, publicPath };
    },
    set: function (x) {
      outputOptions = x;
    },
  });

My complete plugins/index.js config file looks like this (Angular + Nx):

import { dynamicImport } from 'tsimportlib';

// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************

// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
module.exports = async (on, config) => {
  // `on` is used to hook into various events Cypress emits
  // `config` is the resolved Cypress config
  const linkerPlugin = await dynamicImport('@angular/compiler-cli/linker/babel', module);
  const webpackPreprocessor = require('@cypress/webpack-batteries-included-preprocessor');
  const webpackOptions = webpackPreprocessor.defaultOptions.webpackOptions;

  webpackOptions.module.rules.unshift({
    test: /\.mjs$/,
    loader: 'babel-loader',
    options: {
      compact: false,
      plugins: [linkerPlugin.default],
    },
    resolve: {
      fullySpecified: false,
    },
  });

  const publicPath = ' ';
  let outputOptions;
  Object.defineProperty(webpackOptions, 'output', {
    get: () => {
      return { ...outputOptions, publicPath };
    },
    set: function (x) {
      outputOptions = x;
    },
  });

  on(
    'file:preprocessor',
    webpackPreprocessor({
      webpackOptions: webpackOptions,
      typescript: require.resolve('typescript'),
    })
  );

  return config;
};

I’ve tried running through all the given solutions here and i’m still seeing this issue. I’m even explicitly setting the publicPath and it’s still throwing “Automatic publicPath is not supported in this browser”.

Any progress on fixing this?

I think there are multiple ways that you can end up triggering webpack’s automatic publicPath detection, which then fails with the not-very-helpful error message from the issue title. Its definitely not just mini-css-extract-plugin.

Instead of trying to override the webpack configuration for all webpack core/plugins that have a publicPath setting, I have the following vile hack to trick webpack’s automatic detection into not throwing an error. Put

/**
 * Workaround for the "Automatic publicPath is not supported in this browser" fail
 *
 * See https://github.com/cypress-io/cypress/issues/18435 for more
 * details. Should be removed when cypress switches to webpack 5.
 */
var scripts = document.getElementsByTagName("script");
scripts[scripts.length - 1].src = ' ';  // yes, thats a single space and not the empty string

into your cypress/support/index.ts (or wherever supportFile from cypress.json points to)

Well since I wrote my comment an hour ago, I found a longer term workaround until a more permanent solution is provided that doesn’t involve manually modifying the webpack-preprocessor. I modified the inline webpack config for my file:preprocessor by adding mini-css-extract-plugin.

const wp = require("@cypress/webpack-preprocessor");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const webpackOptions = {
  devtool: false,
  ...
  plugins: [new MiniCssExtractPlugin()],
  module: {
    rules: [
      ...
      {
        test: /\.css$/,
        use: [
          "style-loader",
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              esModule: false,
              publicPath: "",
            },
          },
          "css-loader",
        ],
      },
    ],
  },
};

const options = {
  webpackOptions,
};

module.exports = wp(options);

The defineProperty(webpackConfig, 'output') hack worked for me. My implementation was different enough from the prior example that I’ll include it here to help others.

Here’s a generic plugins/index.ts that should work with an external webpack config file:

/* Cypress plugins/index.ts */
/// <reference types="cypress" />

import webpackPreprocessor from '@cypress/webpack-preprocessor';

import webpackConfig from '../cypress.webpack.config';

// Patch webpackConfig.output so it returns a `publicPath`, even though output is overwritten in webpackPreprocessor
// Credit: https://github.com/cypress-io/cypress/issues/8900#issuecomment-866897397
// https://github.com/cypress-io/cypress/issues/18435
const publicPath = '';
let outputOptions = {};
Object.defineProperty(webpackConfig, 'output', {
  get: () => {
    return { ...outputOptions, publicPath };
  },
  set: function (x) {
    outputOptions = x;
  }
});

/**
 * @type {Cypress.PluginConfig}
 */
module.exports = /*async if needed*/ (on: Cypress.PluginEvents, config: Cypress.ResolvedConfigOptions) => {

  on('file:preprocessor', webpackPreprocessor({
    webpackOptions: webpackConfig
  }));

  return config;
};

Is there a reason why setting publicPath: '' in https://github.com/cypress-io/cypress/blob/master/npm/webpack-preprocessor/index.ts#L204 has never been done, or is it simply because nobody bothered to make a PR ? This is the only workaround that works for me.

Thanks for the report and detailed investigation, webpack errors are always hard to track down.

The change to @cypress/webpack-preprocessor looks fairly straightforward, if anyone wants to submit a PR we’d be happy to review and get it in. Otherwise, I’ve added this to our list to discuss, but can’t promise when the team will get around to a fix, especially since @thril has provided a working / less hacky solution.