Skip to content

Testing Guide

This document covers the test framework, mock patterns, test file conventions, verification scripts, and the boundary between automated and manual testing in the Foundation/Lens app.

ComponentFrameworkRunnerEnvironment
Backend (foundation/)Jest 29 + ts-jestnpm test in foundation/Node.js
Frontend (foundation/static/main/)Jest 29 + React Testing Library 16 + ts-jestnpm test in foundation/static/main/jsdom

Backend:

  • jest ^29.7.0
  • ts-jest ^29.1.0
  • @types/jest ^29.5.0

Frontend:

  • jest ^29.7.0
  • @testing-library/react ^16.3.2
  • @testing-library/dom ^10.4.1
  • @testing-library/jest-dom ^6.9.1
  • @testing-library/user-event ^14.6.1
  • jest-environment-jsdom ^29.7.0
  • ts-jest ^29.4.6

Tests live in __tests__/ subdirectories adjacent to the code they test:

src/components/Grid/
HierarchyGrid.tsx
columns.tsx
__tests__/
HierarchyGrid.test.tsx
columns.test.tsx
src/components/Toolbar/
Toolbar.tsx
__tests__/
Toolbar.test.tsx
src/hooks/
useJobStatus.ts
__tests__/
useJobStatus.test.ts
  • Component tests: <ComponentName>.test.tsx
  • Hook tests: <hookName>.test.ts
  • Utility tests: <utilName>.test.ts
  • Backend resolver tests: <resolverName>.test.ts
  • Schema/migration tests: schema.test.ts, migrations.test.ts

A global mock for the Forge bridge is at src/__mocks__/@forge/bridge.ts. This mock provides:

  • invoke() — mocked function for resolver calls
  • view — mocked view context

This mock is auto-loaded by Jest’s module resolution for any import of @forge/bridge.

The API client (src/api/client.ts) should be mocked inline in each test file:

jest.mock('../../../api/client', () => ({
api: {
getLensView: jest.fn().mockResolvedValue({
rows: [],
columns: [],
views: [],
totalCount: 0,
}),
updateField: jest.fn().mockResolvedValue({ success: true }),
// Add other methods as needed
},
}));

A global mock in setupTests.ts returns English strings for react-intl, so test assertions work with English text:

// Tests assert on English text that the mock returns
screen.getByText('Change Status');
screen.getByText('Add Issue');

The mock handles both simple {key} interpolation and ICU {key, plural, ...} syntax.

Terminal window
# Run all backend tests
cd foundation && npm test
# Run in watch mode
cd foundation && npm run test:watch
# Run specific test pattern
cd foundation && npx jest --testPathPattern='schema' --no-coverage
# Run schema/migration tests only
cd foundation && npx jest --testPathPattern='schema|migration' --no-coverage
Terminal window
# Run all frontend tests
cd foundation/static/main && npm test
# Run specific test pattern
cd foundation/static/main && npm test -- --testPathPattern=Grid
# Run i18n key parity test
cd foundation/static/main && npm test -- --testPathPattern=i18n-coverage

Runs the enforced local pre-push checks:

  1. Forge consumption guardrails (check-consumption.js)
  2. Manifest + TypeScript release readiness (predeploy:check):
    • Manifest byte budget check (check-manifest-limits.js)
    • Deploy hash generation
    • TypeScript type check (tsc --noEmit)
  3. Frontend production builds (all three packages)
Terminal window
cd foundation && npm run verify:quick

Runs the complete former-CI sweep:

  1. Everything in verify:quick
  2. Backend Jest suite (npm test -- --watchAll=false)
  3. Main UI Jest suite (npm test -- --watchAll=false)
Terminal window
cd foundation && npm run verify:local

Both scripts produce a colored pass/fail summary at the end.

The i18n-coverage test verifies that all translation keys in en.ts (the source of truth) exist in every other locale file:

Terminal window
cd foundation/static/main && npm test -- --testPathPattern=i18n-coverage

Supported locales: en, es, fr, de, ja, pt-BR, zh-CN, ko, it, ru, nl

