Skip to content

Latest commit

 

History

History
158 lines (116 loc) · 5.55 KB

File metadata and controls

158 lines (116 loc) · 5.55 KB

TESTING

Frontend Test Types

Unit tests

Focuses on testing individual React components, hooks, and utilities in isolation.

  • Framework: Jest
  • Libraries: @testing-library/react, @testing-library/jest-dom

Integration tests

  • Tool: Playwright
  • Specialized test suites (projects) for:
    • Core Console
    • OLM (Operator Lifecycle Manager)
    • Dev Console
    • Helm
    • Knative
    • Topology
    • Web Terminal
    • Smoke
    • Helm
    • Topology
  • Supports headless and interactive modes

Unit Testing with Jest and React Testing Library

When generating unit tests, ALWAYS use the gen-rtl-test skill to create tests that follow our best practices and conventions.

Testing Best Practices

  1. User-Centric Testing - Test what users see and interact with. DO NOT test:

    • Internal component state
    • Private component methods
    • Props passed to child components
    • CSS class names or styles
    • Component structure (e.g., expect(container.firstChild).toBe...)
  2. Accessibility-First - Queries match how screen readers and users interact with the UI

  3. Semantic Over Generic - Always prefer role-based queries (e.g., getByRole) over generic selectors

  4. DRY Helpers - Use reusable function such as renderWithProviders, renderHookWithProviders in frontend/packages/console-shared/src/test-utils. Extract repetitive setup not covered by these helpers into custom functions if needed.

  5. Async-Aware - Handle asynchronous updates with findBy* and waitFor

  6. TypeScript Safety - Use proper types for props, state, and mock data

  7. Arrange-Act-Assert (AAA) Pattern - Structure tests logically:

    • Arrange: Render component with mocks
    • Act: Perform user actions
    • Assert: Verify expected state

Test File Co-location and Naming Convention

  • Test file must be in __tests__/ directory within component directory
  • Test file must have same name as implementation file
  • Use .spec.tsx extension
MyComponentDirectory/
├── __tests__/
│   └── MyComponent.spec.tsx
└── MyComponent.tsx

Mocking Strategies

When mocking is necessary:

  • ALWAYS use ESM import statements at the top of the file
  • NEVER use require('react') or React.createElement() in mocks
  • Prefer jest.mock() for module mocks and jest.fn() for component mocks instead of jest.spyOn()
  • Keep mocks simple - return null, strings, or children directly
  • Use jest.fn(() => null) for simple component mocks
  • Use jest.fn(() => 'ComponentName') for mocks that need to display text
  • Use jest.fn((props) => props.children) for wrapper components

Correct Mock Patterns:

// CORRECT - Return null
jest.mock('../MyComponent', () => () => null);

// CORRECT - Return string
jest.mock('../LoadingSpinner', () => () => 'Loading...');

// CORRECT - Return children directly
jest.mock('../Wrapper', () => ({ children }) => children);

// CORRECT - Use jest.fn for tracking calls
jest.mock('../ButtonBar', () => jest.fn(({ children }) => children));

// FORBIDDEN - require()
jest.mock('../Component', () => {
  const React = require('react'); // NEVER
  return () => React.createElement('div');
});

// FORBIDDEN - JSX in mocks
jest.mock('../Component', () => () => <div>Mock</div>);

Mock Custom Hooks with jest.fn()

jest.mock("../useCustomHook", () => ({
  useCustomHook: jest.fn(() => [
    /* mock data */
  ]),
}));

Test user behavior, not implementation

// GOOD - Testing user-visible behavior
expect(screen.getByRole('heading', { name: 'Resource Details' })).toBeVisible();

// BAD - Testing implementation
expect(wrapper.find(DetailsPage).props()).toEqual({...});

Clean up mocks

// GOOD - Proper cleanup
afterEach(() => {
  jest.restoreAllMocks();
});

// BAD - No cleanup
jest.spyOn(module, "function");

End-to-End Testing with Playwright

E2E tests validate full user workflows against a real OpenShift cluster using Playwright. Tests live in frontend/e2e/tests/<package>/.

  • Self-contained tests: Each test() block creates its own resources, asserts independently, and cleans up via the cleanup fixture.
  • Selectors: Always use page.getByTestId('x') which queries [data-test="x"]. If a React element only has a legacy test attribute (data-test-id, data-test-selector, etc.), add data-test to the element. Never remove legacy attributes — external consumers may depend on them.
  • Page Objects: Extend BasePage (e2e/pages/base-page.ts) which provides robustClick(), waitForLoadingComplete(), and goTo(). Locators are private readonly properties; actions are async methods.
  • Cluster Interactions: Use KubernetesClient (e2e/clients/kubernetes-client.ts) — never shell commands in tests.
  • Fixtures: Import test and expect from e2e/fixtures, not from @playwright/test. Custom fixtures provide cleanup, testConfig, and k8sClient.

Running Playwright Tests

Prerequisites: oc login to a cluster, set BRIDGE_KUBEADMIN_PASSWORD and WEB_CONSOLE_URL (or configure e2e/.env).

  • yarn test-playwright — run all tests in headless mode
  • yarn test-playwright-headed — run with a visible browser
  • yarn test-playwright-debug — run in debug mode (Playwright Inspector)
  • yarn test-playwright-ui — run in interactive UI mode
  • yarn test-playwright-admin — run only admin persona tests
  • yarn test-playwright-developer — run only developer persona tests

See README.md#integration-tests for full prerequisites and running instructions.