chore: wip
Co-authored-by: Patrik Oldsberg <poldsberg@gmail.com> Co-authored-by: Fredrik Adelöw <freben@users.noreply.github.com> Co-authored-by: Camila Belo <camilaibs@users.noreply.github.com> Co-authored-by: Johan Haals <johan@haals.se> Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
@@ -16,9 +16,11 @@
|
||||
|
||||
import React from 'react';
|
||||
import { SignInPageBlueprint } from './SignInPageBlueprint';
|
||||
import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
import {
|
||||
createExtensionTester,
|
||||
renderInTestApp,
|
||||
} from '@backstage/frontend-test-utils';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import { coreExtensionData, createExtension } from '../wiring';
|
||||
|
||||
describe('SignInPageBlueprint', () => {
|
||||
it('should create an extension with sensible defaults', () => {
|
||||
@@ -60,20 +62,11 @@ describe('SignInPageBlueprint', () => {
|
||||
|
||||
const tester = createExtensionTester(extension);
|
||||
|
||||
expect(tester.data(SignInPageBlueprint.dataRefs.component)).toBeDefined();
|
||||
const Element = tester.data(SignInPageBlueprint.dataRefs.component)!;
|
||||
|
||||
createExtensionTester(
|
||||
createExtension({
|
||||
name: 'dummy',
|
||||
attachTo: { id: 'ignored', input: 'ignored' },
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
},
|
||||
factory: () => ({ element: <div /> }),
|
||||
}),
|
||||
)
|
||||
.add(extension)
|
||||
.render();
|
||||
expect(Element).toBeDefined();
|
||||
|
||||
renderInTestApp(<Element onSignInSuccess={() => {}} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('mock-sign-in')).toBeInTheDocument();
|
||||
|
||||
@@ -49,57 +49,6 @@ import { instantiateAppNodeTree } from '../../../frontend-app-api/src/tree/insta
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { readAppExtensionsConfig } from '../../../frontend-app-api/src/tree/readAppExtensionsConfig';
|
||||
|
||||
const NavItem = (props: {
|
||||
routeRef: RouteRef<undefined>;
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
}) => {
|
||||
const { routeRef, title, icon: Icon } = props;
|
||||
const link = useRouteRef(routeRef);
|
||||
if (!link) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<li>
|
||||
<Link to={link()}>
|
||||
<Icon /> {title}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
const TestAppNavExtension = createExtension({
|
||||
namespace: 'app',
|
||||
name: 'nav',
|
||||
attachTo: { id: 'app/layout', input: 'nav' },
|
||||
inputs: {
|
||||
items: createExtensionInput({
|
||||
target: createNavItemExtension.targetDataRef,
|
||||
}),
|
||||
},
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
},
|
||||
factory({ inputs }) {
|
||||
return {
|
||||
element: (
|
||||
<nav>
|
||||
<ul>
|
||||
{inputs.items.map((item, index) => (
|
||||
<NavItem
|
||||
key={index}
|
||||
icon={item.output.target.icon}
|
||||
title={item.output.target.title}
|
||||
routeRef={item.output.target.routeRef}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
/** @public */
|
||||
export class ExtensionQuery {
|
||||
#node: AppNode;
|
||||
@@ -245,6 +194,25 @@ export class ExtensionTester {
|
||||
return new ExtensionQuery(node);
|
||||
}
|
||||
|
||||
element(): JSX.Element {
|
||||
const tree = this.#resolveTree();
|
||||
|
||||
const element = new ExtensionQuery(tree.root).data(
|
||||
coreExtensionData.reactElement,
|
||||
);
|
||||
|
||||
if (!element) {
|
||||
throw new Error(
|
||||
'No element found. Make sure the extension has a `coreExtensionData.reactElement` output, or use the `.get(myComponentDataRef)` method to get the component',
|
||||
);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Switch to using `renderInTestApp` directly and using `.element()` or `.get(myComponentDataRef)` to get the component you would like to wrap up
|
||||
*/
|
||||
render(options?: { config?: JsonObject }): RenderResult {
|
||||
const { config = {} } = options ?? {};
|
||||
|
||||
@@ -260,7 +228,6 @@ export class ExtensionTester {
|
||||
createExtensionOverrides({
|
||||
extensions: [
|
||||
...this.#extensions.map(extension => extension.definition),
|
||||
TestAppNavExtension,
|
||||
createRouterExtension({
|
||||
namespace: 'test',
|
||||
Component: ({ children }) => (
|
||||
|
||||
@@ -16,11 +16,24 @@
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
ExtensionDefinition,
|
||||
IconComponent,
|
||||
RouteRef,
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
createExtensionInput,
|
||||
createExtensionOverrides,
|
||||
createNavItemExtension,
|
||||
createRouterExtension,
|
||||
useRouteRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { createExtensionTester } from './createExtensionTester';
|
||||
import { Link, MemoryRouter } from 'react-router-dom';
|
||||
import { createSpecializedApp } from '@backstage/frontend-app-api';
|
||||
import { render } from '@testing-library/react';
|
||||
import { resolveExtensionDefinition } from '@backstage/frontend-plugin-api/src/wiring/resolveExtensionDefinition';
|
||||
import { resolve } from 'path';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
|
||||
/**
|
||||
* Options to customize the behavior of the test app.
|
||||
@@ -44,8 +57,64 @@ export type TestAppOptions = {
|
||||
* ```
|
||||
*/
|
||||
mountedRoutes?: { [path: string]: RouteRef };
|
||||
|
||||
/**
|
||||
* Additional configuration passed to the app when rendering elements inside it.
|
||||
*/
|
||||
config?: JsonObject;
|
||||
};
|
||||
|
||||
const NavItem = (props: {
|
||||
routeRef: RouteRef<undefined>;
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
}) => {
|
||||
const { routeRef, title, icon: Icon } = props;
|
||||
const link = useRouteRef(routeRef);
|
||||
if (!link) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<li>
|
||||
<Link to={link()}>
|
||||
<Icon /> {title}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
const TestAppNavExtension = createExtension({
|
||||
namespace: 'app',
|
||||
name: 'nav',
|
||||
attachTo: { id: 'app/layout', input: 'nav' },
|
||||
inputs: {
|
||||
items: createExtensionInput({
|
||||
target: createNavItemExtension.targetDataRef,
|
||||
}),
|
||||
},
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
},
|
||||
factory({ inputs }) {
|
||||
return {
|
||||
element: (
|
||||
<nav>
|
||||
<ul>
|
||||
{inputs.items.map((item, index) => (
|
||||
<NavItem
|
||||
key={index}
|
||||
icon={item.output.target.icon}
|
||||
title={item.output.target.title}
|
||||
routeRef={item.output.target.routeRef}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @public
|
||||
* Renders the given element in a test app, for use in unit tests.
|
||||
@@ -54,20 +123,28 @@ export function renderInTestApp(
|
||||
element: JSX.Element,
|
||||
options?: TestAppOptions,
|
||||
) {
|
||||
const extension = createExtension({
|
||||
namespace: 'test',
|
||||
attachTo: { id: 'app', input: 'root' },
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
},
|
||||
factory: () => ({ element }),
|
||||
});
|
||||
const tester = createExtensionTester(extension);
|
||||
const extensions: Array<ExtensionDefinition<any, any>> = [
|
||||
createExtension({
|
||||
namespace: 'test',
|
||||
attachTo: { id: 'app/routes', input: 'routes' },
|
||||
output: [coreExtensionData.reactElement, coreExtensionData.routePath],
|
||||
factory: () => {
|
||||
return [
|
||||
coreExtensionData.reactElement(element),
|
||||
coreExtensionData.routePath('/'),
|
||||
];
|
||||
},
|
||||
}),
|
||||
createRouterExtension({
|
||||
namespace: 'test',
|
||||
Component: ({ children }) => <MemoryRouter>{children}</MemoryRouter>,
|
||||
}),
|
||||
];
|
||||
|
||||
if (options?.mountedRoutes) {
|
||||
for (const [path, routeRef] of Object.entries(options.mountedRoutes)) {
|
||||
// TODO(Rugvip): add support for external route refs
|
||||
tester.add(
|
||||
extensions.push(
|
||||
createExtension({
|
||||
kind: 'test-route',
|
||||
name: path,
|
||||
@@ -84,5 +161,17 @@ export function renderInTestApp(
|
||||
);
|
||||
}
|
||||
}
|
||||
return tester.render();
|
||||
|
||||
const app = createSpecializedApp({
|
||||
features: [
|
||||
createExtensionOverrides({
|
||||
extensions,
|
||||
}),
|
||||
],
|
||||
config: ConfigReader.fromConfigs([
|
||||
{ context: 'render-config', data: options?.config ?? {} },
|
||||
]),
|
||||
});
|
||||
|
||||
return render(app.createRoot());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user