From 5f6d97d044bb0c8e802e4e002b01bd43f32ce6de Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 16 Nov 2021 21:26:29 +0100 Subject: [PATCH] test-utils: add TestApiRegistry Signed-off-by: Patrik Oldsberg --- .../src/apis/system/ApiRegistry.ts | 2 +- packages/test-utils/api-report.md | 12 +++- .../src/testUtils/TestApiProvider.tsx | 58 ++++++++++++++----- packages/test-utils/src/testUtils/index.tsx | 2 +- 4 files changed, 58 insertions(+), 16 deletions(-) diff --git a/packages/core-app-api/src/apis/system/ApiRegistry.ts b/packages/core-app-api/src/apis/system/ApiRegistry.ts index 2d12fcfb35..e433381dd1 100644 --- a/packages/core-app-api/src/apis/system/ApiRegistry.ts +++ b/packages/core-app-api/src/apis/system/ApiRegistry.ts @@ -36,7 +36,7 @@ class ApiRegistryBuilder { * A registry for utility APIs. * * @public - * @deprecated Will be removed, use {@link @backstage/test-utils#TestApiProvider} instead. + * @deprecated Will be removed, use {@link @backstage/test-utils#TestApiProvider} or {@link @backstage/test-utils#TestApiRegistry} instead. */ export class ApiRegistry implements ApiHolder { static builder() { diff --git a/packages/test-utils/api-report.md b/packages/test-utils/api-report.md index 38656137a3..5bb552563b 100644 --- a/packages/test-utils/api-report.md +++ b/packages/test-utils/api-report.md @@ -5,6 +5,7 @@ ```ts import { AnalyticsApi } from '@backstage/core-plugin-api'; import { AnalyticsEvent } from '@backstage/core-plugin-api'; +import { ApiHolder } from '@backstage/core-plugin-api'; import { ApiRef } from '@backstage/core-plugin-api'; import { ComponentType } from 'react'; import { ErrorApi } from '@backstage/core-plugin-api'; @@ -184,10 +185,19 @@ export const TestApiProvider: ({ // @public export type TestApiProviderProps = { - apis: [...TestApiProviderPropsApiPairs]; + apis: readonly [...TestApiProviderPropsApiPairs]; children: ReactNode; }; +// @public +export class TestApiRegistry implements ApiHolder { + // (undocumented) + get(api: ApiRef): T | undefined; + static with( + ...apis: readonly [...TestApiProviderPropsApiPairs] + ): TestApiRegistry; +} + // @public export type TestAppOptions = { routeEntries?: string[]; diff --git a/packages/test-utils/src/testUtils/TestApiProvider.tsx b/packages/test-utils/src/testUtils/TestApiProvider.tsx index 272da8c807..92be0ffd77 100644 --- a/packages/test-utils/src/testUtils/TestApiProvider.tsx +++ b/packages/test-utils/src/testUtils/TestApiProvider.tsx @@ -18,8 +18,9 @@ import React, { ReactNode } from 'react'; import { ApiProvider } from '@backstage/core-app-api'; import { ApiRef, ApiHolder } from '@backstage/core-plugin-api'; +/** @ignore */ type TestApiProviderPropsApiPair = TApi extends infer TImpl - ? [ApiRef, Partial] + ? readonly [ApiRef, Partial] : never; /** @ignore */ @@ -33,22 +34,58 @@ type TestApiProviderPropsApiPairs = { * @public */ export type TestApiProviderProps = { - apis: [...TestApiProviderPropsApiPairs]; + apis: readonly [...TestApiProviderPropsApiPairs]; children: ReactNode; }; -/** @internal */ -class TestApiRegistry implements ApiHolder { - constructor(private readonly apis: Map) {} +/** + * The `TestApiRegistry` is an {@link @backstage/core-plugin-api#ApiHolder} implementation + * that is particularly well suited for development and test environments such as + * unit tests, storybooks, and isolated plugin development setups. + * + * @public + */ +export class TestApiRegistry implements ApiHolder { + /** + * Creates a new {@link TestApiRegistry} with a list of API implementation pairs. + * + * Similar to the {@link TestApiProvider}, there is no need to provide a full + * implementation of each API, it's enough to implement the methods that are tested. + * + * @example + * ```ts + * const apis = TestApiRegistry.with( + * [configApiRef, new ConfigReader({})], + * [identityApiRef, { getUserId: () => 'tester' }], + * ); + * ``` + * + * @public + * @param apis - A list of pairs mapping an ApiRef to its respective implementation. + */ + static with( + ...apis: readonly [...TestApiProviderPropsApiPairs] + ) { + return new TestApiRegistry( + new Map(apis.map(([api, impl]) => [api.id, impl])), + ); + } + private constructor(private readonly apis: Map) {} + + /** {@inheritdoc @backstage/core-plugin-api#ApiHolder.get} */ get(api: ApiRef): T | undefined { return this.apis.get(api.id) as T | undefined; } } /** - * An API provider that lets you provide any number of API implementations in - * a test, without necessarily having to implement the full APIs. + * The `TestApiProvider` is a Utility API context provider that is particularly + * well suited for development and test environments such as unit tests, storybooks, + * and isolated plugin development setups. + * + * It lets you provide any number of API implementations, without necessarily + * having to fully implement each of the APIs. * * A migration from `ApiRegistry` and `ApiProvider` might look like this, from: * @@ -84,11 +121,6 @@ export const TestApiProvider = ({ children, }: TestApiProviderProps) => { return ( - [api.id, impl]))) - } - children={children} - /> + ); }; diff --git a/packages/test-utils/src/testUtils/index.tsx b/packages/test-utils/src/testUtils/index.tsx index d48767d3cd..c778c5c837 100644 --- a/packages/test-utils/src/testUtils/index.tsx +++ b/packages/test-utils/src/testUtils/index.tsx @@ -22,5 +22,5 @@ export * from './msw'; export * from './Keyboard'; export * from './logCollector'; export * from './testingLibrary'; -export { TestApiProvider } from './TestApiProvider'; +export { TestApiProvider, TestApiRegistry } from './TestApiProvider'; export type { TestApiProviderProps } from './TestApiProvider';