import { AnalyticsApi, AnalyticsEvent, ConfigApi, ErrorApi, ErrorApiError, ErrorApiErrorContext, FetchApi, DiscoveryApi, IdentityApi, StorageApi, StorageValueSnapshot, ApiFactory, RouteRef, ExternalRouteRef, AppComponents, IconComponent, ApiRef, ApiHolder } from '@backstage/core-plugin-api';
import * as _backstage_config from '@backstage/config';
import { Config } from '@backstage/config';
import { JsonObject, JsonValue, Observable } from '@backstage/types';
import crossFetch from 'cross-fetch';
import { PermissionApi } from '@backstage/plugin-permission-react';
import { EvaluatePermissionRequest, AuthorizeResult, EvaluatePermissionResponse } from '@backstage/plugin-permission-common';
import { TranslationApi } from '@backstage/core-plugin-api/alpha';
import { ReactElement, ComponentType, ReactNode, PropsWithChildren } from 'react';
import { AppIcons } from '@backstage/core-app-api';
import { RenderOptions, RenderResult, MatcherFunction } from '@testing-library/react';
import * as react_jsx_runtime from 'react/jsx-runtime';
/**
* @public
* @deprecated Use `registerMswTestHooks` from `@backstage/test-utils` instead.
*/
declare function setupRequestMockHandlers(worker: {
listen: (t: any) => void;
close: () => void;
resetHandlers: () => void;
}): void;
/**
* Mock implementation of {@link core-plugin-api#AnalyticsApi} with helpers to ensure that events are sent correctly.
* Use getEvents in tests to verify captured events.
*
* @public
* @deprecated Use {@link @backstage/test-utils#mockApis.(analytics:namespace)} instead
*/
declare class MockAnalyticsApi implements AnalyticsApi {
private events;
captureEvent(event: AnalyticsEvent): void;
getEvents(): AnalyticsEvent[];
}
/**
* MockConfigApi is a thin wrapper around {@link @backstage/config#ConfigReader}
* that can be used to mock configuration using a plain object.
*
* @public
* @deprecated Use {@link mockApis.(config:namespace)} instead
* @example
* ```tsx
* const mockConfig = new MockConfigApi({
* app: { baseUrl: 'https://example.com' },
* });
*
* const rendered = await renderInTestApp(
*
*
* ,
* );
* ```
*/
declare class MockConfigApi implements ConfigApi {
private readonly config;
constructor(data: JsonObject);
/** {@inheritdoc @backstage/config#Config.has} */
has(key: string): boolean;
/** {@inheritdoc @backstage/config#Config.keys} */
keys(): string[];
/** {@inheritdoc @backstage/config#Config.get} */
get(key?: string): T;
/** {@inheritdoc @backstage/config#Config.getOptional} */
getOptional(key?: string): T | undefined;
/** {@inheritdoc @backstage/config#Config.getConfig} */
getConfig(key: string): Config;
/** {@inheritdoc @backstage/config#Config.getOptionalConfig} */
getOptionalConfig(key: string): Config | undefined;
/** {@inheritdoc @backstage/config#Config.getConfigArray} */
getConfigArray(key: string): Config[];
/** {@inheritdoc @backstage/config#Config.getOptionalConfigArray} */
getOptionalConfigArray(key: string): Config[] | undefined;
/** {@inheritdoc @backstage/config#Config.getNumber} */
getNumber(key: string): number;
/** {@inheritdoc @backstage/config#Config.getOptionalNumber} */
getOptionalNumber(key: string): number | undefined;
/** {@inheritdoc @backstage/config#Config.getBoolean} */
getBoolean(key: string): boolean;
/** {@inheritdoc @backstage/config#Config.getOptionalBoolean} */
getOptionalBoolean(key: string): boolean | undefined;
/** {@inheritdoc @backstage/config#Config.getString} */
getString(key: string): string;
/** {@inheritdoc @backstage/config#Config.getOptionalString} */
getOptionalString(key: string): string | undefined;
/** {@inheritdoc @backstage/config#Config.getStringArray} */
getStringArray(key: string): string[];
/** {@inheritdoc @backstage/config#Config.getOptionalStringArray} */
getOptionalStringArray(key: string): string[] | undefined;
}
/**
* Constructor arguments for {@link MockErrorApi}
* @public
*/
type MockErrorApiOptions = {
collect?: boolean;
};
/**
* ErrorWithContext contains error and ErrorApiErrorContext
* @public
*/
type ErrorWithContext = {
error: ErrorApiError;
context?: ErrorApiErrorContext;
};
/**
* Mock implementation of the {@link core-plugin-api#ErrorApi} to be used in tests.
* Includes withForError and getErrors methods for error testing.
* @public
*/
declare class MockErrorApi implements ErrorApi {
private readonly options;
private readonly errors;
private readonly waiters;
constructor(options?: MockErrorApiOptions);
post(error: ErrorApiError, context?: ErrorApiErrorContext): void;
error$(): Observable<{
error: ErrorApiError;
context?: ErrorApiErrorContext;
}>;
getErrors(): ErrorWithContext[];
waitForError(pattern: RegExp, timeoutMs?: number): Promise;
}
/**
* The options given when constructing a {@link MockFetchApi}.
*
* @public
*/
interface MockFetchApiOptions {
/**
* Define the underlying base `fetch` implementation.
*
* @defaultValue undefined
* @remarks
*
* Leaving out this parameter or passing `undefined`, makes the API use the
* global `fetch` implementation to make real network requests.
*
* `'none'` swallows all calls and makes no requests at all.
*
* You can also pass in any `fetch` compatible callback, such as a
* `jest.fn()`, if you want to use a custom implementation or to just track
* and assert on calls.
*/
baseImplementation?: undefined | 'none' | typeof crossFetch;
/**
* Add translation from `plugin://` URLs to concrete http(s) URLs, basically
* simulating what
* {@link @backstage/core-app-api#FetchMiddlewares.resolvePluginProtocol}
* does.
*
* @defaultValue undefined
* @remarks
*
* Leaving out this parameter or passing `undefined`, disables plugin protocol
* translation.
*
* To enable the feature, pass in a discovery API which is then used to
* resolve the URLs.
*/
resolvePluginProtocol?: undefined | {
discoveryApi: Pick;
};
/**
* Add token based Authorization headers to requests, basically simulating
* what {@link @backstage/core-app-api#FetchMiddlewares.injectIdentityAuth}
* does.
*
* @defaultValue undefined
* @remarks
*
* Leaving out this parameter or passing `undefined`, disables auth injection.
*
* To enable the feature, pass in either a static token or an identity API
* which is queried on each request for a token.
*/
injectIdentityAuth?: undefined | {
token: string;
} | {
identityApi: Pick;
};
}
/**
* A test helper implementation of {@link @backstage/core-plugin-api#FetchApi}.
*
* @public
*/
declare class MockFetchApi implements FetchApi {
private readonly implementation;
/**
* Creates a mock {@link @backstage/core-plugin-api#FetchApi}.
*/
constructor(options?: MockFetchApiOptions);
/** {@inheritdoc @backstage/core-plugin-api#FetchApi.fetch} */
get fetch(): typeof crossFetch;
}
/**
* Mock implementation of
* {@link @backstage/plugin-permission-react#PermissionApi}. Supply a
* requestHandler function to override the mock result returned for a given
* request.
* @deprecated Use {@link @backstage/test-utils#mockApis.(permission:namespace)} instead
* @public
*/
declare class MockPermissionApi implements PermissionApi {
private readonly requestHandler;
constructor(requestHandler?: (request: EvaluatePermissionRequest) => AuthorizeResult.ALLOW | AuthorizeResult.DENY);
authorize(request: EvaluatePermissionRequest): Promise;
}
/**
* Type for map holding data in {@link MockStorageApi}
* @deprecated Use {@link @backstage/test-utils#mockApis.(storage:namespace)} instead
* @public
*/
type MockStorageBucket = {
[key: string]: any;
};
/**
* Mock implementation of the {@link core-plugin-api#StorageApi} to be used in tests
* @deprecated Use {@link @backstage/test-utils#mockApis.(storage:namespace)} instead
* @public
*/
declare class MockStorageApi implements StorageApi {
private readonly namespace;
private readonly data;
private readonly bucketStorageApis;
private constructor();
static create(data?: MockStorageBucket): MockStorageApi;
forBucket(name: string): StorageApi;
snapshot(key: string): StorageValueSnapshot;
set(key: string, data: T): Promise;
remove(key: string): Promise;
observe$(key: string): Observable>;
private getKeyName;
private notifyChanges;
private subscribers;
private readonly observable;
}
/**
* Represents a mocked version of an API, where you automatically have access to
* the mocked versions of all of its methods along with a factory that returns
* that same mock.
*
* @public
*/
type ApiMock = {
factory: ApiFactory;
} & {
[Key in keyof TApi]: TApi[Key] extends (...args: infer Args) => infer Return ? TApi[Key] & jest.MockInstance : TApi[Key];
};
/**
* Mock implementations of the core utility APIs, to be used in tests.
*
* @public
* @remarks
*
* There are some variations among the APIs depending on what needs tests
* might have, but overall there are three main usage patterns:
*
* 1: Creating an actual fake API instance, often with a simplified version
* of functionality, by calling the mock API itself as a function.
*
* ```ts
* // The function often accepts parameters that control its behavior
* const foo = mockApis.foo();
* ```
*
* 2: Creating a mock API, where all methods are replaced with jest mocks, by
* calling the API's `mock` function.
*
* ```ts
* // You can optionally supply a subset of its methods to implement
* const foo = mockApis.foo.mock({
* someMethod: () => 'mocked result',
* });
* // After exercising your test, you can make assertions on the mock:
* expect(foo.someMethod).toHaveBeenCalledTimes(2);
* expect(foo.otherMethod).toHaveBeenCalledWith(testData);
* ```
*
* 3: Creating an API factory that behaves similarly to the mock as per above.
*
* ```ts
* const factory = mockApis.foo.factory({
* someMethod: () => 'mocked result',
* });
* ```
*/
declare namespace mockApis {
/**
* Mock implementation of {@link @backstage/core-plugin-api#AnalyticsApi}.
*
* @public
*/
function analytics(): AnalyticsApi;
/**
* Mock implementations of {@link @backstage/core-plugin-api#AnalyticsApi}.
*
* @public
*/
namespace analytics {
const factory: () => ApiFactory;
const mock: (partialImpl?: Partial | undefined) => ApiMock;
}
/**
* Fake implementation of {@link @backstage/core-plugin-api#ConfigApi}
* with optional data supplied.
*
* @public
* @example
*
* ```tsx
* const config = mockApis.config({
* data: { app: { baseUrl: 'https://example.com' } },
* });
*
* await renderInTestApp(
*
*
* ,
* );
* ```
*/
function config(options?: {
data?: JsonObject;
}): ConfigApi;
/**
* Mock helpers for {@link @backstage/core-plugin-api#ConfigApi}.
*
* @see {@link @backstage/core-plugin-api#mockApis.config}
* @public
*/
namespace config {
/**
* Creates a factory for a fake implementation of
* {@link @backstage/core-plugin-api#ConfigApi} with optional
* configuration data supplied.
*
* @public
*/
const factory: (options?: {
data?: JsonObject;
} | undefined) => ApiFactory<_backstage_config.Config, _backstage_config.Config, {}>;
/**
* Creates a mock implementation of
* {@link @backstage/core-plugin-api#ConfigApi}. All methods are
* replaced with jest mock functions, and you can optionally pass in a
* subset of methods with an explicit implementation.
*
* @public
*/
const mock: (partialImpl?: Partial<_backstage_config.Config> | undefined) => ApiMock<_backstage_config.Config>;
}
/**
* Fake implementation of {@link @backstage/core-plugin-api#DiscoveryApi}. By
* default returns URLs on the form `http://example.com/api/`.
*
* @public
*/
function discovery(options?: {
baseUrl?: string;
}): DiscoveryApi;
/**
* Mock implementations of {@link @backstage/core-plugin-api#DiscoveryApi}.
*
* @public
*/
namespace discovery {
const factory: (options?: {
baseUrl?: string;
} | undefined) => ApiFactory;
const mock: (partialImpl?: Partial | undefined) => ApiMock;
}
/**
* Fake implementation of {@link @backstage/core-plugin-api#IdentityApi}. By
* default returns no token or profile info, and the user `user:default/test`.
*
* @public
*/
function identity(options?: {
userEntityRef?: string;
ownershipEntityRefs?: string[];
token?: string;
email?: string;
displayName?: string;
picture?: string;
}): IdentityApi;
/**
* Mock implementations of {@link @backstage/core-plugin-api#IdentityApi}.
*
* @public
*/
namespace identity {
const factory: (options?: {
userEntityRef?: string;
ownershipEntityRefs?: string[];
token?: string;
email?: string;
displayName?: string;
picture?: string;
} | undefined) => ApiFactory;
const mock: (partialImpl?: Partial | undefined) => ApiMock;
}
/**
* Fake implementation of
* {@link @backstage/plugin-permission-react#PermissionApi}. By default allows
* all actions.
*
* @public
*/
function permission(options?: {
authorize?: AuthorizeResult.ALLOW | AuthorizeResult.DENY | ((request: EvaluatePermissionRequest) => AuthorizeResult.ALLOW | AuthorizeResult.DENY);
}): PermissionApi;
/**
* Mock implementation of
* {@link @backstage/plugin-permission-react#PermissionApi}.
*
* @public
*/
namespace permission {
const factory: (options?: {
authorize?: AuthorizeResult.ALLOW | AuthorizeResult.DENY | ((request: EvaluatePermissionRequest) => AuthorizeResult.ALLOW | AuthorizeResult.DENY);
} | undefined) => ApiFactory;
const mock: (partialImpl?: Partial | undefined) => ApiMock;
}
/**
* Fake implementation of {@link @backstage/core-plugin-api#StorageApi}.
* Stores data temporarily in memory.
*
* @public
*/
function storage(options?: {
data?: JsonObject;
}): StorageApi;
/**
* Mock implementations of {@link @backstage/core-plugin-api#StorageApi}.
*
* @public
*/
namespace storage {
const factory: (options?: {
data?: JsonObject;
} | undefined) => ApiFactory;
const mock: (partialImpl?: Partial | undefined) => ApiMock;
}
/**
* Fake implementation of {@link @backstage/core-plugin-api/alpha#TranslationApi}.
* By default returns the default translation.
*
* @public
*/
function translation(): TranslationApi;
/**
* Mock implementations of {@link @backstage/core-plugin-api/alpha#TranslationApi}.
*
* @public
*/
namespace translation {
const factory: () => ApiFactory;
const mock: (partialImpl?: Partial | undefined) => ApiMock;
}
}
/**
* This is a mocking method suggested in the Jest docs, as it is not implemented in JSDOM yet.
* It can be used to mock values for the Material UI `useMediaQuery` hook if it is used in a tested component.
*
* For issues checkout the documentation:
* https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom
*
* If there are any updates from Material UI React on testing `useMediaQuery` this mock should be replaced
* https://mui.com/material-ui/react-use-media-query/#testing
*
* @public
* @deprecated Import from `@backstage/core-components/testUtils` instead.
*/
declare function mockBreakpoint(options: {
matches: boolean;
}): void;
/**
* @public
* Set legacy mode when using React 18/RTL 13+.
* Mock this option while we're working against React 17 or lower.
*/
type LegacyRootOption = {
legacyRoot?: boolean;
};
/**
* @public
* Simplifies rendering of async components in by taking care of the wrapping inside act
*
* @remarks
*
* Components using useEffect to perform an asynchronous action (such as fetch) must be rendered within an async
* act call to properly get the final state, even with mocked responses. This utility method makes the signature a bit
* cleaner, since act doesn't return the result of the evaluated function.
* https://github.com/testing-library/react-testing-library/issues/281
* https://github.com/facebook/react/pull/14853
*/
declare function renderWithEffects(nodes: ReactElement, options?: Pick & LegacyRootOption): Promise;
/**
* Options to customize the behavior of the test app wrapper.
* @public
*/
type TestAppOptions = {
/**
* Initial route entries to pass along as `initialEntries` to the router.
*/
routeEntries?: string[];
/**
* An object of paths to mount route ref on, with the key being the path and the value
* being the RouteRef that the path will be bound to. This allows the route refs to be
* used by `useRouteRef` in the rendered elements.
*
* @example
* wrapInTestApp(, \{
* mountedRoutes: \{
* '/my-path': myRouteRef,
* \}
* \})
* // ...
* const link = useRouteRef(myRouteRef)
*/
mountedRoutes?: {
[path: string]: RouteRef | ExternalRouteRef;
};
/**
* Components to be forwarded to the `components` option of `createApp`.
*/
components?: Partial;
/**
* Icons to be forwarded to the `icons` option of `createApp`.
*/
icons?: Partial & {
[key in string]: IconComponent;
};
};
/**
* Creates a Wrapper component that wraps a component inside a Backstage test app,
* providing a mocked theme and app context, along with mocked APIs.
*
* @param options - Additional options for the rendering.
* @public
*/
declare function createTestAppWrapper(options?: TestAppOptions): (props: {
children: ReactNode;
}) => JSX.Element;
/**
* Wraps a component inside a Backstage test app, providing a mocked theme
* and app context, along with mocked APIs.
*
* @param Component - A component or react node to render inside the test app.
* @param options - Additional options for the rendering.
* @public
*/
declare function wrapInTestApp(Component: ComponentType | ReactNode, options?: TestAppOptions): ReactElement;
/**
* Renders a component inside a Backstage test app, providing a mocked theme
* and app context, along with mocked APIs.
*
* The render executes async effects similar to `renderWithEffects`. To avoid this
* behavior, use a regular `render()` + `wrapInTestApp()` instead.
*
* @param Component - A component or react node to render inside the test app.
* @param options - Additional options for the rendering.
* @public
*/
declare function renderInTestApp(Component: ComponentType> | ReactNode, options?: TestAppOptions & LegacyRootOption): Promise;
/**
* Returns a `@testing-library/react` valid MatcherFunction for supplied text
*
* @param string - text Text to match by element's textContent
*
* @public
*/
declare const textContentMatcher: (text: string) => MatcherFunction;
/**
* Sets up handlers for request mocking
* @public
* @param worker - service worker
*/
declare function registerMswTestHooks(worker: {
listen: (t: any) => void;
close: () => void;
resetHandlers: () => void;
}): void;
/**
* Severity levels of {@link CollectedLogs}
* @public
*/
type LogFuncs = 'log' | 'warn' | 'error';
/**
* AsyncLogCollector type used in {@link (withLogCollector:1)} callback function.
* @public
*/
type AsyncLogCollector = () => Promise;
/**
* SyncLogCollector type used in {@link (withLogCollector:2)} callback function.
* @public
*/
type SyncLogCollector = () => void;
/**
* Union type used in {@link (withLogCollector:3)} callback function.
* @public
*/
type LogCollector = AsyncLogCollector | SyncLogCollector;
/**
* Map of severity level and corresponding log lines.
* @public
*/
type CollectedLogs = {
[key in T]: string[];
};
/**
* Asynchronous log collector with that collects all categories
* @public
*/
declare function withLogCollector(callback: AsyncLogCollector): Promise>;
/**
* Synchronous log collector with that collects all categories
* @public
*/
declare function withLogCollector(callback: SyncLogCollector): CollectedLogs;
/**
* Asynchronous log collector with that only collects selected categories
* @public
*/
declare function withLogCollector(logsToCollect: T[], callback: AsyncLogCollector): Promise>;
/**
* Synchronous log collector with that only collects selected categories
* @public
*/
declare function withLogCollector(logsToCollect: T[], callback: SyncLogCollector): CollectedLogs;
/** @ignore */
type TestApiProviderPropsApiPair = TApi extends infer TImpl ? readonly [ApiRef, Partial] : never;
/** @ignore */
type TestApiProviderPropsApiPairs = {
[TIndex in keyof TApiPairs]: TestApiProviderPropsApiPair;
};
/**
* Properties for the {@link TestApiProvider} component.
*
* @public
*/
type TestApiProviderProps = {
apis: readonly [...TestApiProviderPropsApiPairs];
children: ReactNode;
};
/**
* The `TestApiRegistry` is an {@link @backstage/core-plugin-api#ApiHolder} implementation
* that is particularly well suited for development and test environments such as
* unit tests, storybooks, and isolated plugin development setups.
*
* @public
*/
declare class TestApiRegistry implements ApiHolder {
private readonly apis;
/**
* 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
* const apis = TestApiRegistry.from(
* [configApiRef, new ConfigReader({})],
* [identityApiRef, { getUserId: () => 'tester' }],
* );
* ```
*
* @public
* @param apis - A list of pairs mapping an ApiRef to its respective implementation.
*/
static from(...apis: readonly [...TestApiProviderPropsApiPairs]): TestApiRegistry;
private constructor();
/**
* Returns an implementation of the API.
*
* @public
*/
get(api: ApiRef): T | undefined;
}
/**
* The `TestApiProvider` is a Utility API context provider that is particularly
* well suited for development and test environments such as unit tests, storybooks,
* and isolated plugin development setups.
*
* It lets you provide any number of API implementations, without necessarily
* having to fully implement each of the APIs.
*
* @remarks
* todo: remove this remark tag and ship in the api-reference. There's some odd formatting going on when this is made into a markdown doc, that there's no line break between
* the emitted for To the following
so what happens is that when parsing in docusaurus, it thinks that the code block is mdx rather than a code
* snippet. Just omitting this from the report for now until we can work out how to fix later.
* A migration from `ApiRegistry` and `ApiProvider` might look like this, from:
*
* ```tsx
* renderInTestApp(
*
* ...
*
* )
* ```
*
* To the following:
*
* ```tsx
* renderInTestApp(
*
* ...
*
* )
* ```
*
* Note that the cast to `IdentityApi` is no longer needed as long as the mock API
* implements a subset of the `IdentityApi`.
*
* @public
*/
declare const TestApiProvider: (props: TestApiProviderProps) => react_jsx_runtime.JSX.Element;
export { MockAnalyticsApi, MockConfigApi, MockErrorApi, MockFetchApi, MockPermissionApi, MockStorageApi, TestApiProvider, TestApiRegistry, createTestAppWrapper, mockApis, mockBreakpoint, registerMswTestHooks, renderInTestApp, renderWithEffects, setupRequestMockHandlers, textContentMatcher, withLogCollector, wrapInTestApp };
export type { ApiMock, AsyncLogCollector, CollectedLogs, ErrorWithContext, LegacyRootOption, LogCollector, LogFuncs, MockErrorApiOptions, MockFetchApiOptions, MockStorageBucket, SyncLogCollector, TestApiProviderProps, TestAppOptions };