From 6362ca4b17e6ffca47c8215b3326d6a04e167474 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 5 Feb 2026 23:36:53 +0100 Subject: [PATCH] catalog-react: factory mock shortcut for catalogApiMock Signed-off-by: Patrik Oldsberg --- packages/frontend-test-utils/report.api.md | 11 +++++-- .../frontend-test-utils/src/apis/index.ts | 2 +- .../frontend-test-utils/src/apis/utils.ts | 33 ++++++++++++++++++- plugins/catalog-graph/src/alpha.test.tsx | 7 ++-- plugins/catalog-react/report-testUtils.api.md | 5 ++- .../src/testUtils/catalogApiMock.ts | 27 +++++++++------ .../testUtils/createTestEntityPage.test.tsx | 3 +- plugins/org/src/alpha.test.tsx | 10 ++---- 8 files changed, 68 insertions(+), 30 deletions(-) diff --git a/packages/frontend-test-utils/report.api.md b/packages/frontend-test-utils/report.api.md index 6b0b4d37b5..d3951a03e8 100644 --- a/packages/frontend-test-utils/report.api.md +++ b/packages/frontend-test-utils/report.api.md @@ -65,6 +65,14 @@ export type ApiMock = { : TApi[Key]; }; +// @public +export function attachMockApiFactory( + apiRef: ApiRef, + implementation: TImpl, +): TImpl & { + [mockApiFactorySymbol]: ApiFactory; +}; + // @public (undocumented) export function createExtensionTester< T extends ExtensionDefinitionParameters, @@ -159,9 +167,6 @@ export class MockAnalyticsApi implements AnalyticsApi { // @public export type MockApiFactorySymbol = typeof mockApiFactorySymbol; -// @public -export const mockApiFactorySymbol: unique symbol; - // @public export namespace mockApis { export function alert(): MockWithApiFactory; diff --git a/packages/frontend-test-utils/src/apis/index.ts b/packages/frontend-test-utils/src/apis/index.ts index 71a7dff3b5..c0275bef3a 100644 --- a/packages/frontend-test-utils/src/apis/index.ts +++ b/packages/frontend-test-utils/src/apis/index.ts @@ -19,7 +19,7 @@ export { type MockApiFactorySymbol, type ApiMock, type MockWithApiFactory, - mockApiFactorySymbol, + attachMockApiFactory, } from './utils'; /** diff --git a/packages/frontend-test-utils/src/apis/utils.ts b/packages/frontend-test-utils/src/apis/utils.ts index 197ab514af..b6f6af6753 100644 --- a/packages/frontend-test-utils/src/apis/utils.ts +++ b/packages/frontend-test-utils/src/apis/utils.ts @@ -20,7 +20,7 @@ import { ApiFactory } from '@backstage/frontend-plugin-api'; /** * Symbol used to mark mock API instances with their corresponding API factory. * - * @public + * @ignore */ export const mockApiFactorySymbol = Symbol.for('@backstage/mock-api'); @@ -78,6 +78,37 @@ export function mockWithApiFactory( return marked; } +/** + * Attaches mock API factory metadata to an API instance, allowing it to be + * passed directly to test utilities without needing to explicitly provide + * the [apiRef, implementation] tuple. + * + * @public + * @example + * ```ts + * const catalogApi = attachMockApiFactory( + * catalogApiRef, + * new InMemoryCatalogClient() + * ); + * // Can now be passed directly to TestApiProvider + * + * ``` + */ +export function attachMockApiFactory( + apiRef: ApiRef, + implementation: TImpl, +): TImpl & { [mockApiFactorySymbol]: ApiFactory } { + const marked = implementation as TImpl & { + [mockApiFactorySymbol]: ApiFactory; + }; + (marked as any)[mockApiFactorySymbol] = { + api: apiRef, + deps: {}, + factory: () => implementation, + }; + return marked; +} + /** * Retrieves the API factory from a mock API instance. * Returns undefined if the value is not a mock API instance. diff --git a/plugins/catalog-graph/src/alpha.test.tsx b/plugins/catalog-graph/src/alpha.test.tsx index 277f6bcc28..e0241ec7b0 100644 --- a/plugins/catalog-graph/src/alpha.test.tsx +++ b/plugins/catalog-graph/src/alpha.test.tsx @@ -21,7 +21,6 @@ import { createTestEntityPage, catalogApiMock, } from '@backstage/plugin-catalog-react/testUtils'; -import { catalogApiRef } from '@backstage/plugin-catalog-react'; import catalogGraphPlugin from './alpha'; import { catalogGraphRouteRef } from './routes'; import { catalogGraphApiRef, DefaultCatalogGraphApi } from './api'; @@ -58,7 +57,7 @@ describe('catalog-graph alpha plugin', () => { '/catalog-graph': catalogGraphRouteRef, }, apis: [ - [catalogApiRef, catalogApiMock({ entities: [entity] })], + catalogApiMock({ entities: [entity] }), [catalogGraphApiRef, new DefaultCatalogGraphApi()], ], }); @@ -95,7 +94,7 @@ describe('catalog-graph alpha plugin', () => { '/catalog-graph': catalogGraphRouteRef, }, apis: [ - [catalogApiRef, catalogApiMock({ entities: [entity] })], + catalogApiMock({ entities: [entity] }), [catalogGraphApiRef, new DefaultCatalogGraphApi()], ], }); @@ -131,7 +130,7 @@ describe('catalog-graph alpha plugin', () => { '/catalog-graph': catalogGraphRouteRef, }, apis: [ - [catalogApiRef, catalogApiMock({ entities: [entity] })], + catalogApiMock({ entities: [entity] }), [catalogGraphApiRef, new DefaultCatalogGraphApi()], ], }); diff --git a/plugins/catalog-react/report-testUtils.api.md b/plugins/catalog-react/report-testUtils.api.md index 0f7b687965..c5c5300761 100644 --- a/plugins/catalog-react/report-testUtils.api.md +++ b/plugins/catalog-react/report-testUtils.api.md @@ -11,10 +11,13 @@ import { Entity } from '@backstage/catalog-model'; import { EntityListContextProps } from '@backstage/plugin-catalog-react'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react/jsx-runtime'; +import { MockWithApiFactory } from '@backstage/frontend-test-utils'; import { PropsWithChildren } from 'react'; // @public -export function catalogApiMock(options?: { entities?: Entity[] }): CatalogApi; +export function catalogApiMock(options?: { + entities?: Entity[]; +}): MockWithApiFactory; // @public export namespace catalogApiMock { diff --git a/plugins/catalog-react/src/testUtils/catalogApiMock.ts b/plugins/catalog-react/src/testUtils/catalogApiMock.ts index a37d2669ad..dd225adc54 100644 --- a/plugins/catalog-react/src/testUtils/catalogApiMock.ts +++ b/plugins/catalog-react/src/testUtils/catalogApiMock.ts @@ -23,7 +23,11 @@ import { InMemoryCatalogClient } from '@backstage/catalog-client/testUtils'; import { Entity } from '@backstage/catalog-model'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { CatalogApi } from '@backstage/catalog-client'; -import { ApiMock } from '@backstage/frontend-test-utils'; +import { + ApiMock, + attachMockApiFactory, + type MockWithApiFactory, +} from '@backstage/frontend-test-utils'; /** @internal */ function simpleMock( @@ -41,13 +45,13 @@ function simpleMock( } } } - return Object.assign(mock, { - factory: createApiFactory({ - api: ref, - deps: {}, - factory: () => mock, - }), - }) as ApiMock; + const factory = createApiFactory({ + api: ref, + deps: {}, + factory: () => mock, + }); + const marked = attachMockApiFactory(ref, mock); + return Object.assign(marked, { factory }) as ApiMock; }; } @@ -58,8 +62,11 @@ function simpleMock( * * @public */ -export function catalogApiMock(options?: { entities?: Entity[] }): CatalogApi { - return new InMemoryCatalogClient(options); +export function catalogApiMock(options?: { + entities?: Entity[]; +}): MockWithApiFactory { + const instance = new InMemoryCatalogClient(options); + return attachMockApiFactory(catalogApiRef, instance); } /** diff --git a/plugins/catalog-react/src/testUtils/createTestEntityPage.test.tsx b/plugins/catalog-react/src/testUtils/createTestEntityPage.test.tsx index 2b15ae5fc4..4fa8465797 100644 --- a/plugins/catalog-react/src/testUtils/createTestEntityPage.test.tsx +++ b/plugins/catalog-react/src/testUtils/createTestEntityPage.test.tsx @@ -23,7 +23,6 @@ import { EntityCardBlueprint, EntityContentBlueprint, } from '../alpha/blueprints'; -import { catalogApiRef } from '../api'; const mockEntity: Entity = { apiVersion: 'backstage.io/v1alpha1', @@ -386,7 +385,7 @@ describe('createTestEntityPage', () => { createTestEntityPage({ entity: mockEntity }), catalogApiCard, ], - apis: [[catalogApiRef, catalogApiMock({ entities: customEntities })]], + apis: [catalogApiMock({ entities: customEntities })], }); // Should use the user-provided catalog API with custom entities, not mockEntity diff --git a/plugins/org/src/alpha.test.tsx b/plugins/org/src/alpha.test.tsx index d643ec79ee..1a42f36e54 100644 --- a/plugins/org/src/alpha.test.tsx +++ b/plugins/org/src/alpha.test.tsx @@ -21,7 +21,6 @@ import { createTestEntityPage, catalogApiMock, } from '@backstage/plugin-catalog-react/testUtils'; -import { catalogApiRef } from '@backstage/plugin-catalog-react'; import orgPlugin from './alpha'; const EntityGroupProfileCard = orgPlugin.getExtension( @@ -230,12 +229,7 @@ describe('org plugin entity cards', () => { createTestEntityPage({ entity: groupEntity }), EntityMembersListCard, ], - apis: [ - [ - catalogApiRef, - catalogApiMock({ entities: [groupEntity, memberUser] }), - ], - ], + apis: [catalogApiMock({ entities: [groupEntity, memberUser] })], }); expect(await screen.findByText('Alice Smith')).toBeInTheDocument(); @@ -261,7 +255,7 @@ describe('org plugin entity cards', () => { createTestEntityPage({ entity: groupEntity }), EntityMembersListCard, ], - apis: [[catalogApiRef, catalogApiMock({ entities: [groupEntity] })]], + apis: [catalogApiMock({ entities: [groupEntity] })], }); expect(