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' });