chore: make jest a peer dependency with v29/v30 support
Move jest from dependencies to peer dependencies, allowing users to
choose between Jest 29 and Jest 30.
The CLI now detects the Jest version at runtime and uses the
appropriate environment:
- Jest 29: Uses standard jest-environment-jsdom
- Jest 30: Uses a custom environment based on @jest/environment-jsdom-abstract
with fixes for Web API globals (fetch, streams, Error, etc.)
The cross-fetch polyfill is only injected for Jest 29, as with Jest 30+
our patched Jest environment is used. The network request blocker is made
MSW-compatible by checking if fetch was wrapped before blocking.
Jest 30 (with jsdom v27) fixes `Could not parse CSS stylesheet`
warnings/errors when testing components from @backstage/ui or other
packages using CSS `@layer` declarations.
New peer dependencies (install based on your Jest version):
- jest (required, ^29 or ^30)
- Jest 29 requires: jest-environment-jsdom
- Jest 30 requires: @jest/environment-jsdom-abstract, jsdom
Production code changes for jsdom 27 testability:
- AppIdentityProxy: extract navigateToUrl method for spying
- LiveReloadAddon: export utils.reloadPage for spying
- collect.ts: export internal.resolvePackagePath for mocking
MockFetchApi: evaluate global.fetch at call time instead of construction
time, allowing MSW to patch fetch after MockFetchApi is constructed.
Test adaptations for jsdom 27:
- Use RGB values instead of named colors in CSS assertions
- Update error format expectations (hyphenated type names, SyntaxError
instead of FetchError for JSON parse errors)
- Simplify URL error assertions for cross-version compatibility
- Fix accessible name whitespace handling for external links
- Use history.replaceState for location mocking (non-configurable)
- Use fireEvent.blur for contentEditable elements
- Move async assertions inside waitFor for race conditions
- Remove Blob.prototype.text polyfill (now native)
- Remove test case using credentials in plugin:// URLs
Test adaptations for Jest 30:
- Replace `expect.objectContaining([...])` with direct array equality
- Replace `expect.objectContaining({ length: N })` with
`expect.any(Array)` + separate `toHaveLength()` assertions
- Use child process for native Node.js module resolution in
collect.test.ts to work around Jest 30's resolver behavior
- Update snapshot headers for new Jest format
Also removes the jest-haste-map patch which is no longer needed.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
---
|
||||
id: jest30-migration
|
||||
title: Migrating to Jest 30
|
||||
description: A guide to migrating your project to Jest 30 and JSDOM 27
|
||||
---
|
||||
|
||||
Starting with a recent version of `@backstage/cli`, `jest` is a peer dependency. If you run tests using Backstage CLI, you must add Jest and its environment dependencies as `devDependencies` in your project.
|
||||
|
||||
You can choose to install either Jest 29 or Jest 30:
|
||||
|
||||
- **Jest 29**: Install `jest@^29` and `jest-environment-jsdom@^29`. No migration needed, but you may see `Could not parse CSS stylesheet` warnings/errors when testing components from `@backstage/ui` or other packages using CSS `@layer` declarations.
|
||||
- **Jest 30**: Install `jest@^30`, `@jest/environment-jsdom-abstract@^30`, and `jsdom@^27`. Fixes the stylesheet parsing warnings/errors, but requires the migration steps below.
|
||||
|
||||
## Migration Guide
|
||||
|
||||
The examples below are issues we encountered while migrating the Backstage repository. For a complete list of breaking changes, see the official documentation:
|
||||
|
||||
- [Jest 30 upgrade guide](https://jestjs.io/docs/upgrading-to-jest30)
|
||||
- [JSDOM changelog](https://github.com/jsdom/jsdom/releases)
|
||||
|
||||
### Jest 30
|
||||
|
||||
**Asymmetric matchers with arrays**: `expect.objectContaining()` no longer works with arrays.
|
||||
|
||||
```diff
|
||||
- expect(result).toEqual(expect.objectContaining([{ id: '123' }]));
|
||||
+ expect(result).toEqual([{ id: '123' }]);
|
||||
// or
|
||||
+ expect(result).toEqual(expect.arrayContaining([{ id: '123' }]));
|
||||
```
|
||||
|
||||
**Array length assertions**: `expect.objectContaining({ length: N })` no longer works.
|
||||
|
||||
```diff
|
||||
- expect(fn).toHaveBeenCalledWith(expect.objectContaining({ length: 2 }));
|
||||
+ expect(fn).toHaveBeenCalledWith(expect.any(Array));
|
||||
+ expect(fn.mock.calls[0][0]).toHaveLength(2);
|
||||
```
|
||||
|
||||
**Deprecated matcher aliases removed**: Replace with canonical names.
|
||||
|
||||
```diff
|
||||
- expect(fn).toBeCalled();
|
||||
+ expect(fn).toHaveBeenCalled();
|
||||
```
|
||||
|
||||
**Snapshots**: Regenerate snapshots as the header format has changed.
|
||||
|
||||
```bash
|
||||
yarn test --no-watch -u
|
||||
```
|
||||
|
||||
### JSDOM 27
|
||||
|
||||
**window.location is non-configurable**: You can no longer mock location via `Object.defineProperty`.
|
||||
|
||||
```diff
|
||||
- Object.defineProperty(window, 'location', { value: { href: '' } });
|
||||
+ // Option 1: Use history API
|
||||
+ history.replaceState({}, '', '/new-path');
|
||||
+ // Option 2: Spy on navigation methods
|
||||
+ const spy = jest.spyOn(component, 'navigate');
|
||||
```
|
||||
|
||||
**CSS color values**: Colors may be returned as RGB instead of named colors.
|
||||
|
||||
```diff
|
||||
- expect(element.style.color).toBe('red');
|
||||
+ expect(element.style.color).toBe('rgb(255, 0, 0)');
|
||||
```
|
||||
|
||||
**Error format changes**: Error messages and stack traces may have different formatting.
|
||||
|
||||
#### If you run into `Cannot read properties of null (reading 'constructor')`
|
||||
|
||||
Certain Backstage UI-components (e.g. Button) have a combination of CSS that triggers this error in tests. The solution is to make sure you have at least v0.9.25 of `@acemir/cssom`.
|
||||
Reference in New Issue
Block a user