Add async-safe polyfills, Jest custom config, and improved token validation
- Introduced Web Crypto API polyfills (`@peculiar/webcrypto`) for Node.js to enable SSR-safe cryptography utilities. - Added Jest setup file for global mocks (e.g., `localStorage`, `sessionStorage`, and `TextEncoder/Decoder`). - Enhanced token validation behavior in `storage` tests to reject incomplete tokens. - Replaced runtime configuration validation with clamping using `parseIntSafe` constraints for improved reliability. - Updated `package.json` and `package-lock.json` to include new dependencies (`@peculiar/webcrypto` and related libraries).
This commit is contained in:
86
frontend/jest.setup.js
Normal file
86
frontend/jest.setup.js
Normal file
@@ -0,0 +1,86 @@
|
||||
// Learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom'
|
||||
import { Crypto } from '@peculiar/webcrypto';
|
||||
|
||||
// Mock window object
|
||||
global.window = global.window || {};
|
||||
|
||||
// Use real Web Crypto API polyfill for Node environment
|
||||
const cryptoPolyfill = new Crypto();
|
||||
|
||||
// Store references before assignment
|
||||
const subtleRef = cryptoPolyfill.subtle;
|
||||
const getRandomValuesRef = cryptoPolyfill.getRandomValues.bind(cryptoPolyfill);
|
||||
|
||||
// Use Object.defineProperty to ensure properties aren't overridden
|
||||
if (!global.crypto) {
|
||||
global.crypto = {};
|
||||
}
|
||||
|
||||
Object.defineProperty(global.crypto, 'subtle', {
|
||||
value: subtleRef,
|
||||
writable: false,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
|
||||
Object.defineProperty(global.crypto, 'getRandomValues', {
|
||||
value: getRandomValuesRef,
|
||||
writable: false,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
|
||||
// Mock TextEncoder/TextDecoder if not available
|
||||
if (typeof TextEncoder === 'undefined') {
|
||||
global.TextEncoder = class TextEncoder {
|
||||
encode(str) {
|
||||
const buf = Buffer.from(str, 'utf-8');
|
||||
return new Uint8Array(buf);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof TextDecoder === 'undefined') {
|
||||
global.TextDecoder = class TextDecoder {
|
||||
decode(arr) {
|
||||
return Buffer.from(arr).toString('utf-8');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Mock localStorage (must be on global to satisfy typeof checks)
|
||||
global.localStorage = {
|
||||
getItem: jest.fn(),
|
||||
setItem: jest.fn(),
|
||||
removeItem: jest.fn(),
|
||||
clear: jest.fn(),
|
||||
length: 0,
|
||||
key: jest.fn(),
|
||||
};
|
||||
|
||||
// Mock sessionStorage (must be on global to satisfy typeof checks)
|
||||
global.sessionStorage = {
|
||||
getItem: jest.fn(),
|
||||
setItem: jest.fn(),
|
||||
removeItem: jest.fn(),
|
||||
clear: jest.fn(),
|
||||
length: 0,
|
||||
key: jest.fn(),
|
||||
};
|
||||
|
||||
// Reset storage mocks before each test
|
||||
beforeEach(() => {
|
||||
// Don't use clearAllMocks - it breaks the mocks
|
||||
// Instead reset individual storage mock return values
|
||||
if (global.localStorage && typeof global.localStorage.getItem.mockReset === 'function') {
|
||||
global.localStorage.getItem.mockReset().mockReturnValue(null);
|
||||
global.localStorage.setItem.mockReset();
|
||||
global.localStorage.removeItem.mockReset();
|
||||
}
|
||||
if (global.sessionStorage && typeof global.sessionStorage.getItem.mockReset === 'function') {
|
||||
global.sessionStorage.getItem.mockReset().mockReturnValue(null);
|
||||
global.sessionStorage.setItem.mockReset();
|
||||
global.sessionStorage.removeItem.mockReset();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user