Key rules:

  • Every key in en.ts must exist in all 10 non-English locale files
  • Russian (ru) uses 3 plural forms: {count, plural, one {...} few {...} other {...}}
  • CJK locales (ja, zh-CN, ko) use identical singular/plural forms but still need ICU plural syntax

Schema and migration tests verify the database DDL and migration runner:

Terminal window
cd foundation && npx jest --testPathPattern='schema|migration' --no-coverage

These tests validate:

  • CURRENT_SCHEMA_VERSION matches the latest migration entry
  • Migration up() functions execute without errors
  • Fresh-install CREATE TABLE statements are valid
  • safeDDL() wrapping is idempotent

Run these whenever you:

  • Add a new column or table
  • Bump CURRENT_SCHEMA_VERSION
  • Add a new migration entry
CapabilityAutomatedManual
Component renderingYes (React Testing Library)
State management logicYes (hooks + callbacks)
API client call parametersYes (mock assertions)
Column definitions / formattersYes
i18n key coverageYes
Schema migrationsYes
Resolver logic (backend)Yes
TypeScript type safetyYes (tsc --noEmit)
Manifest validityYes (check-manifest-limits.js)
Visual rendering in Forge iframeYes (screenshot after deploy)
Cross-origin focus/blur behaviorYes (manual in Forge iframe)
AG Grid interactive editingYes (manual click/type in Forge iframe)
Drag-and-drop operationsYes (manual in Forge iframe)
Jira API integrationYes (deploy + real Jira site)
Product event triggersYes (create/update issue in Jira)
Forge SQL operationsYes (deploy + invoke resolvers)
Native OS dropdowns (<select>)Yes (cannot be captured in screenshots)

After deploying, visual verification is done via browser screenshots:

  1. Open the Foundation app tab in Jira Cloud (titled “Foundation - Jira” on humanr.atlassian.net)
  2. Refresh the page to load the latest deploy
  3. Take a screenshot to verify:
    • No crashes or blank screens
    • Expected new UI elements are visible
    • Data is rendering correctly

The Foundation app runs in a Forge Custom UI cross-origin iframe. Browser-level screenshots CAN see inside the iframe, so visual verification catches rendering issues.

Interactive testing must be done manually because:

  • The Forge iframe is cross-origin, preventing automated click interactions
  • Native OS UI elements (like <select> dropdown menus) cannot be interacted with or captured in screenshots

After visual verification, identify what needs manual testing and have the user verify:

  • Button clicks and menu interactions
  • Inline cell editing (text, status, priority, assignee, date)
  • Drag-and-drop reordering
  • Modal dialogs and side panels
  • Keyboard shortcuts
  1. Create test file at src/components/<Dir>/__tests__/<Component>.test.tsx
  2. Import React Testing Library: render, screen, fireEvent, waitFor
  3. Mock @forge/bridge (auto-mocked) and api/client (inline mock)
  4. Mock any Atlaskit components that require browser APIs not available in jsdom
  5. Test at minimum: renders without crashing, shows expected text, handles user interaction
  6. Assert on English text (i18n mock returns English strings)
  1. Create test file at src/components/<Dir>/__tests__/<hookName>.test.ts
  2. Use renderHook from @testing-library/react
  3. Mock external dependencies (API calls, Forge bridge)
  4. Test initial state, state transitions, cleanup
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
import en from '../../../i18n/en';
import { MyComponent } from '../MyComponent';
// Mock the API client
jest.mock('../../../api/client', () => ({
api: {
listLenses: jest.fn().mockResolvedValue([
{ id: '1', name: 'Test Lens', issueCount: 5 },
]),
},
}));
const renderWithIntl = (ui: React.ReactElement) =>
render(<IntlProvider locale="en" messages={en}>{ui}</IntlProvider>);
describe('MyComponent', () => {
it('renders lens list', async () => {
renderWithIntl(<MyComponent />);
await waitFor(() => {
expect(screen.getByText('Test Lens')).toBeInTheDocument();
});
});
});