Merge pull request #32617 from backstage/rugvip/api-override-test-utils

frontend-test-utils: add API override support
This commit is contained in:
Patrik Oldsberg
2026-02-03 13:55:58 +01:00
committed by GitHub
16 changed files with 710 additions and 354 deletions
@@ -15,11 +15,7 @@
*/
import { screen, waitFor } from '@testing-library/react';
import {
MockErrorApi,
TestApiProvider,
withLogCollector,
} from '@backstage/test-utils';
import { MockErrorApi, withLogCollector } from '@backstage/test-utils';
import { errorApiRef } from '../apis';
import {
createExtensionTester,
@@ -74,11 +70,9 @@ describe('AppRootElementBlueprint', () => {
});
const tester = createExtensionTester(extension);
renderInTestApp(
<TestApiProvider apis={[[errorApiRef, errorApi]]}>
{tester.reactElement()}
</TestApiProvider>,
);
renderInTestApp(tester.reactElement(), {
apis: [[errorApiRef, errorApi]],
});
await waitFor(() => {
const errors = errorApi.getErrors();
@@ -16,7 +16,7 @@
import { useEffect, ReactNode } from 'react';
import { act, screen, waitFor } from '@testing-library/react';
import { TestApiProvider, withLogCollector } from '@backstage/test-utils';
import { withLogCollector } from '@backstage/test-utils';
import { ExtensionBoundary } from './ExtensionBoundary';
import { coreExtensionData, createExtension } from '../wiring';
import { analyticsApiRef } from '../apis/definitions/AnalyticsApi';
@@ -30,6 +30,7 @@ import {
pluginWrapperApiRef,
PluginWrapperApi,
} from '../apis/definitions/PluginWrapperApi';
import { useAppNode } from '@backstage/frontend-plugin-api';
const wrapInBoundaryExtension = (element?: JSX.Element) => {
const routeRef = createRouteRef();
@@ -105,11 +106,12 @@ describe('ExtensionBoundary', () => {
};
renderInTestApp(
<TestApiProvider apis={[[analyticsApiRef, analyticsApiMock]]}>
{createExtensionTester(
wrapInBoundaryExtension(<AnalyticsComponent />),
).reactElement()}
</TestApiProvider>,
createExtensionTester(
wrapInBoundaryExtension(<AnalyticsComponent />),
).reactElement(),
{
apis: [[analyticsApiRef, analyticsApiMock]],
},
);
await waitFor(() => {
@@ -132,9 +134,10 @@ describe('ExtensionBoundary', () => {
};
const WrapperComponent = ({ children }: { children: ReactNode }) => {
const node = useAppNode();
return (
<div data-testid="plugin-wrapper">
<span>Wrapper</span>
<span>Wrapper for {node?.spec.id}</span>
{children}
</div>
);
@@ -150,15 +153,18 @@ describe('ExtensionBoundary', () => {
};
renderInTestApp(
<TestApiProvider apis={[[pluginWrapperApiRef, pluginWrapperApi]]}>
{createExtensionTester(
wrapInBoundaryExtension(<TextComponent />),
).reactElement()}
</TestApiProvider>,
createExtensionTester(
wrapInBoundaryExtension(<TextComponent />),
).reactElement(),
{
apis: [[pluginWrapperApiRef, pluginWrapperApi]],
},
);
expect(await screen.findByTestId('plugin-wrapper')).toBeInTheDocument();
expect(screen.getByText('Wrapper')).toBeInTheDocument();
const wrappers = await screen.findAllByTestId('plugin-wrapper');
expect(wrappers.length).toBeGreaterThan(1);
expect(screen.getByText('Wrapper for app')).toBeInTheDocument();
expect(screen.getByText('Wrapper for test')).toBeInTheDocument();
expect(screen.getByText(text)).toBeInTheDocument();
expect(pluginWrapperApi.getPluginWrapper).toHaveBeenCalledWith('app');
});
@@ -184,11 +190,12 @@ describe('ExtensionBoundary', () => {
const { error } = await withLogCollector(['error'], async () => {
renderInTestApp(
<TestApiProvider apis={[[pluginWrapperApiRef, pluginWrapperApi]]}>
{createExtensionTester(
wrapInBoundaryExtension(<TextComponent />),
).reactElement()}
</TestApiProvider>,
createExtensionTester(
wrapInBoundaryExtension(<TextComponent />),
).reactElement(),
{
apis: [[pluginWrapperApiRef, pluginWrapperApi]],
},
);
await waitFor(() =>
expect(screen.getByText(errorMsg)).toBeInTheDocument(),
@@ -204,9 +211,7 @@ describe('ExtensionBoundary', () => {
);
});
// TODO(Rugvip): Need a way to be able to override APIs in the app to be able to test this properly
// eslint-disable-next-line jest/no-disabled-tests
it.skip('should emit analytics events if routable', async () => {
it('should emit analytics events if routable', async () => {
const Emitter = () => {
const analytics = useAnalytics();
useEffect(() => {
@@ -221,22 +226,25 @@ describe('ExtensionBoundary', () => {
createExtensionTester(
wrapInBoundaryExtension(<Emitter />),
).reactElement(),
// { apis: [[analyticsApiRef, analyticsApiMock]] },
{ apis: [[analyticsApiRef, analyticsApiMock]] },
);
});
// The navigate event is emitted by the app's routing, with app context
expect(analyticsApiMock.captureEvent).toHaveBeenCalledWith(
expect.objectContaining({
action: 'navigate',
subject: '/',
}),
);
// The dummy event from our test extension has the correct extension context
expect(analyticsApiMock.captureEvent).toHaveBeenCalledWith(
expect.objectContaining({
action: 'dummy',
context: expect.objectContaining({
pluginId: 'root',
extensionId: 'test',
}),
}),
);
expect(analyticsApiMock.captureEvent).toHaveBeenCalledWith(
expect.objectContaining({ action: 'dummy' }),
);
});
});