frontend-app-api: add internal app options

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2026-02-01 15:43:48 +01:00
parent 421770753a
commit 09032d7bd4
3 changed files with 24 additions and 23 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-app-api': patch
---
Internal update to simplify testing utility implementations.
@@ -284,6 +284,14 @@ export type CreateSpecializedAppOptions = {
};
};
// Internal options type, not exported in the public API
export interface CreateSpecializedAppInternalOptions
extends CreateSpecializedAppOptions {
__internal?: {
apiFactoryOverrides?: AnyApiFactory[];
};
}
/**
* Creates an empty app without any default features. This is a low-level API is
* intended for use in tests or specialized setups. Typically you want to use
@@ -296,6 +304,7 @@ export function createSpecializedApp(options?: CreateSpecializedAppOptions): {
tree: AppTree;
errors?: AppError[];
} {
const internalOptions = options as CreateSpecializedAppInternalOptions;
const config = options?.config ?? new ConfigReader({}, 'empty-config');
const features = deduplicateFeatures(options?.features ?? []).map(
createPluginInfoAttacher(config, options?.advanced?.pluginInfoResolver),
@@ -337,6 +346,7 @@ export function createSpecializedApp(options?: CreateSpecializedAppOptions): {
createApiFactory(configApiRef, config),
createApiFactory(routeResolutionApiRef, routeResolutionApi),
createApiFactory(identityApiRef, appIdentityProxy),
...(internalOptions?.__internal?.apiFactoryOverrides ?? []),
],
});
@@ -31,11 +31,13 @@ import {
createFrontendPlugin,
FrontendFeature,
createFrontendModule,
ApiBlueprint,
createApiFactory,
} from '@backstage/frontend-plugin-api';
import { RouterBlueprint } from '@backstage/plugin-app-react';
import appPlugin from '@backstage/plugin-app';
import { type TestApiPairs } from '../utils';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import type { CreateSpecializedAppInternalOptions } from '../../../frontend-app-api/src/wiring/createSpecializedApp';
const DEFAULT_MOCK_CONFIG = {
app: { baseUrl: 'http://localhost:3000' },
@@ -223,27 +225,6 @@ export function renderInTestApp<TApiPairs extends any[] = any[]>(
features.push(...options.features);
}
// If API overrides are provided, add them as a module for the 'app' plugin
// This must come after appPluginOverride so it can override app's default APIs
if (options?.apis) {
features.push(
createFrontendModule({
pluginId: 'app',
extensions: options.apis.map(([apiRef, implementation], index) =>
ApiBlueprint.make({
name: `test-api-override-${index}`,
params: defineParams =>
defineParams({
api: apiRef,
deps: {},
factory: () => implementation,
}),
}),
),
}),
);
}
const app = createSpecializedApp({
features,
config: ConfigReader.fromConfigs([
@@ -252,7 +233,12 @@ export function renderInTestApp<TApiPairs extends any[] = any[]>(
data: options?.config ?? DEFAULT_MOCK_CONFIG,
},
]),
});
__internal: options?.apis && {
apiFactoryOverrides: options.apis.map(([apiRef, implementation]) =>
createApiFactory(apiRef, implementation),
),
},
} as CreateSpecializedAppInternalOptions);
return render(
app.tree.root.instance!.getData(coreExtensionData.reactElement),