mirror of
https://github.com/hanzoai/jest.git
synced 2026-09-24 16:59:57 +00:00
fix(node-env): do not warn on localStorage unless actually accessed (#16086)
This commit is contained in:
@@ -129,7 +129,13 @@ Located in `e2e/` with ~190 integration test directories:
|
||||
|
||||
```bash
|
||||
cd e2e/<test-directory>
|
||||
node ../../packages/jest-cli/bin/jest.js
|
||||
node ../../packages/jest-cli/bin/jest.js --no-cache
|
||||
```
|
||||
|
||||
Pass Node flags via the environment, e.g.:
|
||||
|
||||
```bash
|
||||
NODE_OPTIONS='--localstorage-file=/tmp/ls.json' node ../../packages/jest-cli/bin/jest.js --no-cache
|
||||
```
|
||||
|
||||
**Note**: Some tests may require Mercurial (`hg`) to be installed. On macOS: `brew install hg`
|
||||
@@ -147,6 +153,15 @@ yarn lint
|
||||
- Uses ESLint 9.x with flat config (`eslint.config.mjs`)
|
||||
- Caches results for faster subsequent runs
|
||||
- Takes 1-2 minutes
|
||||
- **Run this only as a final check.** During development, lint only changed files (see below).
|
||||
|
||||
**During verification — lint only changed files first**:
|
||||
|
||||
```bash
|
||||
yarn eslint --cache --fix <changed-file-1> <changed-file-2> ...
|
||||
```
|
||||
|
||||
Run `yarn eslint --fix` on each file you edited before running the full `yarn lint`. This catches and auto-fixes issues (import order, formatting) without waiting 1-2 minutes for the full suite.
|
||||
|
||||
**Lint specific files/directories**:
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
- `[fake-timers]` Convert `Date` to milliseconds before passing to `@sinonjs/fake-timers` ([#16029](https://github.com/jestjs/jest/pull/16029))
|
||||
- `[jest-circus]` Prevent crash when `asyncError` is undefined for non-Error throws ([#16003](https://github.com/jestjs/jest/pull/16003))
|
||||
- `[jest-circus, jest-jasmine2]` Include `Error.cause` in JSON `failureMessages` output ([#15949](https://github.com/jestjs/jest/issues/15949))
|
||||
- `[jest-environment-node]` Fix `--localstorage-file` warning on Node 25+ ([#16086](https://github.com/jestjs/jest/pull/16086))
|
||||
- `[jest-runtime]` Improve CJS-from-ESM interop: `__esModule`/Babel default unwrap, broader named-export coverage, and shared CJS singleton across importers ([#16050](https://github.com/jestjs/jest/pull/16050))
|
||||
- `[jest-runtime]` Load `.js` files with ESM syntax but no `"type":"module"` marker as native ESM ([#16050](https://github.com/jestjs/jest/pull/16050))
|
||||
- `[jest-runtime]` Extend the `.js`-with-ESM-syntax fallback to `require()` on Node v24.9+ - falls back to `require(esm)` when the CJS parser rejects ESM syntax ([#16078](https://github.com/jestjs/jest/pull/16078))
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import tempy from 'tempy';
|
||||
import {onNodeVersions} from '@jest/test-utils';
|
||||
import runJest from '../runJest';
|
||||
|
||||
onNodeVersions('>=25', () => {
|
||||
it('localStorage CRUD works inside a Jest test', async () => {
|
||||
await tempy.file.task(storageFile => {
|
||||
const result = runJest('local-storage', [], {
|
||||
nodeOptions: `--localstorage-file=${storageFile}`,
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.stderr).not.toContain('--localstorage-file');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
test('localStorage CRUD', () => {
|
||||
expect(localStorage).toHaveLength(0);
|
||||
|
||||
localStorage.setItem('a', '1');
|
||||
localStorage.setItem('b', '2');
|
||||
expect(localStorage.getItem('a')).toBe('1');
|
||||
expect(localStorage.getItem('b')).toBe('2');
|
||||
expect(localStorage).toHaveLength(2);
|
||||
|
||||
localStorage.removeItem('a');
|
||||
expect(localStorage.getItem('a')).toBeNull();
|
||||
expect(localStorage).toHaveLength(1);
|
||||
|
||||
localStorage.clear();
|
||||
expect(localStorage).toHaveLength(0);
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"jest": {
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,12 @@ const denyList = new Set([
|
||||
|
||||
type GlobalProperties = Array<keyof typeof globalThis>;
|
||||
|
||||
// Storage proxies (Node 25+) emit a warning on any Reflect.get access when
|
||||
// --localstorage-file is not set. We install them as non-caching passthrough
|
||||
// getters so they never land in GlobalProxy.propertyToValue and are never
|
||||
// inspected by deleteProperties at teardown.
|
||||
const storageGlobals = new Set(['localStorage', 'sessionStorage']);
|
||||
|
||||
const nodeGlobals = new Map(
|
||||
(Object.getOwnPropertyNames(globalThis) as GlobalProperties)
|
||||
.filter(global => !denyList.has(global as string))
|
||||
@@ -109,9 +115,25 @@ export default class NodeEnvironment implements JestEnvironment<Timer> {
|
||||
Object.getOwnPropertyNames(global) as GlobalProperties,
|
||||
);
|
||||
for (const [nodeGlobalsKey, descriptor] of nodeGlobals) {
|
||||
protectProperties(globalThis[nodeGlobalsKey]);
|
||||
if (!storageGlobals.has(nodeGlobalsKey as string)) {
|
||||
protectProperties(globalThis[nodeGlobalsKey]);
|
||||
}
|
||||
if (!contextGlobals.has(nodeGlobalsKey)) {
|
||||
if (descriptor.configurable) {
|
||||
if (storageGlobals.has(nodeGlobalsKey as string)) {
|
||||
Object.defineProperty(global, nodeGlobalsKey, {
|
||||
configurable: true,
|
||||
enumerable: descriptor.enumerable,
|
||||
get: () => globalThis[nodeGlobalsKey],
|
||||
set(value) {
|
||||
Object.defineProperty(global, nodeGlobalsKey, {
|
||||
configurable: true,
|
||||
enumerable: descriptor.enumerable,
|
||||
value,
|
||||
writable: true,
|
||||
});
|
||||
},
|
||||
});
|
||||
} else if (descriptor.configurable) {
|
||||
Object.defineProperty(global, nodeGlobalsKey, {
|
||||
configurable: true,
|
||||
enumerable: descriptor.enumerable,
|
||||
|
||||
Reference in New Issue
Block a user