Merge pull request #21320 from backstage/mob/test-utils
add frontend-test-utils with createExtensionTester
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-test-utils': patch
|
||||
---
|
||||
|
||||
Added `createExtensionTester` for rendering extensions in tests.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-test-utils': minor
|
||||
---
|
||||
|
||||
New testing utility library for `@backstage/frontend-app-api` and `@backstage/frontend-plugin-api`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-app-api': patch
|
||||
---
|
||||
|
||||
Added `createSpecializedApp`, which is a synchronous version of `createApp` where config and features already need to be loaded.
|
||||
@@ -41,6 +41,15 @@ export function createApp(options?: {
|
||||
// @public (undocumented)
|
||||
export function createExtensionTree(options: { config: Config }): ExtensionTree;
|
||||
|
||||
// @public
|
||||
export function createSpecializedApp(options?: {
|
||||
features?: (BackstagePlugin | ExtensionOverrides)[];
|
||||
config?: ConfigApi;
|
||||
bindRoutes?(context: { bind: AppRouteBinder }): void;
|
||||
}): {
|
||||
createRoot(): JSX_2.Element;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ExtensionTree {
|
||||
// (undocumented)
|
||||
|
||||
@@ -244,49 +244,18 @@ export function createApp(options?: {
|
||||
|
||||
const discoveredFeatures = getAvailableFeatures(config);
|
||||
const loadedFeatures = (await options?.featureLoader?.({ config })) ?? [];
|
||||
const allFeatures = deduplicateFeatures([
|
||||
...discoveredFeatures,
|
||||
...loadedFeatures,
|
||||
...(options?.features ?? []),
|
||||
]);
|
||||
|
||||
const tree = createAppTree({
|
||||
features: allFeatures,
|
||||
builtinExtensions,
|
||||
const app = createSpecializedApp({
|
||||
config,
|
||||
});
|
||||
features: [
|
||||
...discoveredFeatures,
|
||||
...loadedFeatures,
|
||||
...(options?.features ?? []),
|
||||
],
|
||||
bindRoutes: options?.bindRoutes,
|
||||
}).createRoot();
|
||||
|
||||
const appContext = createLegacyAppContext(
|
||||
allFeatures.filter(
|
||||
(f): f is BackstagePlugin => f.$$type === '@backstage/BackstagePlugin',
|
||||
),
|
||||
);
|
||||
|
||||
const routeIds = collectRouteIds(allFeatures);
|
||||
|
||||
const App = () => (
|
||||
<ApiProvider apis={createApiHolder(tree, config)}>
|
||||
<AppContextProvider appContext={appContext}>
|
||||
<AppThemeProvider>
|
||||
<RoutingProvider
|
||||
{...extractRouteInfoFromAppNode(tree.root)}
|
||||
routeBindings={resolveRouteBindings(
|
||||
options?.bindRoutes,
|
||||
config,
|
||||
routeIds,
|
||||
)}
|
||||
>
|
||||
{/* TODO: set base path using the logic from AppRouter */}
|
||||
<BrowserRouter>
|
||||
{tree.root.instance!.getData(coreExtensionData.reactElement)}
|
||||
</BrowserRouter>
|
||||
</RoutingProvider>
|
||||
</AppThemeProvider>
|
||||
</AppContextProvider>
|
||||
</ApiProvider>
|
||||
);
|
||||
|
||||
return { default: App };
|
||||
return { default: () => app };
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -301,6 +270,64 @@ export function createApp(options?: {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronous version of {@link createApp}, expecting all features and
|
||||
* config to have been loaded already.
|
||||
* @public
|
||||
*/
|
||||
export function createSpecializedApp(options?: {
|
||||
features?: (BackstagePlugin | ExtensionOverrides)[];
|
||||
config?: ConfigApi;
|
||||
bindRoutes?(context: { bind: AppRouteBinder }): void;
|
||||
}): { createRoot(): JSX.Element } {
|
||||
const {
|
||||
features: duplicatedFeatures = [],
|
||||
config = new ConfigReader({}, 'empty-config'),
|
||||
} = options ?? {};
|
||||
|
||||
const features = deduplicateFeatures(duplicatedFeatures);
|
||||
|
||||
const tree = createAppTree({
|
||||
features,
|
||||
builtinExtensions,
|
||||
config,
|
||||
});
|
||||
|
||||
const appContext = createLegacyAppContext(
|
||||
features.filter(
|
||||
(f): f is BackstagePlugin => f.$$type === '@backstage/BackstagePlugin',
|
||||
),
|
||||
);
|
||||
|
||||
const apiHolder = createApiHolder(tree, config);
|
||||
const routeInfo = extractRouteInfoFromAppNode(tree.root);
|
||||
const routeBindings = resolveRouteBindings(
|
||||
options?.bindRoutes,
|
||||
config,
|
||||
collectRouteIds(features),
|
||||
);
|
||||
const rootEl = tree.root.instance!.getData(coreExtensionData.reactElement);
|
||||
|
||||
const App = () => (
|
||||
<ApiProvider apis={apiHolder}>
|
||||
<AppContextProvider appContext={appContext}>
|
||||
<AppThemeProvider>
|
||||
<RoutingProvider {...routeInfo} routeBindings={routeBindings}>
|
||||
{/* TODO: set base path using the logic from AppRouter */}
|
||||
<BrowserRouter>{rootEl}</BrowserRouter>
|
||||
</RoutingProvider>
|
||||
</AppThemeProvider>
|
||||
</AppContextProvider>
|
||||
</ApiProvider>
|
||||
);
|
||||
|
||||
return {
|
||||
createRoot() {
|
||||
return <App />;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Make sure that we only convert each new plugin instance to its legacy equivalent once
|
||||
const legacyPluginStore = getOrCreateGlobalSingleton(
|
||||
'legacy-plugin-compatibility-store',
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
export {
|
||||
createApp,
|
||||
createSpecializedApp,
|
||||
createExtensionTree,
|
||||
type ExtensionTreeNode,
|
||||
type ExtensionTree,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/frontend-app-api": "workspace:^",
|
||||
"@backstage/frontend-test-utils": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
|
||||
@@ -16,44 +16,12 @@
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import {
|
||||
MockAnalyticsApi,
|
||||
MockConfigApi,
|
||||
TestApiProvider,
|
||||
renderWithEffects,
|
||||
} from '@backstage/test-utils';
|
||||
import { MockAnalyticsApi, TestApiProvider } from '@backstage/test-utils';
|
||||
import { ExtensionBoundary } from './ExtensionBoundary';
|
||||
import {
|
||||
Extension,
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
createPlugin,
|
||||
} from '../wiring';
|
||||
import { coreExtensionData, createExtension } from '../wiring';
|
||||
import { analyticsApiRef, useAnalytics } from '@backstage/core-plugin-api';
|
||||
import { createApp } from '@backstage/frontend-app-api';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { createRouteRef } from '../routing';
|
||||
|
||||
function renderExtensionInTestApp(
|
||||
extension: Extension<unknown>,
|
||||
options?: {
|
||||
config?: JsonObject;
|
||||
},
|
||||
) {
|
||||
const { config = {} } = options ?? {};
|
||||
|
||||
const app = createApp({
|
||||
features: [
|
||||
createPlugin({
|
||||
id: 'plugin',
|
||||
extensions: [extension],
|
||||
}),
|
||||
],
|
||||
configLoader: async () => new MockConfigApi(config),
|
||||
});
|
||||
|
||||
return renderWithEffects(app.createRoot());
|
||||
}
|
||||
import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
|
||||
const wrapInBoundaryExtension = (element: JSX.Element) => {
|
||||
const id = 'plugin.extension';
|
||||
@@ -86,7 +54,7 @@ describe('ExtensionBoundary', () => {
|
||||
const TextComponent = () => {
|
||||
return <p>{text}</p>;
|
||||
};
|
||||
await renderExtensionInTestApp(wrapInBoundaryExtension(<TextComponent />));
|
||||
createExtensionTester(wrapInBoundaryExtension(<TextComponent />)).render();
|
||||
await waitFor(() => expect(screen.getByText(text)).toBeInTheDocument());
|
||||
});
|
||||
|
||||
@@ -95,7 +63,7 @@ describe('ExtensionBoundary', () => {
|
||||
const ErrorComponent = () => {
|
||||
throw new Error(error);
|
||||
};
|
||||
await renderExtensionInTestApp(wrapInBoundaryExtension(<ErrorComponent />));
|
||||
createExtensionTester(wrapInBoundaryExtension(<ErrorComponent />)).render();
|
||||
await waitFor(() => expect(screen.getByText(error)).toBeInTheDocument());
|
||||
});
|
||||
|
||||
@@ -112,13 +80,13 @@ describe('ExtensionBoundary', () => {
|
||||
return null;
|
||||
};
|
||||
|
||||
await renderExtensionInTestApp(
|
||||
createExtensionTester(
|
||||
wrapInBoundaryExtension(
|
||||
<TestApiProvider apis={[[analyticsApiRef, analyticsApiMock]]}>
|
||||
<AnalyticsComponent />
|
||||
</TestApiProvider>,
|
||||
),
|
||||
);
|
||||
).render();
|
||||
|
||||
await waitFor(() =>
|
||||
expect(analyticsApiMock.getEvents()[0]).toMatchObject({
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -0,0 +1,12 @@
|
||||
# @backstage/frontend-test-utils
|
||||
|
||||
Contains utilities that can be used when testing frontend features such as extensions.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the package via Yarn into your own packages:
|
||||
|
||||
```sh
|
||||
cd <package-dir> # if within a monorepo
|
||||
yarn add --dev @backstage/frontend-test-utils
|
||||
```
|
||||
@@ -0,0 +1,30 @@
|
||||
## API Report File for "@backstage/frontend-test-utils"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { Extension } from '@backstage/frontend-plugin-api';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { RenderResult } from '@testing-library/react';
|
||||
|
||||
// @public (undocumented)
|
||||
export function createExtensionTester<TConfig>(
|
||||
subject: Extension<TConfig>,
|
||||
options?: {
|
||||
config?: TConfig;
|
||||
},
|
||||
): ExtensionTester;
|
||||
|
||||
// @public (undocumented)
|
||||
export class ExtensionTester {
|
||||
// (undocumented)
|
||||
add<TConfig>(
|
||||
extension: Extension<TConfig>,
|
||||
options?: {
|
||||
config?: TConfig;
|
||||
},
|
||||
): ExtensionTester;
|
||||
// (undocumented)
|
||||
render(options?: { config?: JsonObject }): RenderResult;
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backstage-frontend-test-utils
|
||||
title: '@backstage/frontend-test-utils'
|
||||
spec:
|
||||
lifecycle: experimental
|
||||
type: backstage-web-library
|
||||
owner: maintainers
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "@backstage/frontend-test-utils",
|
||||
"version": "0.0.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "web-library"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"start": "backstage-cli package start",
|
||||
"build": "backstage-cli package build",
|
||||
"lint": "backstage-cli package lint",
|
||||
"test": "backstage-cli package test",
|
||||
"clean": "backstage-cli package clean",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@testing-library/jest-dom": "^6.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"@backstage/frontend-app-api": "workspace:^",
|
||||
"@backstage/frontend-plugin-api": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@backstage/types": "workspace:^"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@testing-library/react": "^12.1.3 || ^13.0.0 || ^14.0.0",
|
||||
"react": "^16.13.1 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2023 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 {
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { createExtensionTester } from './createExtensionTester';
|
||||
|
||||
describe('createExtensionTester', () => {
|
||||
it('should render a simple extension', async () => {
|
||||
createExtensionTester(
|
||||
createExtension({
|
||||
id: 'test',
|
||||
attachTo: { id: 'ignored', input: 'ignored' },
|
||||
output: { element: coreExtensionData.reactElement },
|
||||
factory: () => ({ element: <div>test</div> }),
|
||||
}),
|
||||
).render();
|
||||
|
||||
await expect(screen.findByText('test')).resolves.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render an extension even if disabled by default', async () => {
|
||||
createExtensionTester(
|
||||
createExtension({
|
||||
id: 'test',
|
||||
attachTo: { id: 'ignored', input: 'ignored' },
|
||||
disabled: true,
|
||||
output: { element: coreExtensionData.reactElement },
|
||||
factory: () => ({ element: <div>test</div> }),
|
||||
}),
|
||||
).render();
|
||||
|
||||
await expect(screen.findByText('test')).resolves.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should fail to render an extension that doesn't output a react element", async () => {
|
||||
expect(() =>
|
||||
createExtensionTester(
|
||||
createExtension({
|
||||
id: 'test',
|
||||
attachTo: { id: 'ignored', input: 'ignored' },
|
||||
disabled: true,
|
||||
output: { path: coreExtensionData.routePath },
|
||||
factory: () => ({ path: '/foo' }),
|
||||
}),
|
||||
).render(),
|
||||
).toThrow(
|
||||
"Failed to instantiate extension 'core', input 'root' did not receive required extension data 'core.reactElement' from extension 'test'",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2023 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 { createSpecializedApp } from '@backstage/frontend-app-api';
|
||||
import { Extension, createPlugin } from '@backstage/frontend-plugin-api';
|
||||
import { MockConfigApi } from '@backstage/test-utils';
|
||||
import { JsonArray, JsonObject, JsonValue } from '@backstage/types';
|
||||
import { RenderResult, render } from '@testing-library/react';
|
||||
|
||||
/** @public */
|
||||
export class ExtensionTester {
|
||||
/** @internal */
|
||||
static forSubject<TConfig>(
|
||||
subject: Extension<TConfig>,
|
||||
options?: { config?: TConfig },
|
||||
): ExtensionTester {
|
||||
const tester = new ExtensionTester();
|
||||
tester.add(subject, options);
|
||||
return tester;
|
||||
}
|
||||
|
||||
readonly #extensions = new Array<{
|
||||
extension: Extension<any>;
|
||||
config?: JsonValue;
|
||||
}>();
|
||||
|
||||
add<TConfig>(
|
||||
extension: Extension<TConfig>,
|
||||
options?: { config?: TConfig },
|
||||
): ExtensionTester {
|
||||
this.#extensions.push({
|
||||
extension,
|
||||
config: options?.config as JsonValue,
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
render(options?: { config?: JsonObject }): RenderResult {
|
||||
const { config = {} } = options ?? {};
|
||||
|
||||
const [subject, ...rest] = this.#extensions;
|
||||
if (!subject) {
|
||||
throw new Error(
|
||||
'No subject found. At least one extension should be added to the tester.',
|
||||
);
|
||||
}
|
||||
|
||||
const extensionsConfig: JsonArray = [
|
||||
...rest.map(entry => ({
|
||||
[entry.extension.id]: {
|
||||
config: entry.config,
|
||||
},
|
||||
})),
|
||||
{
|
||||
[subject.extension.id]: {
|
||||
attachTo: { id: 'core', input: 'root' },
|
||||
config: subject.config,
|
||||
disabled: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
'core.layout': false,
|
||||
},
|
||||
{
|
||||
'core.nav': false,
|
||||
},
|
||||
{
|
||||
'core.routes': false,
|
||||
},
|
||||
];
|
||||
|
||||
const finalConfig = {
|
||||
...config,
|
||||
app: {
|
||||
...(typeof config.app === 'object' ? config.app : undefined),
|
||||
extensions: extensionsConfig,
|
||||
},
|
||||
};
|
||||
|
||||
const app = createSpecializedApp({
|
||||
features: [
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
extensions: this.#extensions.map(entry => entry.extension),
|
||||
}),
|
||||
],
|
||||
config: new MockConfigApi(finalConfig),
|
||||
});
|
||||
|
||||
return render(app.createRoot());
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function createExtensionTester<TConfig>(
|
||||
subject: Extension<TConfig>,
|
||||
options?: { config?: TConfig },
|
||||
): ExtensionTester {
|
||||
return ExtensionTester.forSubject(subject, options);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
|
||||
export {
|
||||
createExtensionTester,
|
||||
type ExtensionTester,
|
||||
} from './createExtensionTester';
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @packageDocumentation
|
||||
*
|
||||
* Contains utilities that can be used when testing frontend features such as extensions.
|
||||
*/
|
||||
|
||||
export * from './app';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2023 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 '@testing-library/jest-dom';
|
||||
@@ -69,6 +69,7 @@
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/core-app-api": "workspace:^",
|
||||
"@backstage/frontend-app-api": "workspace:^",
|
||||
"@backstage/frontend-test-utils": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@testing-library/dom": "^9.0.0",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
|
||||
@@ -14,41 +14,28 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import {
|
||||
createExtensionInput,
|
||||
createPageExtension,
|
||||
createPlugin,
|
||||
createSchemaFromZod,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
import { SearchResult } from '@backstage/plugin-search-common';
|
||||
import { createApp } from '@backstage/frontend-app-api';
|
||||
import { MockConfigApi } from '@backstage/test-utils';
|
||||
|
||||
import { screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import {
|
||||
BaseSearchResultListItemProps,
|
||||
createSearchResultListItemExtension,
|
||||
searchResultItemExtensionData as searchResultListItemExtensionData,
|
||||
} from './alpha';
|
||||
|
||||
// TODO: Remove this mock when we have a permanent solution for nav items extensions
|
||||
// The `GraphiQLIcon` used in "packages/frontend-app-api/src/extensions/CoreNav.tsx" file
|
||||
// is throwing a "ReferenceError: ref is not defined" error during test
|
||||
jest.mock('@backstage/plugin-graphiql', () => ({
|
||||
...jest.requireActual('@backstage/plugin-graphiql'),
|
||||
GraphiQLIcon: () => null,
|
||||
}));
|
||||
|
||||
describe('createSearchResultListItemExtension', () => {
|
||||
it('Should use the correct result component', async () => {
|
||||
type TechDocsSearchReasulListItemProps = BaseSearchResultListItemProps<{
|
||||
type TechDocsSearchResultListItemProps = BaseSearchResultListItemProps<{
|
||||
lineClamp: number;
|
||||
}>;
|
||||
const TechDocsSearchResultItemComponent = (
|
||||
props: TechDocsSearchReasulListItemProps,
|
||||
props: TechDocsSearchResultListItemProps,
|
||||
) => (
|
||||
<div>
|
||||
TechDocs - Rank: {props.rank} - Line clamp: {props.lineClamp}
|
||||
@@ -60,10 +47,12 @@ describe('createSearchResultListItemExtension', () => {
|
||||
id: 'techdocs',
|
||||
attachTo: { id: 'plugin.search.page', input: 'items' },
|
||||
configSchema: createSchemaFromZod(z =>
|
||||
z.object({
|
||||
noTrack: z.boolean().default(true),
|
||||
lineClamp: z.number().default(5),
|
||||
}),
|
||||
z
|
||||
.object({
|
||||
noTrack: z.boolean().default(true),
|
||||
lineClamp: z.number().default(5),
|
||||
})
|
||||
.default({}),
|
||||
),
|
||||
predicate: result => result.type === 'techdocs',
|
||||
component:
|
||||
@@ -158,34 +147,15 @@ describe('createSearchResultListItemExtension', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const SearchPlugin = createPlugin({
|
||||
id: 'search.plugin',
|
||||
extensions: [
|
||||
SearchPageExtension,
|
||||
ExploreSearchResultItemExtension,
|
||||
TechDocsSearchResultItemExtension,
|
||||
],
|
||||
});
|
||||
|
||||
const app = createApp({
|
||||
features: [SearchPlugin],
|
||||
configLoader: async () =>
|
||||
new MockConfigApi({
|
||||
app: {
|
||||
extensions: [
|
||||
{
|
||||
'plugin.search.result.item.techdocs': {
|
||||
config: {
|
||||
lineClamp: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
render(app.createRoot());
|
||||
createExtensionTester(SearchPageExtension)
|
||||
.add(TechDocsSearchResultItemExtension, {
|
||||
// TODO(Rugvip): We need to make the config input type available for use here
|
||||
config: {
|
||||
lineClamp: 3,
|
||||
} as any,
|
||||
})
|
||||
.add(ExploreSearchResultItemExtension)
|
||||
.render();
|
||||
|
||||
expect(await screen.findByText(/Search Page/)).toBeInTheDocument();
|
||||
|
||||
|
||||
@@ -4354,6 +4354,7 @@ __metadata:
|
||||
"@backstage/core-components": "workspace:^"
|
||||
"@backstage/core-plugin-api": "workspace:^"
|
||||
"@backstage/frontend-app-api": "workspace:^"
|
||||
"@backstage/frontend-test-utils": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
"@backstage/types": "workspace:^"
|
||||
"@backstage/version-bridge": "workspace:^"
|
||||
@@ -4371,6 +4372,22 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/frontend-test-utils@workspace:^, @backstage/frontend-test-utils@workspace:packages/frontend-test-utils":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/frontend-test-utils@workspace:packages/frontend-test-utils"
|
||||
dependencies:
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/frontend-app-api": "workspace:^"
|
||||
"@backstage/frontend-plugin-api": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
"@backstage/types": "workspace:^"
|
||||
"@testing-library/jest-dom": ^6.0.0
|
||||
peerDependencies:
|
||||
"@testing-library/react": ^12.1.3 || ^13.0.0 || ^14.0.0
|
||||
react: ^16.13.1 || ^17.0.0 || ^18.0.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/integration-aws-node@workspace:^, @backstage/integration-aws-node@workspace:packages/integration-aws-node":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/integration-aws-node@workspace:packages/integration-aws-node"
|
||||
@@ -9117,6 +9134,7 @@ __metadata:
|
||||
"@backstage/core-plugin-api": "workspace:^"
|
||||
"@backstage/frontend-app-api": "workspace:^"
|
||||
"@backstage/frontend-plugin-api": "workspace:^"
|
||||
"@backstage/frontend-test-utils": "workspace:^"
|
||||
"@backstage/plugin-search-common": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
"@backstage/theme": "workspace:^"
|
||||
|
||||
Reference in New Issue
Block a user