jest.setup.js4.7 KBView on GitHub // Jest setup file for mail app (including Cedar-OS tests)
require('@testing-library/jest-dom');
// Polyfill TextEncoder/TextDecoder — required by react-router and other packages
// that use web-standard APIs not automatically present in jsdom.
const { TextEncoder, TextDecoder } = require('util');
if (typeof globalThis.TextEncoder === 'undefined') globalThis.TextEncoder = TextEncoder;
if (typeof globalThis.TextDecoder === 'undefined') globalThis.TextDecoder = TextDecoder;
// Provide a mock for import.meta (transformed by our inline babel plugin in jest.config.cjs
// to globalThis.__importMeta__). Covers import.meta.env.X used throughout the codebase.
globalThis.__importMeta__ = {
env: {
MODE: 'test',
DEV: false,
PROD: false,
SSR: false,
VITE_PUBLIC_BACKEND_URL: 'http://localhost:3001',
VITE_PUBLIC_APP_URL: 'http://localhost:5173',
VITE_PUBLIC_WORKER_URL: 'http://localhost:3002',
},
};
// Polyfill for TransformStream (needed for AI SDK)
if (typeof globalThis.TransformStream === 'undefined') {
const { TransformStream } = require('stream/web');
globalThis.TransformStream = TransformStream;
}
// Mock localStorage
const localStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
clear: jest.fn(),
};
global.localStorage = localStorageMock;
// Mock sessionStorage
const sessionStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
clear: jest.fn(),
};
global.sessionStorage = sessionStorageMock;
// Mock window.matchMedia
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
// Mock IntersectionObserver
global.IntersectionObserver = class IntersectionObserver {
constructor() {}
disconnect() {}
observe() {}
takeRecords() {
return [];
}
unobserve() {}
};
// Mock ResizeObserver
global.ResizeObserver = class ResizeObserver {
constructor() {}
disconnect() {}
observe() {}
unobserve() {}
};
// jsdom has no layout, so it ships no scrollIntoView. Any list that keeps its highlighted
// row in view (OptionPicker, the tabs strip) calls it on every move; without this every such
// component throws on its first keystroke in a test.
if (!Element.prototype.scrollIntoView) {
Element.prototype.scrollIntoView = function scrollIntoView() {};
}
// jsdom implements no Pointer Events API at all, and every Radix floating primitive
// (DropdownMenu, Select, Popover, DropdownMenuSub) opens on `pointerdown` and calls
// `hasPointerCapture` while doing it. Without these, a Radix menu in a test does not
// throw — it simply never opens, so the assertion that follows fails with a DOM dump
// containing only the trigger, and the test reads as "the menu has no items" rather
// than "jsdom cannot click it". That misdiagnosis is the reason this is here.
if (typeof window !== 'undefined' && typeof window.PointerEvent === 'undefined') {
window.PointerEvent = class PointerEvent extends MouseEvent {
constructor(type, params = {}) {
super(type, params);
this.pointerId = params.pointerId ?? 1;
this.pointerType = params.pointerType ?? 'mouse';
this.isPrimary = params.isPrimary ?? true;
}
};
}
for (const method of ['hasPointerCapture', 'setPointerCapture', 'releasePointerCapture']) {
if (!Element.prototype[method]) {
Element.prototype[method] =
method === 'hasPointerCapture' ? function () { return false; } : function () {};
}
}
// Suppress console errors in tests unless explicitly needed
const originalError = console.error;
beforeAll(() => {
console.error = (...args) => {
// Only suppress React act() warnings and some common test warnings
if (
typeof args[0] === 'string' &&
(args[0].includes('Warning: ReactDOM.render') ||
args[0].includes('Warning: useLayoutEffect') ||
args[0].includes('Not implemented: HTMLFormElement.prototype.submit'))
) {
return;
}
originalError.call(console, ...args);
};
});
afterAll(() => {
console.error = originalError;
});
// An SVG `<title>` is an icon's ACCESSIBLE NAME, not page text. Left visible to
// `getByText`, every brand mark answers a query aimed at the label beside it —
// `getByText('Gmail')` matched both the Gmail glyph's title and the row that says
// Gmail, and eight AgentConnectionsSection tests failed the moment that glyph was
// given the title it needed. Reach for it deliberately (`getByTitle`, or a role +
// name query); it is not a text node.
require('@testing-library/dom').configure({ defaultIgnore: 'script, style, title' });