or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

app-wrappers.mdindex.mdmock-apis.mdmsw-integration.mdtest-api-provider.mdtesting-utilities.md
tile.json

test-api-provider.mddocs/

Test API Provider

Type-safe API context provider for tests with flexible API registration and registry management. The TestApiProvider system enables precise control over which APIs are available to components during testing.

Capabilities

TestApiProvider Component

React component that provides API context for testing with type-safe API registration.

/**
 * API context provider for tests with type-safe API registration
 * @param props - Provider properties with APIs and children
 * @returns JSX element providing API context to children
 */
function TestApiProvider<T extends readonly ApiPair[]>(
  props: TestApiProviderProps<T>
): JSX.Element;

interface TestApiProviderProps<TApiPairs extends readonly ApiPair[]> {
  /** Array of API pairs to register in the test context */
  apis: TApiPairs;
  
  /** Child components that will have access to the provided APIs */
  children: ReactNode;
}

type ApiPair = readonly [ApiRef<any>, any];

Usage Examples:

import { 
  TestApiProvider, 
  mockApis,
  configApiRef,
  identityApiRef,
  storageApiRef 
} from "@backstage/test-utils";

// Basic usage with mock APIs
function TestWrapper({ children }) {
  return (
    <TestApiProvider
      apis={[
        [configApiRef, mockApis.config({ data: { app: { title: 'Test' } } })],
        [identityApiRef, mockApis.identity({ userEntityRef: 'user:default/test' })],
        [storageApiRef, mockApis.storage()]
      ]}
    >
      {children}
    </TestApiProvider>
  );
}

// With custom implementations
<TestApiProvider
  apis={[
    [configApiRef, new CustomConfigApi()],
    [errorApiRef, new MockErrorApi({ collect: true })]
  ]}
>
  <MyComponent />
</TestApiProvider>

// Type-safe API pairs ensure correctness
<TestApiProvider
  apis={[
    [configApiRef, mockApis.config()], // ✓ Correct type
    [configApiRef, mockApis.identity()] // ✗ Type error - wrong API type
  ]}
>
  <MyComponent />
</TestApiProvider>

TestApiRegistry

ApiHolder implementation for tests that manages API registration and retrieval.

/**
 * ApiHolder implementation for tests with type-safe API registration
 */
class TestApiRegistry implements ApiHolder {
  /**
   * Creates TestApiRegistry from API pairs with type safety
   * @param apis - Variable number of API pairs to register
   * @returns TestApiRegistry instance with registered APIs
   */
  static from<TApiPairs extends readonly ApiPair[]>(...apis: TApiPairs): TestApiRegistry;
  
  /**
   * Retrieves API implementation by reference
   * @param api - API reference to lookup
   * @returns API implementation or undefined if not registered
   */
  get<T>(api: ApiRef<T>): T | undefined;
}

Usage Examples:

import { TestApiRegistry, mockApis } from "@backstage/test-utils";

// Create registry with APIs
const apiRegistry = TestApiRegistry.from(
  [configApiRef, mockApis.config({ data: { backend: { baseUrl: 'http://localhost' } } })],
  [identityApiRef, mockApis.identity()],
  [discoveryApiRef, mockApis.discovery({ baseUrl: 'http://localhost:7007' })]
);

// Retrieve APIs
const configApi = apiRegistry.get(configApiRef);
const identityApi = apiRegistry.get(identityApiRef);

// Use with ApiProvider
<ApiProvider apis={apiRegistry}>
  <MyComponent />
</ApiProvider>

// Custom registry usage
class CustomTestRegistry extends TestApiRegistry {
  constructor() {
    super();
    // Custom initialization logic
  }
}

API Reference Types

Core types for API registration and management in the test context.

/**
 * Reference to an API with type information
 */
interface ApiRef<T> {
  readonly id: string;
  readonly T: T;
}

/**
 * Interface for API holder implementations
 */
interface ApiHolder {
  get<T>(api: ApiRef<T>): T | undefined;
}

/**
 * Pair of API reference and its implementation
 */
type ApiPair = readonly [ApiRef<any>, any];

/**
 * Extract API reference type from API pair
 */
type ApiRefFromPair<TPair> = TPair extends readonly [ApiRef<infer T>, any] ? ApiRef<T> : never;

/**
 * Extract implementation type from API pair  
 */
type ImplFromPair<TPair> = TPair extends readonly [ApiRef<any>, infer T] ? T : never;

Advanced Usage Patterns

Multiple API Configurations

import { TestApiProvider, mockApis } from "@backstage/test-utils";

// Development-like configuration
const developmentApis = [
  [configApiRef, mockApis.config({ 
    data: { 
      app: { title: 'Dev App' },
      backend: { baseUrl: 'http://localhost:7007' }
    }
  })],
  [identityApiRef, mockApis.identity({ 
    userEntityRef: 'user:default/developer' 
  })],
  [permissionApiRef, mockApis.permission({ allow: true })]
] as const;

// Production-like configuration
const productionApis = [
  [configApiRef, mockApis.config({ 
    data: { 
      app: { title: 'Production App' },
      backend: { baseUrl: 'https://api.company.com' }
    }
  })],
  [identityApiRef, mockApis.identity({ 
    userEntityRef: 'user:default/user123' 
  })],
  [permissionApiRef, mockApis.permission({ allow: false })]
] as const;

// Use in tests
describe('MyComponent', () => {
  test('works in development', () => {
    render(
      <TestApiProvider apis={developmentApis}>
        <MyComponent />
      </TestApiProvider>
    );
  });

  test('works in production', () => {
    render(
      <TestApiProvider apis={productionApis}>
        <MyComponent />  
      </TestApiProvider>
    );
  });
});

Custom API Implementations

import { TestApiProvider } from "@backstage/test-utils";

// Custom config API for specific test scenarios
class TestSpecificConfigApi implements ConfigApi {
  constructor(private data: any) {}
  
  getString(key: string): string {
    // Custom logic for test scenario
    if (key === 'app.title') return 'Test Specific Title';
    return this.data[key] || '';
  }
  
  // Implement other ConfigApi methods...
}

// Use custom implementation
<TestApiProvider
  apis={[
    [configApiRef, new TestSpecificConfigApi({ feature: 'enabled' })],
    [identityApiRef, mockApis.identity()]
  ]}
>
  <ComponentUnderTest />
</TestApiProvider>

Nested API Providers

// Layer API providers for complex scenarios
<TestApiProvider
  apis={[
    [configApiRef, mockApis.config()],
    [identityApiRef, mockApis.identity()]
  ]}
>
  <PluginContext>
    <TestApiProvider
      apis={[
        [storageApiRef, mockApis.storage({ data: { theme: 'dark' } })]
      ]}
    >
      <MyComponent />
    </TestApiProvider>
  </PluginContext>
</TestApiProvider>

Types

type ReactNode = React.ReactNode;

interface JSX {
  Element: React.ReactElement<any, any>;
}

/**
 * Tuple type for API reference and implementation pairs
 */
type ApiPair = readonly [ApiRef<any>, any];

/**
 * Extract tuple of API pairs from provider props
 */
type ExtractApiPairs<T> = T extends TestApiProviderProps<infer U> ? U : never;