Skip to content

@memberjunction/react-test-harness

Automated test harness for MemberJunction interactive React components. Provides static analysis (linting) with 57 extensible rules, type inference, constraint validation, browser-based rendering via Playwright, and a CLI for running test suites.

import { ComponentLinter } from '@memberjunction/react-test-harness';
const result = await ComponentLinter.lintComponent(
code, // JavaScript/JSX source
'MyComponent', // Component name
componentSpec, // ComponentSpec with metadata
true, // isRootComponent
);
if (!result.success) {
for (const v of result.violations) {
console.log(`[${v.severity}] ${v.rule}: ${v.message} (line ${v.line})`);
}
}
import { ComponentRunner } from '@memberjunction/react-test-harness';
const runner = new ComponentRunner();
const result = await runner.executeComponent(componentSpec, {
contextUser,
props: { data: testData },
});
console.log('Rendered:', result.success);
console.log('Errors:', result.errors);

By default the harness launches its own throwaway Chromium. You can instead attach to an already-running browser — useful for watching component runs in a browser you control, or for reusing a warm/remote browser (pool, Docker) instead of paying a cold launch each run.

// Attach to a real Chrome started with --remote-debugging-port=9222 (CDP)
const harness = new ReactTestHarness({ connect: 'http://localhost:9222' });
// Attach to a Playwright server started via chromium.launchServer() (ws endpoint)
const harness = new ReactTestHarness({ connect: 'ws://localhost:55001/<id>' });
  • Auto-detect: http(s):// endpoints use CDP (connectOverCDP); ws(s):// endpoints use a Playwright server (connect). A raw CDP websocket also starts with ws:// — pass connectType: 'cdp' to force CDP in that case.
  • Env-var fallback: set MJ_REACT_TEST_HARNESS_CONNECT to the endpoint instead of passing connect (so the mj-react-test CLI can attach without a new flag).
  • Session reuse: by default a fresh isolated context is created inside the attached browser. Set reuseExistingContext: true (or MJ_REACT_TEST_HARNESS_REUSE_CONTEXT=true) to reuse the browser’s existing default context and share its cookies/auth/session. This breaks per-test isolation, so keep the default for parallel runs.
  • Lifecycle safety: when attached, harness.close() only closes the pages and contexts the harness created — it never closes a browser it did not launch, nor a reused/shared context. The external browser’s lifecycle is the caller’s.
  • headless is ignored when attaching (the external browser already decided).

Rules extend BaseLintRule and auto-register via @RegisterClass:

import { RegisterClass } from '@memberjunction/global';
import { BaseLintRule } from '@memberjunction/react-test-harness';
@RegisterClass(BaseLintRule, 'my-custom-rule')
export class MyCustomRule extends BaseLintRule {
get Name() { return 'my-custom-rule'; }
get AppliesTo(): 'all' | 'child' | 'root' { return 'all'; }
Test(ast, componentName, componentSpec, options, typeContext) {
const violations = [];
// Babel AST traversal and validation logic
return violations;
}
}

External packages (e.g., Skip-Brain) can define custom rules — the linter discovers them automatically via MJGlobal’s ClassFactory.

Terminal window
cd packages/React/test-harness
# Build (required before testing)
npm run build
# Run all 425 tests
npx vitest run
# Run with verbose output (see individual fixture results)
npx vitest run --reporter=verbose
# Watch mode
npx vitest

Some rules validate against entity metadata from the database. Create .env:

DB_HOST=your-host
DB_DATABASE=your-db
DB_USERNAME=your-user
DB_PASSWORD=your-password
DB_PORT=1433
DB_TRUST_SERVER_CERTIFICATE=1

Without a database, DB-dependent fixtures still run but rules that need entity metadata emit low-severity warnings instead of violations.

See LINTER-ARCHITECTURE.md for comprehensive documentation including:

  • How rules work and how to add new ones
  • Rule categories and what each validates
  • Type inference engine capabilities
  • Metadata fallback strategy
  • SQL dialect configuration
  • Test fixture organization
  • runview-call-validation — RunView/RunViews call-site validation
  • runquery-call-validation — RunQuery call-site validation with parameter type checking
  • data-result-validation — RunView/RunQuery/Search result usage patterns
  • search-availability-checkutilities.search null guard
  • search-call-validation — Search/PreviewSearch parameter validation
  • entity-field-access-validation — Field access on RunView results (typos, case, type coercion)
  • query-result-field-access-validation — Field access on RunQuery results
  • chart-field-validation — Chart prop field references
  • datagrid-field-validation — Grid column field references
  • no-import-statements, no-export-statements, no-require-statements
  • no-iife-wrapper, no-return-component, no-window-access
  • use-function-declaration, single-function-only
  • react-component-naming, component-name-mismatch, pass-standard-props
  • no-react-destructuring, no-data-prop, no-child-implementation
  • component-props-validation, child-component-prop-validation
  • component-usage-without-destructuring
  • component-not-in-dependencies, undefined-component-usage
  • unused-libraries, unused-component-dependencies
  • library-variable-names, dependency-shadowing, validate-component-references
  • callback-event-validation — Method usage, parameter signatures, passthrough, event null-checks
  • prefer-async-await, prefer-jsx-syntax, react-hooks-rules
  • useeffect-unstable-dependencies, unsafe-array-operations, unsafe-formatting-methods
  • string-replace-all-occurrences, string-template-validation
  • type-inference-errors, type-mismatch-operation
  • styles-validation — Invalid path access, unsafe patterns
  • utilities-api-validation, utilities-no-direct-instantiation, ai-tools-availability-check
  • saved-user-settings-pattern, noisy-settings-updates, prop-state-sync
  • property-name-consistency, server-reload-on-client-operation
  • no-use-reducer, required-queries-not-called
  • undefined-jsx-component, no-child-implementation
// Core linting
export { ComponentLinter, LintResult, Violation } from './lib/component-linter';
export { BaseLintRule } from './lib/lint-rule';
// Test harness
export { ReactTestHarness, TestHarnessOptions } from './lib/test-harness';
export { ComponentRunner, ComponentExecutionOptions, ComponentExecutionResult } from './lib/component-runner';
export { BrowserManager, BrowserContextOptions } from './lib/browser-context';
// Utilities
export { LibraryLintCache, CompiledLibraryRules, CompiledValidator } from './lib/library-lint-cache';
export { ComponentSpec } from '@memberjunction/interactive-component-types';

ISC