cypress-axe icon indicating copy to clipboard operation
cypress-axe copied to clipboard

Resolved path for `axe-core/axe.min.js` is incorrect if the Cypress Config is in a sub-directory when using Cypress v10+

Open aturingmachine opened this issue 3 years ago • 17 comments

axe-core/axe.min.js cannot be loaded if the Cypress config is contained in a sub directory. The resolved path traverses from the location of the config, but starts at the project root where the command was invoked.

This seems to be due to require.resolve returning the path relative to the configuration file, whereas when running in Cypress Versions <10 require.resolve returns the path relative to the directory the initial command was invoked.

So assuming a project with a directory structure of:

/home/user/dev/project/
- e2e/
  - config/
    - cypress.config.ts
- src/
- node_modules/
- package.json

The path we attempt to require for axe-core/axe.min.js will be /home/user/dev/node_modules/axe-core/axe.min.js.

A minimum reproduction can be found using my fork of this repo.

image

aturingmachine avatar Jun 17 '22 00:06 aturingmachine

We are also experiencing this issue

ldeveber avatar Jun 28 '22 18:06 ldeveber

This might be a duplicate of #84 and #73 if a preprocessor is configured.

jonkoops avatar Jun 29 '22 10:06 jonkoops

Fun fact: it only throws this error with cypress run for us. When using cypress open it seems to work fine, but we can't use that on the build servers :(

This is using the same config file for both commands.

ldeveber avatar Jul 26 '22 18:07 ldeveber

Facing the same issue, both when cypress runand cypress open.

Environment: node 16.16.0 yarn 3.2.2

devDependencies:

"axe-core": "^4.4.3",
"cypress": "^10.5.0",
"cypress-axe": "^1.0.0"

christianmartinnies avatar Aug 16 '22 19:08 christianmartinnies

still does not work with cypress 10.6

ldeveber avatar Aug 18 '22 14:08 ldeveber

We are experiencing this issue on Cypress 10.3. We have a cypress.config.ts in project root. In <projectRoot>/cypress/configurations we have several custom configurations that extend the main configuration (via import). We override with the Cypress --config-file command line. When running tests with cypress-axe we experience the same error as above.

rdylanwalker avatar Sep 06 '22 19:09 rdylanwalker

I'm experiencing this as well, see CI logs here: https://github.com/ismay/portfolio/actions/runs/3067878096/jobs/4954656842#step:3:266

Looking at the resolved path, it seems like that path is correct relative to my config file (which is in ./.cypress): cy.readFile("../node_modules/axe-core/axe.min.js").

However, judging from the error it seems like cy.readFile("../node_modules/axe-core/axe.min.js") is resolving from the root of the project. So it ends up looking one level too high. It tries to find the file at /home/runner/work/portfolio/node_modules/axe-core/axe.min.js, but it should be looking for /home/runner/work/portfolio/portfolio/node_modules/axe-core/axe.min.js

The cypress docs mention that readFile resolves relative to the config file: https://docs.cypress.io/api/commands/readfile#Arguments, at least that's how I read it: A path to a file within the project root (the directory that contains the Cypress configuration file).. But that seems to be incorrect.

Judging from this issue the base path is actually hardcoded to the project root: https://github.com/cypress-io/cypress/issues/2673 (see the related issues as well). So if that's correct, to fix this I think we have to make sure that we're resolving from the root of the project here: https://github.com/component-driven/cypress-axe/blob/master/src/index.ts#L26. See the docs here: https://nodejs.org/api/modules.html#requireresolverequest-options. Maybe https://github.com/sindresorhus/pkg-dir would be useful to resolve this.

ismay avatar Sep 16 '22 12:09 ismay

