Skip to content

Commit

Permalink
ci: minimum working example for jest-axe accessibility CI (#412)
Browse files Browse the repository at this point in the history
This PR implements the minimum overhead required to run `jest-axe` via GitHub Actions on a set of predefined components. In order, we:

1. install `jest`, `jest-axe`, `@testing-library/react`, `@testing-library/jest-dom`, the minimum set up for a viable test infrastructure
    1. to install `jest`, we add a general `jest.config.js` as a jest configuration file, and `jest.setup.js` as a pre-test setup file (automatically importing our dependencies)
    2. creates a set of mock module mappers for "non-code" webpack elements; a stub file mock (`__mocks__/fileMock.js`) for most files, and the `identity-obj-proxy` to proxy-back CSS/SASS/SCSS class names
    3. adds the `next` preset to babel
    4. adjusts the ESLint config to use the jest environment
2. implements a rudimentary accessibility test in `NewsArticle.test.js`, which is a simple subcomponent with no errors. You can validate this working by adding an arbitrary `<img />` to `NewsArticle.js`; `npm test` will throw an error.
3. implements a rudimentary failing accessibility test in `Footer.test.js`, where an `<ul>` has children that are not `<li>` - showing that negatives work. I then resolve this issue.
4. updates the "Node.JS CI" action to be "Build and Test", and runs `npm test` in it

Ideally, you should be able to add more test files following the configuration in `NewsArticle.test.js`; note the strange `let` pattern with the async version of `act`.

I didn't use the native next testing plugin, since that seems blocked by the upgrade to Next 12 (#409).

Part of #218, supersedes #301.

Relevant reading:

* [Configuring Jest](https://jestjs.io/docs/configuration)
* [`act()` in React](https://reactjs.org/docs/test-utils.html#act)
* [Blog Post: Next.js 11: Setup & Config for Testing, Linting, and Absolute Imports](https://benjaminwfox.medium.com/next-js-setup-config-for-testing-linting-and-absolute-imports-605959d7bd6f)
  • Loading branch information
mattxwang authored Jan 31, 2022
1 parent e97828b commit ba8a649
Show file tree
Hide file tree
Showing 11 changed files with 8,957 additions and 3,550 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["next/babel"]
}
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module.exports = {
browser: true,
es2020: true,
node: true,
jest: true,
},
extends: [
'eslint:recommended',
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/node-build.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: Node.js CI
name: Build and Test

on:
push:
Expand Down Expand Up @@ -30,6 +30,6 @@ jobs:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm run build
# - run: npm test
# env:
# CI: true
- run: npm test
env:
CI: true
1 change: 1 addition & 0 deletions __mocks__/fileMock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = 'test-file-stub';
20 changes: 20 additions & 0 deletions __tests__/components/Footer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
import React from 'react';
import { act } from 'react-dom/test-utils';
import Footer from '../../components/Footer';

expect.extend(toHaveNoViolations);

it('has no axe violations', async () => {
// this let / async construct is required since next/image rendering
// is a side effect that needs to be captured with async act
// see https://reactjs.org/docs/test-utils.html#act
let results;
await act(async () => {
const { container } = render(<Footer />);
results = await axe(container);
});

expect(results).toHaveNoViolations();
});
22 changes: 22 additions & 0 deletions __tests__/components/NewsArticle.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
import React from 'react';
import { act } from 'react-dom/test-utils';
import NewsArticle from '../../components/NewsArticle';
import data from '../../data';

expect.extend(toHaveNoViolations);

it('has no axe violations', async () => {
const article = data.news[0];
// this let / async construct is required since next/image rendering
// is a side effect that needs to be captured with async act
// see https://reactjs.org/docs/test-utils.html#act
let results;
await act(async () => {
const { container } = render(<NewsArticle article={article} />);
results = await axe(container);
});

expect(results).toHaveNoViolations();
});
21 changes: 9 additions & 12 deletions components/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,15 @@ function Footer(){
committees.map(({name, slug, external_link, wordmark_dark}) => {
const path = external_link ? external_link : `/committees#${slug}`;
return (
<>
<li key={slug} className={styles['display-inline']}>
<FooterLinkElement
path={path}
/* TODO: resolve 404 with <Image /> component */
/* eslint-disable-next-line @next/next/no-img-element */
title={<img className='committee-sidebar-image' src={wordmark_dark} alt={`ACM ${name}`} />}
ext={external_link}
/>
</li>
<br />
</>
<li key={slug}>
<FooterLinkElement
path={path}
/* TODO: resolve 404 with <Image /> component */
/* eslint-disable-next-line @next/next/no-img-element */
title={<img className='committee-sidebar-image' src={wordmark_dark} alt={`ACM ${name}`} />}
ext={external_link}
/>
</li>
);
})
}
Expand Down
10 changes: 10 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = {
clearMocks: true,
coverageDirectory: '.coverage',
// for mocking static assets and stylesheets, see https://jestjs.io/docs/webpack#handling-static-assets
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/__mocks__/fileMock.js',
'\\.(css|sass|scss)$': 'identity-obj-proxy',
},
setupFilesAfterEnv: ['./jest.setup.js'],
};
1 change: 1 addition & 0 deletions jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@testing-library/jest-dom';
Loading

0 comments on commit ba8a649

Please sign in to comment.