- Introduction
- Configuring Jest
- Configuring ESLint: overrides for Jest tests
- Configuring GitHub Actions: running tests in CI
The preferred toolset for creating unit tests for Stripes UI
modules such as ui-plugin-eusage-reports
is currently
the Jest framework
with React interaction provided by
RTL (React Testing Library)
and mocks for back-end interaction provided by Jest itself (rather than
Mock Service Worker
as suggested in
the RTL Example page.)
The principal configuration file is jest.config.js
at the top level, a short JavaScript program that exports a JSON object with the generated configuration. The format of this file is well documented on the Jest site.
This file refers to several files and modules outside itself, and we conventionally keep all these additional parts of the configuration in the test/jest
directory. We will now review the most important entries in the Jest configuration and the files that they use.
The testMatch
directive lists patterns which filenames must match in order to be considered tests, and testPathIgnorePatterns
lists pattern which should be ignored -- typically ['/node_modules/']
.
The reporters
directive specifies how the results of tests are reported. It can be used to override the standard behaviour of using just the default
reporter -- our projects use reporters: ['jest-junit', 'default']
to leave behind a junit.xml
file for our CI builds.
Dev Dependencies: the only module required to run simple Jest tests is jest
itself. To generate a junit.xml
file, jest-junit
is also needed.
Babel is used to support the modern JavaScript dialect ES6. This is achieved by the transform
directive, which specifies that files whose names end in .js
or .jsx
are handled by the transformer defined by test/jest/jest-transformer.js
. This short file uses the adapter module babel-jest
to make Babel function as a Jest transformer, and configures it with the Stripes configuration exported from the additional configuration file test/jest/babel.config.js
. This file in turn gets that configuration from the Stripes CLI module and re-exports it. (We get the Stripes CLI configuration from the Stripes CLI instead of directly from stripes-webpack
because every module already relies on the former, so we can avoid adding yet another dev-dependency on the latter.)
- Dependencies:
babel-jest
,@folio/stripes-cli
Jest can report test coverage just be providing the --coverage
command-line option, but it needs to be told:
- which files to collect coverage information from, in the
collectCoverageFrom
element - how to emit the results of coverage analysis, in the
coverageReporters
. The value['lcov', 'text']
says to store in the lcov format used by FOLIO's CI system and also emit a summary of the results as text. - where to store the coverage analysis results, in the
coverageDirectory
element.
The setupFiles
and setupFilesAfterEnv
configuration elements are used to execute specified scripts before each test -- the former before the testing framework is installed in the environment, and the latter after.
We use setupFilesAfterEnv
to run another file, test/jest/jest.setup.js
. All this does is import @testing-library/jest-dom
which provides Jest matchers for DOM elements, enabling you to say things like expect(form).toBeVisible()
.
- Dependencies:
@testing-library/jest-dom
We use setupFiles
to run test/jest/setupTests.js
, which in turn installs all the specific mocks provided in the test/jest/__mock__
directory, as well as the regenerator-runtime
module, which implements async
for Babel. setupFiles
also pulls in jest-canvas-mock
, which mocks the canvas element.
We also use moduleNameMapper
to mock CSS and SVG files using the identity-obj-proxy
module.
- Dependencies:
regenerator-runtime
,jest-canvas-mock
,identity-obj-proxy
The individual mocks in the test/jest/__mock__
directory fall into two categories:
-
Files that mock the modules making up the Stripes framework:
stripesCore.mock.js
,stripesUtils.mock.js
, etc. These are required for the tested components to be able to function outside the context of Stripes itself. The current pattern is to copy these files from other projects. This may be centralized in the future. -
In some modules, this directory is also used as the repository for mocked data-sets used by specific tests: for example,
ui-users
providescashDrawerReconciliationReportData.mock.js
, which exports a data-structure used in tests includingcashDrawerReconciliationReportCSV.test.js
.
Tests written for Jest include some aspects that are usually flagged as errors by ESLint -- for example, use of global functions such as test
and expect
. Similarly, mocks provided to the tests will use these globals. To keep lint-clean, we can instruct ESLint to override its usual rules for files matching *.test.js
and all files in the Jest area by using the jest
environment when linting them. Add this clause to the top-level .eslintrc
:
"overrides": [
{
"files": ["**/*.test.js", "test/jest/**/*"],
"env": {
"jest": true
}
}
]
Once you have your tests running, you want them to run automatically in CI. Assuming your project is set up in the standard way in the GitHub Actions for CI, add these lines to ./.github/workflows/build-npm.yml
:
env:
YARN_TEST_OPTIONS: '--ci --coverage --colors'
JEST_JUNIT_OUTPUT_DIR: 'artifacts/jest-junit'
JEST_COVERAGE_REPORT_DIR: 'artifacts/coverage-jest/lcov-report/'
...
steps:
- name: Run yarn test
run: xvfb-run --server-args="-screen 0 1024x768x24" yarn test $YARN_TEST_OPTIONS JEST_COVERAGE_REPORT_DIR: 'artifacts/coverage-jest/lcov-report/'
- name: Publish Jest unit test results
uses: docker://ghcr.io/enricomi/publish-unit-test-result-action:v1
if: always()
with:
github_token: ${{ github.token }}
files: "${{ env.JEST_JUNIT_OUTPUT_DIR }}/*.xml"
check_name: Jest Unit Test Results
comment_mode: update last
comment_title: Jest Unit Test Statistics
- name: Publish Jest coverage report
uses: actions/upload-artifact@v2
if: always()
with:
name: jest-coverage-report
path: ${{ env.JEST_COVERAGE_REPORT_DIR }}
retention-days: 30
The CI configuration knows to find the coverage analysis results in artifacts/coverage-jest
, which is where the Jest configuration says to leave them, so coverage artifacts will appear in GitHub Actions.