Here is a patch you can use to fix it with the patch-package Updated just this file node_modules/cypress-axe/dist/index.js

 exports.injectAxe = function () {
-    var fileName = typeof (require === null || require === void 0 ? void 0 : require.resolve) === 'function'
-        ? require.resolve('axe-core/axe.min.js')
-        : 'node_modules/axe-core/axe.min.js';
+    var fileName = 'node_modules/axe-core/axe.min.js';
+
     cy.readFile(fileName).then(function (source) {
         return cy.window({ log: false }).then(function (window) {
             window.eval(source);

artvichi avatar Nov 10 '22 18:11 artvichi

If that fixes it for you it would be good to submit your patch as a PR.

ismay avatar Nov 11 '22 12:11 ismay

@artvichi this won't work for workspaces when axe-core i installed as dependency of cypress-axe in workspace root, thats how its working on yarn.3

When install using workspace scoping packages:

- project `a` node_modules (here's always will be installed axe-core)
--- workspace `one` node_modules (cypress-axe when install with `yarn workspace @a/one add -D cypress-axe)

if install normally cd workspace; yarn add -D cypress-axe then package will be installed on root node_modules either way resolve of axe-core fail in

exports.injectAxe = function () {
    var fileName = typeof (require === null || require === void 0 ? void 0 : require.resolve) === 'function'
        ? require.resolve('axe-core/axe.min.js')
        : 'node_modules/axe-core/axe.min.js';
    cy.readFile(fileName).then(function (source) {
        return cy.window({ log: false }).then(function (window) {
            window.eval(source);
        });
    });
};

Problem only occurs on vite because webpack resolves properly with require.resolve even if package is installed in different node_modules

EDIT: plugin should be compiled to commonjs AND esm, also should use cypress task to read/reasolve package via createRequire so vite would be able to resolve package same way as webpack.

Szymon-dziewonski avatar Nov 17 '22 15:11 Szymon-dziewonski

This seems to be due to require.resolve returning the path relative to the configuration file, whereas when running in Cypress Versions <10 require.resolve returns the path relative to the directory the initial command was invoked.

This all sounds exactly as the upstream issue: https://github.com/cypress-io/cypress/issues/22689. It makes no sense that all plugins in the ecosystem start patching themselves due to a bug in Cypress. Code coverage plugin is also seeing this: https://github.com/cypress-io/code-coverage/issues/586.

Work-around for now: https://github.com/cypress-io/cypress/issues/22689#issuecomment-1211732120

AriPerkkio avatar Dec 02 '22 09:12 AriPerkkio

This PR address this issue as well https://github.com/component-driven/cypress-axe/pull/150

dkryaklin avatar Dec 13 '22 16:12 dkryaklin

Any solution one year later?

Edit: To the person who marked this comment as spam - it is 2023 and this is still a persisting issue in this library.

pdimova avatar Jan 27 '23 14:01 pdimova

Hi,

I'm still seeing issues with this after upgrading cypress-axe version to 1.3.0 and axe-core to 4.6.3.

This is what I see in terminal when running my tests:

AssertionError: Timed out retrying after 2500ms: `cy.readFile("../../../../node_modules/axe-core/axe.min.js")` failed because the file does not exist at the following path:

`/node_modules/axe-core/axe.min.js`

https://on.cypress.io/readfile
      at ../../../node_modules/cypress-axe/dist/index.js.exports.injectAxe (webpack:////Users/blah/blah/blah/node_modules/cypress-axe/dist/index.js:20:0)

The file does exist in /node_modules/axe-core/axe.min.js

I've made the following change in my custom Cypress function that tests a11y (as indicated here):

Cypress.Commands.add('checkAccessibility', () => {
  const terminalLog = (violations) => {
    cy.task(
      'log',
      `${violations.length} accessibility violation${violations.length === 1 ? '' : 's'
      } ${violations.length === 1 ? 'was' : 'were'} detected`,
    );
    const violationData = violations.map(
      ({
        id, impact, description, nodes,
      }) => ({
        id,
        impact,
        description,
        nodes: nodes.length,
      }),
    );
    cy.task('table', violationData);
  };
  cy.injectAxe({ axeCorePath: '../../../../node_modules/axe-core/axe.min.js'})
    .checkA11y(null, null, terminalLog, true, {
      runOnly: {
        values: ['wcag2aa'],
      },
    });
});

My config files are nested as follows:

my-app/
├─ node_modules/
├─ tests/
│  ├─ e2e/
│  │  ├─ configs/
│  │  │  ├─ common.config.js
│  │  │  ├─ ui.config.js
│  │  │  ├─ visual.config.js
│  │  ├─ tests/
│  │  │  ├─ ui/
│  │  │  ├─ visual/
├─ .gitignore
├─ package.json
├─ README.md

Can someone please advise?

Many thanks!

9j0hn50n avatar Feb 14 '23 16:02 9j0hn50n

@9j0hn50n Hi,

Not sure why you are using ../../../../node_modules/axe-core/axe.min.js path. I would suggest trying

../../../node_modules/axe-core/axe.min.js or ./node_modules/axe-core/axe.min.js

dkryaklin avatar Feb 20 '23 15:02 dkryaklin

Lovely, thank you @dkryaklin!

I had tried ../../../node_modules/axe-core/axe.min.js but still got the mentioned error.

But ./node_modules/axe-core/axe.min.js did the trick.

Thanks again.

9j0hn50n avatar Feb 22 '23 11:02 9j0hn50n

I have a similar, but slightly bizarre issue. When I try to injectAxe in an afterEach it works fine, however if the beforeEach or test fails it can't fine the file axe-core/axe.min.js. Does anyone know why that would be? Here is my checkAxe custom command: -

Cypress.Commands.add('checkAxe', (wait?: number) => {
    if (wait) {
        cy.wait(wait);
    }
    cy.injectAxe();
    cy.configureAxe({
        rules: exceptions.map(rule => ({id: rule, enabled: false})),
        standards: accessibilityStandards
    });

    cy.window({log: false})
        .then((win: any) => {

            const context = {
                include: [win.document],
                exclude: exclusions,
            };

            const rules = exceptions.reduce((result, exception) => {
                result[exception] = {enabled: false};
                return result;
            }, {});

            return cy.checkA11y(context, <any>{runOnly: accessibilityStandards, rules});
        });
});

image

AshMcConnell avatar Jan 30 '24 12:01 AshMcConnell