From 56d01c7a7d254c6898446e1fa97616b16061e11f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Feb 2026 20:22:28 +0100 Subject: [PATCH 1/5] test-utils: add `const` type parameter modifier to TestApiProvider and TestApiRegistry.from Signed-off-by: Patrik Oldsberg --- .changeset/fix-test-api-provider-const.md | 5 +++++ packages/test-utils/report.api.md | 4 ++-- packages/test-utils/src/testUtils/TestApiProvider.tsx | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 .changeset/fix-test-api-provider-const.md diff --git a/.changeset/fix-test-api-provider-const.md b/.changeset/fix-test-api-provider-const.md new file mode 100644 index 0000000000..b2bc0f49cd --- /dev/null +++ b/.changeset/fix-test-api-provider-const.md @@ -0,0 +1,5 @@ +--- +'@backstage/test-utils': patch +--- + +Added `const` type parameter modifier to `TestApiProvider` and `TestApiRegistry.from` for improved type inference of API pairs. diff --git a/packages/test-utils/report.api.md b/packages/test-utils/report.api.md index b3cf84b5ae..f830cde655 100644 --- a/packages/test-utils/report.api.md +++ b/packages/test-utils/report.api.md @@ -340,7 +340,7 @@ export function setupRequestMockHandlers(worker: { export type SyncLogCollector = () => void; // @public -export const TestApiProvider: ( +export const TestApiProvider: ( props: TestApiProviderProps, ) => JSX_2.Element; @@ -352,7 +352,7 @@ export type TestApiProviderProps = { // @public export class TestApiRegistry implements ApiHolder { - static from( + static from( ...apis: readonly [...TestApiProviderPropsApiPairs] ): TestApiRegistry; get(api: ApiRef): T | undefined; diff --git a/packages/test-utils/src/testUtils/TestApiProvider.tsx b/packages/test-utils/src/testUtils/TestApiProvider.tsx index 18a7174201..63e8a56f3b 100644 --- a/packages/test-utils/src/testUtils/TestApiProvider.tsx +++ b/packages/test-utils/src/testUtils/TestApiProvider.tsx @@ -63,7 +63,7 @@ export class TestApiRegistry implements ApiHolder { * @public * @param apis - A list of pairs mapping an ApiRef to its respective implementation. */ - static from( + static from( ...apis: readonly [...TestApiProviderPropsApiPairs] ) { return new TestApiRegistry( @@ -124,7 +124,7 @@ export class TestApiRegistry implements ApiHolder { * * @public */ -export const TestApiProvider = ( +export const TestApiProvider = ( props: TestApiProviderProps, ) => { return ( From ce7894007fedddd55f4f47db70b5f3254b9e34be Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Feb 2026 20:24:25 +0100 Subject: [PATCH 2/5] patches: add pr-33057 to patch release Signed-off-by: Patrik Oldsberg --- .patches/pr-33057.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 .patches/pr-33057.txt diff --git a/.patches/pr-33057.txt b/.patches/pr-33057.txt new file mode 100644 index 0000000000..9f17de4205 --- /dev/null +++ b/.patches/pr-33057.txt @@ -0,0 +1 @@ +Added `const` type parameter modifier to `TestApiProvider` and `TestApiRegistry.from` in `@backstage/test-utils` for improved type inference of API pairs. \ No newline at end of file From a39b080302a8c6c4013620700b75a1da401610eb Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Feb 2026 21:03:05 +0100 Subject: [PATCH 3/5] patches: remove pr-33057 from patch release Signed-off-by: Patrik Oldsberg --- .patches/pr-33057.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .patches/pr-33057.txt diff --git a/.patches/pr-33057.txt b/.patches/pr-33057.txt deleted file mode 100644 index 9f17de4205..0000000000 --- a/.patches/pr-33057.txt +++ /dev/null @@ -1 +0,0 @@ -Added `const` type parameter modifier to `TestApiProvider` and `TestApiRegistry.from` in `@backstage/test-utils` for improved type inference of API pairs. \ No newline at end of file From 479282f38e0fcd60385059a6e93bb7cf99910f10 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Feb 2026 21:13:25 +0100 Subject: [PATCH 4/5] frontend-test-utils: fix TestApiPair type inference for tuple syntax Signed-off-by: Patrik Oldsberg --- .changeset/fix-test-api-pair-inference.md | 5 +++++ .changeset/fix-test-api-provider-const.md | 5 ----- .patches/pr-33057.txt | 1 + packages/frontend-test-utils/report.api.md | 2 +- packages/frontend-test-utils/src/apis/TestApiProvider.tsx | 2 +- packages/test-utils/report.api.md | 4 ++-- packages/test-utils/src/testUtils/TestApiProvider.tsx | 4 ++-- 7 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 .changeset/fix-test-api-pair-inference.md delete mode 100644 .changeset/fix-test-api-provider-const.md create mode 100644 .patches/pr-33057.txt diff --git a/.changeset/fix-test-api-pair-inference.md b/.changeset/fix-test-api-pair-inference.md new file mode 100644 index 0000000000..d527a27600 --- /dev/null +++ b/.changeset/fix-test-api-pair-inference.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-test-utils': patch +--- + +Fixed type inference of `TestApiPair` when using tuple syntax by wrapping `MockWithApiFactory` in `NoInfer`. diff --git a/.changeset/fix-test-api-provider-const.md b/.changeset/fix-test-api-provider-const.md deleted file mode 100644 index b2bc0f49cd..0000000000 --- a/.changeset/fix-test-api-provider-const.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/test-utils': patch ---- - -Added `const` type parameter modifier to `TestApiProvider` and `TestApiRegistry.from` for improved type inference of API pairs. diff --git a/.patches/pr-33057.txt b/.patches/pr-33057.txt new file mode 100644 index 0000000000..3499e84a89 --- /dev/null +++ b/.patches/pr-33057.txt @@ -0,0 +1 @@ +Fixed a type inference issue with the `apis` option of testing utilities in `@backstage/frontend-test-utils`. \ No newline at end of file diff --git a/packages/frontend-test-utils/report.api.md b/packages/frontend-test-utils/report.api.md index 1cba02f393..a17c2ed483 100644 --- a/packages/frontend-test-utils/report.api.md +++ b/packages/frontend-test-utils/report.api.md @@ -443,7 +443,7 @@ export type RenderTestAppOptions = { // @public export type TestApiPair = | readonly [ApiRef, TApi extends infer TImpl ? Partial : never] - | MockWithApiFactory; + | MockWithApiFactory>; // @public export type TestApiPairs = { diff --git a/packages/frontend-test-utils/src/apis/TestApiProvider.tsx b/packages/frontend-test-utils/src/apis/TestApiProvider.tsx index 215e8c8a71..d0eca87a33 100644 --- a/packages/frontend-test-utils/src/apis/TestApiProvider.tsx +++ b/packages/frontend-test-utils/src/apis/TestApiProvider.tsx @@ -29,7 +29,7 @@ import { */ export type TestApiPair = | readonly [ApiRef, TApi extends infer TImpl ? Partial : never] - | MockWithApiFactory; + | MockWithApiFactory>; /** * Represents an array of mock API implementation. diff --git a/packages/test-utils/report.api.md b/packages/test-utils/report.api.md index f830cde655..b3cf84b5ae 100644 --- a/packages/test-utils/report.api.md +++ b/packages/test-utils/report.api.md @@ -340,7 +340,7 @@ export function setupRequestMockHandlers(worker: { export type SyncLogCollector = () => void; // @public -export const TestApiProvider: ( +export const TestApiProvider: ( props: TestApiProviderProps, ) => JSX_2.Element; @@ -352,7 +352,7 @@ export type TestApiProviderProps = { // @public export class TestApiRegistry implements ApiHolder { - static from( + static from( ...apis: readonly [...TestApiProviderPropsApiPairs] ): TestApiRegistry; get(api: ApiRef): T | undefined; diff --git a/packages/test-utils/src/testUtils/TestApiProvider.tsx b/packages/test-utils/src/testUtils/TestApiProvider.tsx index 63e8a56f3b..18a7174201 100644 --- a/packages/test-utils/src/testUtils/TestApiProvider.tsx +++ b/packages/test-utils/src/testUtils/TestApiProvider.tsx @@ -63,7 +63,7 @@ export class TestApiRegistry implements ApiHolder { * @public * @param apis - A list of pairs mapping an ApiRef to its respective implementation. */ - static from( + static from( ...apis: readonly [...TestApiProviderPropsApiPairs] ) { return new TestApiRegistry( @@ -124,7 +124,7 @@ export class TestApiRegistry implements ApiHolder { * * @public */ -export const TestApiProvider = ( +export const TestApiProvider = ( props: TestApiProviderProps, ) => { return ( From fba2809b20996c676d6d5ef98c619b0633741aed Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 27 Feb 2026 23:45:54 +0100 Subject: [PATCH 5/5] frontend-test-utils: add type tests for TestApiProvider Signed-off-by: Patrik Oldsberg --- .../src/apis/TestApiProvider.test.tsx | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 packages/frontend-test-utils/src/apis/TestApiProvider.test.tsx diff --git a/packages/frontend-test-utils/src/apis/TestApiProvider.test.tsx b/packages/frontend-test-utils/src/apis/TestApiProvider.test.tsx new file mode 100644 index 0000000000..1bb5673751 --- /dev/null +++ b/packages/frontend-test-utils/src/apis/TestApiProvider.test.tsx @@ -0,0 +1,95 @@ +/* + * Copyright 2020 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. + */ + +import { createApiRef } from '@backstage/frontend-plugin-api'; +import { TestApiProvider } from './TestApiProvider'; +import { mockApis } from './mockApis'; +import { render, screen } from '@testing-library/react'; + +const xApiRef = createApiRef<{ a: string; b: number }>({ + id: 'x', +}); +const yApiRef = createApiRef({ + id: 'y', +}); + +describe('TestApiProvider', () => { + it('should provide tuple APIs and check types', () => { + render( + +
+ , + ); + }); + + it('should allow partial API implementations', () => { + render( + +
+ , + ); + }); + + it('should reject mismatched types in tuple syntax', () => { + render( + // @ts-expect-error - a should be a string, not a number + +
+ , + ); + }); + + it('should accept MockWithApiFactory entries', () => { + render( + +
+ , + ); + }); + + it('should accept a mix of tuples and MockWithApiFactory entries', () => { + render( + +
+ , + ); + }); + + it('should allow empty APIs', () => { + render( + +
+ , + ); + }); + + it('should provide APIs at runtime', async () => { + const alertApi = mockApis.alert(); + + render( + + rendered + , + ); + + expect(await screen.findByText('rendered')).toBeInTheDocument(); + }); +});