frontend-test-utils: api cleanup, remove TestApiRegistry
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -580,11 +580,6 @@ export const TestApiProvider: <T extends any[]>(
|
||||
props: TestApiProviderProps<T>,
|
||||
) => JSX_2.Element;
|
||||
|
||||
// @public
|
||||
export type TestApiProviderEntry =
|
||||
| readonly [ApiRef<any>, any]
|
||||
| MockWithApiFactory<any>;
|
||||
|
||||
// @public
|
||||
export type TestApiProviderProps<TApiPairs extends any[]> = {
|
||||
apis: readonly [
|
||||
@@ -593,22 +588,6 @@ export type TestApiProviderProps<TApiPairs extends any[]> = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type TestApiProviderPropsApiPair<TApi> = TApi extends infer TImpl
|
||||
? readonly [ApiRef<TApi>, Partial<TImpl>]
|
||||
: never;
|
||||
|
||||
// @public
|
||||
export type TestApiProviderPropsApiPairs<TApiPairs> = {
|
||||
[TIndex in keyof TApiPairs]: TestApiProviderPropsApiPair<TApiPairs[TIndex]>;
|
||||
};
|
||||
|
||||
// @public
|
||||
export class TestApiRegistry implements ApiHolder {
|
||||
static from(...apis: readonly TestApiProviderEntry[]): TestApiRegistry;
|
||||
get<T>(api: ApiRef<T>): T | undefined;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type TestAppOptions<TApiPairs extends any[] = any[]> = {
|
||||
mountedRoutes?: {
|
||||
|
||||
+34
-85
@@ -18,11 +18,11 @@ import { ReactNode } from 'react';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { ApiProvider } from '../../../core-app-api/src/apis/system';
|
||||
import { ApiHolder, ApiRef } from '@backstage/frontend-plugin-api';
|
||||
import { getMockApiFactory, type MockWithApiFactory } from '../apis/utils';
|
||||
import { getMockApiFactory, type MockWithApiFactory } from './utils';
|
||||
|
||||
/**
|
||||
* Helper type for representing an API reference paired with a partial implementation.
|
||||
* @public
|
||||
* @ignore
|
||||
*/
|
||||
export type TestApiProviderPropsApiPair<TApi> = TApi extends infer TImpl
|
||||
? readonly [ApiRef<TApi>, Partial<TImpl>]
|
||||
@@ -30,7 +30,7 @@ export type TestApiProviderPropsApiPair<TApi> = TApi extends infer TImpl
|
||||
|
||||
/**
|
||||
* Helper type for representing an array of API reference pairs.
|
||||
* @public
|
||||
* @ignore
|
||||
*/
|
||||
export type TestApiProviderPropsApiPairs<TApiPairs> = {
|
||||
[TIndex in keyof TApiPairs]: TestApiProviderPropsApiPair<TApiPairs[TIndex]>;
|
||||
@@ -43,16 +43,37 @@ export type TestApiProviderPropsApiPairs<TApiPairs> = {
|
||||
export type TestApiPairs<TApiPairs> = TestApiProviderPropsApiPairs<TApiPairs>;
|
||||
|
||||
/**
|
||||
* Type for entries that can be passed to TestApiProvider/TestApiRegistry.
|
||||
* Type for entries that can be passed to TestApiProvider.
|
||||
* Can be either a traditional [apiRef, implementation] tuple or a mock API instance
|
||||
* marked with the mockApiFactorySymbol.
|
||||
*
|
||||
* @public
|
||||
* @internal
|
||||
*/
|
||||
export type TestApiProviderEntry =
|
||||
| readonly [ApiRef<any>, any]
|
||||
| MockWithApiFactory<any>;
|
||||
|
||||
/** @internal */
|
||||
export function resolveTestApiEntries(
|
||||
apis: readonly (TestApiProviderEntry | readonly [ApiRef<any>, any])[],
|
||||
): ApiHolder {
|
||||
const apiMap = new Map<string, unknown>();
|
||||
|
||||
for (const entry of apis) {
|
||||
const mockFactory = getMockApiFactory(entry);
|
||||
if (mockFactory) {
|
||||
apiMap.set(mockFactory.api.id, mockFactory.factory({}));
|
||||
} else {
|
||||
const [apiRef, impl] = entry as readonly [ApiRef<any>, any];
|
||||
apiMap.set(apiRef.id, impl);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
get: <T,>(ref: ApiRef<T>) => apiMap.get(ref.id) as T | undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties for the {@link TestApiProvider} component.
|
||||
*
|
||||
@@ -65,81 +86,6 @@ export type TestApiProviderProps<TApiPairs extends any[]> = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* The `TestApiRegistry` is an {@link @backstage/frontend-plugin-api#ApiHolder} implementation
|
||||
* that is particularly well suited for development and test environments such as
|
||||
* unit tests, storybooks, and isolated plugin development setups.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* For most test scenarios, prefer using the `apis` option in `renderInTestApp` or
|
||||
* `createExtensionTester` instead of creating a registry directly.
|
||||
*
|
||||
* @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
|
||||
* import { identityApiRef } from '@backstage/frontend-plugin-api';
|
||||
* import { mockApis } from '@backstage/frontend-test-utils';
|
||||
*
|
||||
* // Traditional tuple syntax
|
||||
* const apis1 = TestApiRegistry.from(
|
||||
* [identityApiRef, mockApis.identity({ userEntityRef: 'user:default/guest' })],
|
||||
* );
|
||||
*
|
||||
* // Direct mock API instance (no tuple needed)
|
||||
* const apis2 = TestApiRegistry.from(
|
||||
* mockApis.identity({ userEntityRef: 'user:default/guest' }),
|
||||
* mockApis.alert(),
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
* @param apis - A list of pairs mapping an ApiRef to its respective implementation,
|
||||
* or mock API instances marked with the mockApiSymbol.
|
||||
*/
|
||||
static from(...apis: readonly TestApiProviderEntry[]) {
|
||||
const apiMap = new Map<string, unknown>();
|
||||
|
||||
for (const entry of apis) {
|
||||
const mockFactory = getMockApiFactory(entry);
|
||||
if (mockFactory) {
|
||||
// Handle mock API instances marked with the symbol
|
||||
const impl = mockFactory.factory({});
|
||||
apiMap.set(mockFactory.api.id, impl);
|
||||
} else if (Array.isArray(entry)) {
|
||||
// Handle traditional [apiRef, impl] tuples
|
||||
const [apiRef, impl] = entry;
|
||||
apiMap.set(apiRef.id, impl);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Invalid API entry provided to TestApiRegistry.from(). Expected either [apiRef, impl] tuple or a mock API instance.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new TestApiRegistry(apiMap);
|
||||
}
|
||||
|
||||
private constructor(private readonly apis: Map<string, unknown>) {}
|
||||
|
||||
/**
|
||||
* Returns an implementation of the API.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
get<T>(api: ApiRef<T>): T | undefined {
|
||||
return this.apis.get(api.id) as T | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `TestApiProvider` is a Utility API context provider for standalone rendering
|
||||
* scenarios where you're not using `renderInTestApp` or other test utilities.
|
||||
@@ -155,19 +101,22 @@ export class TestApiRegistry implements ApiHolder {
|
||||
* @example
|
||||
* ```tsx
|
||||
* import { render } from '\@testing-library/react';
|
||||
* import { identityApiRef } from '\@backstage/frontend-plugin-api';
|
||||
* import { myCustomApiRef } from '../apis';
|
||||
* import { TestApiProvider, mockApis } from '\@backstage/frontend-test-utils';
|
||||
*
|
||||
* // Traditional tuple syntax
|
||||
* // Mock custom APIs with tuple syntax
|
||||
* const myCustomApiMock = { myMethod: jest.fn() };
|
||||
* render(
|
||||
* <TestApiProvider
|
||||
* apis={[[identityApiRef, mockApis.identity({ userEntityRef: 'user:default/guest' })]]}
|
||||
* apis={[
|
||||
* [myCustomApiRef, myCustomApiMock]
|
||||
* ]}
|
||||
* >
|
||||
* <MyComponent />
|
||||
* </TestApiProvider>
|
||||
* );
|
||||
*
|
||||
* // Direct mock API instances (no tuples needed)
|
||||
* // Use with built-in mock APIs (no tuples needed)
|
||||
* render(
|
||||
* <TestApiProvider
|
||||
* apis={[
|
||||
@@ -187,7 +136,7 @@ export const TestApiProvider = <T extends any[]>(
|
||||
) => {
|
||||
return (
|
||||
<ApiProvider
|
||||
apis={TestApiRegistry.from(...props.apis)}
|
||||
apis={resolveTestApiEntries(props.apis)}
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
@@ -21,6 +21,14 @@ export {
|
||||
type MockWithApiFactory,
|
||||
attachMockApiFactory,
|
||||
} from './utils';
|
||||
export {
|
||||
TestApiProvider,
|
||||
type TestApiProviderPropsApiPair,
|
||||
type TestApiProviderPropsApiPairs,
|
||||
type TestApiPairs,
|
||||
type TestApiProviderEntry,
|
||||
} from './TestApiProvider';
|
||||
export type { TestApiProviderProps } from './TestApiProvider';
|
||||
|
||||
/**
|
||||
* Mock API classes are exported as types only to prevent direct instantiation.
|
||||
|
||||
@@ -38,7 +38,8 @@ import { readAppExtensionsConfig } from '../../../frontend-app-api/src/tree/read
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { createErrorCollector } from '../../../frontend-app-api/src/wiring/createErrorCollector';
|
||||
import { OpaqueExtensionDefinition } from '@internal/frontend';
|
||||
import { TestApiRegistry, type TestApiPairs } from '../utils';
|
||||
import { type TestApiPairs } from '../apis';
|
||||
import { resolveTestApiEntries } from '../apis/TestApiProvider';
|
||||
|
||||
/**
|
||||
* Represents a snapshot of an extension in the app tree.
|
||||
@@ -281,9 +282,7 @@ export class ExtensionTester<UOutput extends ExtensionDataRef> {
|
||||
collector,
|
||||
);
|
||||
|
||||
const apiHolder = this.#apis
|
||||
? TestApiRegistry.from(...this.#apis)
|
||||
: TestApiRegistry.from();
|
||||
const apiHolder = resolveTestApiEntries(this.#apis ?? []);
|
||||
|
||||
instantiateAppNodeTree(tree.root, apiHolder, collector);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { RouterBlueprint } from '@backstage/plugin-app-react';
|
||||
import appPlugin from '@backstage/plugin-app';
|
||||
import { type TestApiProviderPropsApiPairs } from '../utils';
|
||||
import { type TestApiProviderPropsApiPairs } from '../apis';
|
||||
import { getMockApiFactory, type MockWithApiFactory } from '../apis/utils';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import type { CreateSpecializedAppInternalOptions } from '../../../frontend-app-api/src/wiring/createSpecializedApp';
|
||||
|
||||
@@ -33,7 +33,7 @@ import { JsonObject } from '@backstage/types';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { RouterBlueprint } from '@backstage/plugin-app-react';
|
||||
import { type TestApiProviderPropsApiPairs } from '../utils';
|
||||
import { type TestApiProviderPropsApiPairs } from '../apis';
|
||||
import { getMockApiFactory, type MockWithApiFactory } from '../apis/utils';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import type { CreateSpecializedAppInternalOptions } from '../../../frontend-app-api/src/wiring/createSpecializedApp';
|
||||
|
||||
@@ -22,10 +22,9 @@
|
||||
|
||||
export * from './apis';
|
||||
export * from './app';
|
||||
export * from './utils';
|
||||
|
||||
// Explicit export to satisfy API Extractor
|
||||
export type { TestApiPairs } from './utils';
|
||||
export type { TestApiPairs } from './apis';
|
||||
|
||||
export { withLogCollector } from '@backstage/test-utils';
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
TestApiProvider,
|
||||
TestApiRegistry,
|
||||
type TestApiProviderPropsApiPair,
|
||||
type TestApiProviderPropsApiPairs,
|
||||
type TestApiPairs,
|
||||
type TestApiProviderEntry,
|
||||
} from './TestApiProvider';
|
||||
export type { TestApiProviderProps } from './TestApiProvider';
|
||||
Reference in New Issue
Block a user