diff --git a/packages/frontend-plugin-api/src/blueprints/NavItemBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/NavItemBlueprint.test.tsx index 94b9e7b8e6..daa21f88bb 100644 --- a/packages/frontend-plugin-api/src/blueprints/NavItemBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/NavItemBlueprint.test.tsx @@ -77,7 +77,7 @@ describe('NavItemBlueprint', () => { const tester = createExtensionTester(extension); - expect(tester.data(NavItemBlueprint.dataRefs.target)).toEqual({ + expect(tester.get(NavItemBlueprint.dataRefs.target)).toEqual({ title: 'TEST', icon: MockIcon, routeRef: mockRouteRef, @@ -97,7 +97,7 @@ describe('NavItemBlueprint', () => { config: { title: 'OVERRIDDEN' }, }); - expect(tester.data(NavItemBlueprint.dataRefs.target)).toEqual({ + expect(tester.get(NavItemBlueprint.dataRefs.target)).toEqual({ title: 'OVERRIDDEN', icon: MockIcon, routeRef: mockRouteRef, diff --git a/packages/frontend-plugin-api/src/blueprints/NavLogoBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/NavLogoBlueprint.test.tsx index 4a96475cf5..b976196080 100644 --- a/packages/frontend-plugin-api/src/blueprints/NavLogoBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/NavLogoBlueprint.test.tsx @@ -64,7 +64,7 @@ describe('NavLogoBlueprint', () => { const tester = createExtensionTester(extension); - expect(tester.data(NavLogoBlueprint.dataRefs.logoElements)).toEqual({ + expect(tester.get(NavLogoBlueprint.dataRefs.logoElements)).toEqual({ logoFull, logoIcon, }); diff --git a/packages/frontend-plugin-api/src/blueprints/PageBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/PageBlueprint.test.tsx index d70a426a57..dedc5454bd 100644 --- a/packages/frontend-plugin-api/src/blueprints/PageBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/PageBlueprint.test.tsx @@ -101,7 +101,7 @@ describe('PageBlueprint', () => { // TODO(blam): test for the routePath output doesn't work, due to the the way the test harness works // expect(tester.data(coreExtensionData.routePath)).toBe('/test'); - expect(tester.data(coreExtensionData.routeRef)).toBe(mockRouteRef); + expect(tester.get(coreExtensionData.routeRef)).toBe(mockRouteRef); const { getByTestId } = tester.render(); @@ -147,7 +147,7 @@ describe('PageBlueprint', () => { CardBlueprint.make({ name: 'card', params: {} }), ); - const { getByTestId, getByText } = renderInTestApp(tester.element()); + const { getByTestId, getByText } = renderInTestApp(tester.reactElement()); await waitFor(() => expect(getByTestId('card')).toBeInTheDocument()); await waitFor(() => diff --git a/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.test.tsx index 61b23e56f9..064021bba2 100644 --- a/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.test.tsx @@ -62,7 +62,7 @@ describe('SignInPageBlueprint', () => { const tester = createExtensionTester(extension); - const Element = tester.data(SignInPageBlueprint.dataRefs.component)!; + const Element = tester.get(SignInPageBlueprint.dataRefs.component)!; expect(Element).toBeDefined(); diff --git a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts b/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts index 40323b6a31..e6daf510eb 100644 --- a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts +++ b/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts @@ -56,7 +56,7 @@ describe('ThemeBlueprint', () => { const extension = ThemeBlueprint.make({ params: { theme } }); expect( - createExtensionTester(extension).data(ThemeBlueprint.dataRefs.theme), + createExtensionTester(extension).get(ThemeBlueprint.dataRefs.theme), ).toEqual(theme); }); }); diff --git a/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts b/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts index dfb918053a..3c7b271322 100644 --- a/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts +++ b/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts @@ -76,7 +76,7 @@ describe('TranslationBlueprint', () => { }); expect( - createExtensionTester(extension).data( + createExtensionTester(extension).get( TranslationBlueprint.dataRefs.translation, ), ).toBe(messages); diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index e33bdcb9a1..5902c5c3e7 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -688,7 +688,7 @@ describe('createExtension', () => { const tester = createExtensionTester(overridden); - expect(tester.data(numberDataRef)).toBe(43); + expect(tester.get(numberDataRef)).toBe(43); }); it('should work functionally with overrides', () => { @@ -722,14 +722,14 @@ describe('createExtension', () => { }, }); - expect(createExtensionTester(overriden).data(stringDataRef)).toBe( + expect(createExtensionTester(overriden).get(stringDataRef)).toBe( 'foo-boom-override-hello', ); expect( createExtensionTester(overriden, { config: { foo: 'hello', bar: 'world' }, - }).data(stringDataRef), + }).get(stringDataRef), ).toBe('foo-hello-override-world'); }); @@ -809,7 +809,7 @@ describe('createExtension', () => { .add(singleExt) .add(multi1Ext) .add(multi2Ext) - .data(outputRef), + .get(outputRef), ).toEqual({ opt: 'orig-opt', single: 'orig-single', @@ -836,7 +836,7 @@ describe('createExtension', () => { .add(singleExt) .add(multi1Ext) .add(multi2Ext) - .data(outputRef), + .get(outputRef), ).toEqual({ opt: 'opt', single: 'single', @@ -862,7 +862,7 @@ describe('createExtension', () => { .add(singleExt) .add(multi1Ext) .add(multi2Ext) - .data(outputRef), + .get(outputRef), ).toEqual({ opt: 'none', single: 'single', @@ -885,7 +885,7 @@ describe('createExtension', () => { .add(singleExt) .add(multi1Ext) .add(multi2Ext) - .data(outputRef), + .get(outputRef), ).toEqual({ opt: 'orig-opt', single: 'orig-single', @@ -912,7 +912,7 @@ describe('createExtension', () => { .add(singleExt) .add(multi1Ext) .add(multi2Ext) - .data(outputRef), + .get(outputRef), ).toEqual({ opt: 'orig-opt', single: 'orig-single', @@ -939,7 +939,7 @@ describe('createExtension', () => { .add(singleExt) .add(multi1Ext) .add(multi2Ext) - .data(outputRef), + .get(outputRef), ).toEqual({ opt: 'orig-opt', single: 'orig-single', @@ -966,7 +966,7 @@ describe('createExtension', () => { .add(singleExt) .add(multi1Ext) .add(multi2Ext) - .data(outputRef), + .get(outputRef), ).toEqual({ opt: 'orig-opt', single: 'orig-single', @@ -997,7 +997,7 @@ describe('createExtension', () => { .add(singleExt) .add(multi1Ext) .add(multi2Ext) - .data(outputRef), + .get(outputRef), ).toEqual({ opt: 'none', single: 'override-orig-single', @@ -1023,7 +1023,7 @@ describe('createExtension', () => { .add(singleExt) .add(multi1Ext) .add(multi2Ext) - .data(outputRef), + .get(outputRef), ).toThrowErrorMatchingInlineSnapshot( `"Failed to instantiate extension 'subject', override data provided for input 'multi' must match the length of the original inputs"`, ); @@ -1046,7 +1046,7 @@ describe('createExtension', () => { .add(singleExt) .add(multi1Ext) .add(multi2Ext) - .data(outputRef), + .get(outputRef), ).toThrowErrorMatchingInlineSnapshot( `"Failed to instantiate extension 'subject', override data for input 'multi' may not mix forwarded inputs with data overrides"`, ); @@ -1069,7 +1069,7 @@ describe('createExtension', () => { .add(singleExt) .add(multi1Ext) .add(multi2Ext) - .data(outputRef), + .get(outputRef), ).toThrowErrorMatchingInlineSnapshot( `"Failed to instantiate extension 'subject', missing required extension data value(s) 'test1'"`, ); @@ -1098,7 +1098,7 @@ describe('createExtension', () => { .add(singleExt) .add(multi1Ext) .add(multi2Ext) - .data(outputRef), + .get(outputRef), ).toThrowErrorMatchingInlineSnapshot( `"Failed to instantiate extension 'subject', extension data 'test2' was provided but not declared"`, ); diff --git a/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx b/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx index 889c815fc7..540a986733 100644 --- a/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx +++ b/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx @@ -246,7 +246,7 @@ describe('createExtensionTester', () => { const tester = createExtensionTester(extension); - expect(tester.data(stringDataRef)).toBe('test-text'); + expect(tester.get(stringDataRef)).toBe('test-text'); }); it('should throw an error if trying to access an instance not provided to the tester', () => { @@ -328,8 +328,8 @@ describe('createExtensionTester', () => { const tester = createExtensionTester(extension).add(extension2); - expect(tester.query(extension).data(stringDataRef)).toBe('nest-test-text'); - expect(tester.query(extension2).data(stringDataRef)).toBe('test-text'); + expect(tester.query(extension).get(stringDataRef)).toBe('nest-test-text'); + expect(tester.query(extension2).get(stringDataRef)).toBe('test-text'); // @ts-expect-error expect(tester.query(extension).input('input').data(stringDataRef)).toBe( 'nest-test-text', @@ -362,6 +362,6 @@ describe('createExtensionTester', () => { inputs: { input: 'test-text' }, }); - expect(tester.query(extension).data(stringDataRef)).toBe('nest-test-text'); + expect(tester.query(extension).get(stringDataRef)).toBe('nest-test-text'); }); }); diff --git a/packages/frontend-test-utils/src/app/createExtensionTester.tsx b/packages/frontend-test-utils/src/app/createExtensionTester.tsx index 83ac2aa600..1ca65478dc 100644 --- a/packages/frontend-test-utils/src/app/createExtensionTester.tsx +++ b/packages/frontend-test-utils/src/app/createExtensionTester.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; +import { MemoryRouter, Link } from 'react-router-dom'; import { RenderResult, render } from '@testing-library/react'; import { createSpecializedApp } from '@backstage/frontend-app-api'; import { @@ -24,10 +24,15 @@ import { Extension, ExtensionDataRef, ExtensionDefinition, + IconComponent, + RouteRef, coreExtensionData, createExtension, + createExtensionInput, createExtensionOverrides, + createNavItemExtension, createRouterExtension, + useRouteRef, } from '@backstage/frontend-plugin-api'; import { Config, ConfigReader } from '@backstage/config'; import { JsonArray, JsonObject, JsonValue } from '@backstage/types'; @@ -43,7 +48,57 @@ import { resolveAppNodeSpecs } from '../../../frontend-app-api/src/tree/resolveA import { instantiateAppNodeTree } from '../../../frontend-app-api/src/tree/instantiateAppNodeTree'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { readAppExtensionsConfig } from '../../../frontend-app-api/src/tree/readAppExtensionsConfig'; -import { TestAppNavExtension } from './renderInTestApp'; + +const NavItem = (props: { + routeRef: RouteRef; + title: string; + icon: IconComponent; +}) => { + const { routeRef, title, icon: Icon } = props; + const link = useRouteRef(routeRef); + if (!link) { + return null; + } + return ( +
  • + + {title} + +
  • + ); +}; + +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: ( + + ), + }; + }, +}); /** @public */ export class ExtensionQuery { @@ -69,7 +124,7 @@ export class ExtensionQuery { return instance; } - data(ref: ExtensionDataRef): T | undefined { + get(ref: ExtensionDataRef): T | undefined { return this.instance.getData(ref); } } @@ -164,10 +219,10 @@ export class ExtensionTester { return this; } - data(ref: ExtensionDataRef): T | undefined { + get(ref: ExtensionDataRef): T | undefined { const tree = this.#resolveTree(); - return new ExtensionQuery(tree.root).data(ref); + return new ExtensionQuery(tree.root).get(ref); } query(id: string | ExtensionDefinition): ExtensionQuery { @@ -190,16 +245,16 @@ export class ExtensionTester { return new ExtensionQuery(node); } - element(): JSX.Element { + reactElement(): JSX.Element { const tree = this.#resolveTree(); - const element = new ExtensionQuery(tree.root).data( + const element = new ExtensionQuery(tree.root).get( 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', + 'No element found. Make sure the extension has a `coreExtensionData.reactElement` output, or use the `.get(...)` to access output data directly instead', ); } @@ -207,33 +262,36 @@ export class ExtensionTester { } /** - * @deprecated Switch to using `renderInTestApp` directly and using `.element()` or `.get(myComponentDataRef)` to get the component you would like to wrap up + * @deprecated Switch to using `renderInTestApp` directly and using `.reactElement()` or `.get(...)` to get the component you w */ render(options?: { config?: JsonObject }): RenderResult { const { config = {} } = options ?? {}; + const [subject] = this.#extensions; if (!subject) { throw new Error( 'No subject found. At least one extension should be added to the tester.', ); } + const app = createSpecializedApp({ features: [ createExtensionOverrides({ extensions: [ ...this.#extensions.map(extension => extension.definition), + TestAppNavExtension, createRouterExtension({ namespace: 'test', Component: ({ children }) => ( {children} ), }), - TestAppNavExtension, ], }), ], config: this.#getConfig(config), }); + return render(app.createRoot()); }