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.
Test Framework
Section titled “Test Framework”| Component | Framework | Runner | Environment |
|---|---|---|---|
Backend (foundation/) | Jest 29 + ts-jest | npm test in foundation/ | Node.js |
Frontend (foundation/static/main/) | Jest 29 + React Testing Library 16 + ts-jest | npm test in foundation/static/main/ | jsdom |
Key Dependencies
Section titled “Key Dependencies”Backend:
jest^29.7.0ts-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.1jest-environment-jsdom^29.7.0ts-jest^29.4.6
Test File Conventions
Section titled “Test File Conventions”Location
Section titled “Location”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.tsNaming
Section titled “Naming”- 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
Mock Setup
Section titled “Mock Setup”@forge/bridge Mock
Section titled “@forge/bridge Mock”A global mock for the Forge bridge is at src/__mocks__/@forge/bridge.ts. This mock provides:
invoke()— mocked function for resolver callsview— mocked view context
This mock is auto-loaded by Jest’s module resolution for any import of @forge/bridge.
API Client Inline Mocking
Section titled “API Client Inline Mocking”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 },}));i18n Mock
Section titled “i18n Mock”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 returnsscreen.getByText('Change Status');screen.getByText('Add Issue');The mock handles both simple {key} interpolation and ICU {key, plural, ...} syntax.
Running Tests
Section titled “Running Tests”Backend Tests
Section titled “Backend Tests”# Run all backend testscd foundation && npm test
# Run in watch modecd foundation && npm run test:watch
# Run specific test patterncd foundation && npx jest --testPathPattern='schema' --no-coverage
# Run schema/migration tests onlycd foundation && npx jest --testPathPattern='schema|migration' --no-coverageFrontend Tests
Section titled “Frontend Tests”# Run all frontend testscd foundation/static/main && npm test
# Run specific test patterncd foundation/static/main && npm test -- --testPathPattern=Grid
# Run i18n key parity testcd foundation/static/main && npm test -- --testPathPattern=i18n-coverageVerification Scripts
Section titled “Verification Scripts”npm run verify:quick (Pre-Push)
Section titled “npm run verify:quick (Pre-Push)”Runs the enforced local pre-push checks:
- Forge consumption guardrails (
check-consumption.js) - Manifest + TypeScript release readiness (
predeploy:check):- Manifest byte budget check (
check-manifest-limits.js) - Deploy hash generation
- TypeScript type check (
tsc --noEmit)
- Manifest byte budget check (
- Frontend production builds (all three packages)
cd foundation && npm run verify:quicknpm run verify:local (Full CI-Equivalent)
Section titled “npm run verify:local (Full CI-Equivalent)”Runs the complete former-CI sweep:
- Everything in
verify:quick - Backend Jest suite (
npm test -- --watchAll=false) - Main UI Jest suite (
npm test -- --watchAll=false)
cd foundation && npm run verify:localBoth scripts produce a colored pass/fail summary at the end.
i18n Test Coverage
Section titled “i18n Test Coverage”The i18n-coverage test verifies that all translation keys in en.ts (the source of truth) exist in every other locale file:
cd foundation/static/main && npm test -- --testPathPattern=i18n-coverageSupported locales: en, es, fr, de, ja, pt-BR, zh-CN, ko, it, ru, nl
Key rules:
- Every key in
en.tsmust 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 & Migration Tests
Section titled “Schema & Migration Tests”Schema and migration tests verify the database DDL and migration runner:
cd foundation && npx jest --testPathPattern='schema|migration' --no-coverageThese tests validate:
CURRENT_SCHEMA_VERSIONmatches 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
What Can Be Tested Automatically
Section titled “What Can Be Tested Automatically”| Capability | Automated | Manual |
|---|---|---|
| Component rendering | Yes (React Testing Library) | — |
| State management logic | Yes (hooks + callbacks) | — |
| API client call parameters | Yes (mock assertions) | — |
| Column definitions / formatters | Yes | — |
| i18n key coverage | Yes | — |
| Schema migrations | Yes | — |
| Resolver logic (backend) | Yes | — |
| TypeScript type safety | Yes (tsc --noEmit) | — |
| Manifest validity | Yes (check-manifest-limits.js) | — |
| Visual rendering in Forge iframe | — | Yes (screenshot after deploy) |
| Cross-origin focus/blur behavior | — | Yes (manual in Forge iframe) |
| AG Grid interactive editing | — | Yes (manual click/type in Forge iframe) |
| Drag-and-drop operations | — | Yes (manual in Forge iframe) |
| Jira API integration | — | Yes (deploy + real Jira site) |
| Product event triggers | — | Yes (create/update issue in Jira) |
| Forge SQL operations | — | Yes (deploy + invoke resolvers) |
Native OS dropdowns (<select>) | — | Yes (cannot be captured in screenshots) |
Visual Verification
Section titled “Visual Verification”After deploying, visual verification is done via browser screenshots:
- Open the Foundation app tab in Jira Cloud (titled “Foundation - Jira” on humanr.atlassian.net)
- Refresh the page to load the latest deploy
- 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.
Functional Verification
Section titled “Functional Verification”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
Writing New Tests
Section titled “Writing New Tests”New Component Test Checklist
Section titled “New Component Test Checklist”- Create test file at
src/components/<Dir>/__tests__/<Component>.test.tsx - Import React Testing Library:
render,screen,fireEvent,waitFor - Mock
@forge/bridge(auto-mocked) andapi/client(inline mock) - Mock any Atlaskit components that require browser APIs not available in jsdom
- Test at minimum: renders without crashing, shows expected text, handles user interaction
- Assert on English text (i18n mock returns English strings)
New Hook Test Checklist
Section titled “New Hook Test Checklist”- Create test file at
src/components/<Dir>/__tests__/<hookName>.test.ts - Use
renderHookfrom@testing-library/react - Mock external dependencies (API calls, Forge bridge)
- Test initial state, state transitions, cleanup
Example Test
Section titled “Example Test”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 clientjest.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(); }); });});