From 7aad08e56403c6b956d6c90d6f4f498b89a294a1 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 3 Oct 2023 15:47:38 +0200 Subject: [PATCH] frontend-app-api: further routing info extraction improvements + test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Co-authored-by: Camila Belo Co-authored-by: Vincenzo Scamporlino Signed-off-by: Patrik Oldsberg --- .../extractRouteInfoFromInstanceTree.test.ts | 217 ++++++++++-------- .../extractRouteInfoFromInstanceTree.ts | 32 ++- 2 files changed, 152 insertions(+), 97 deletions(-) diff --git a/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.test.ts b/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.test.ts index fbc3c95fa8..b4e2675d51 100644 --- a/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.test.ts +++ b/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.test.ts @@ -24,8 +24,8 @@ import { extractRouteInfoFromInstanceTree } from './extractRouteInfoFromInstance import { Extension, coreExtensionData, + createExtension, createExtensionInput, - createPageExtension, createPlugin, } from '@backstage/frontend-plugin-api'; import { createInstances } from '../wiring/createApp'; @@ -38,25 +38,32 @@ const ref4 = createRouteRef({ id: 'page4' }); const ref5 = createRouteRef({ id: 'page5' }); const refOrder = [ref1, ref2, ref3, ref4, ref5]; -const emptyLoader = () => Promise.resolve(React.createElement('div')); - function createTestExtension(options: { id: string; at?: string; - path: string; + path?: string; routeRef?: RouteRef; }) { - return createPageExtension({ + return createExtension({ id: options.id, - at: options.at, - defaultPath: options.path, - routeRef: options.routeRef, + at: options.at ?? 'core.routes/children', + output: { + element: coreExtensionData.reactElement, + path: coreExtensionData.routePath.optional(), + routeRef: coreExtensionData.routeRef.optional(), + }, inputs: { - routes: createExtensionInput({ + children: createExtensionInput({ element: coreExtensionData.reactElement, }), }, - loader: emptyLoader, + factory({ bind }) { + bind({ + path: options.path, + routeRef: options.routeRef, + element: React.createElement('div'), + }); + }, }); } @@ -119,13 +126,13 @@ describe('discovery', () => { }), createTestExtension({ id: 'page2', - at: 'page1/routes', + at: 'page1/children', path: 'bar/:id', routeRef: ref2, }), createTestExtension({ id: 'page3', - at: 'page2/routes', + at: 'page2/children', path: 'baz', routeRef: ref3, }), @@ -136,7 +143,7 @@ describe('discovery', () => { }), createTestExtension({ id: 'page5', - at: 'page1/routes', + at: 'page1/children', path: 'blop', routeRef: ref5, }), @@ -187,7 +194,7 @@ describe('discovery', () => { }), createTestExtension({ id: 'page2', - at: 'page1/routes', + at: 'page1/children', path: 'bar/:id', routeRef: ref2, }), @@ -198,13 +205,13 @@ describe('discovery', () => { }), createTestExtension({ id: 'page4', - at: 'page3/routes', + at: 'page3/children', path: 'divsoup', routeRef: ref4, }), createTestExtension({ id: 'page5', - at: 'page3/routes', + at: 'page3/children', path: 'blop', routeRef: ref5, }), @@ -227,37 +234,45 @@ describe('discovery', () => { }); // it('should handle absolute route paths', () => { - // const root = ( - // - // - // }> - // - // } /> - // - // - // }> - // } /> - // } /> - // - // - // - // ); + // const info = routeInfoFromExtensions([ + // createTestExtension({ + // id: 'page1', + // path: '/foo', + // routeRef: ref1, + // }), + // createTestExtension({ + // id: 'page2', + // at: 'page1/children', + // path: '/bar/:id', + // routeRef: ref2, + // }), + // createTestExtension({ + // id: 'page3', + // path: '/baz', + // routeRef: ref3, + // }), + // createTestExtension({ + // id: 'page4', + // at: 'page3/children', + // path: '/divsoup', + // routeRef: ref4, + // }), + // createTestExtension({ + // id: 'page5', + // at: 'page3/children', + // path: '/blop', + // routeRef: ref5, + // }), + // ]); - // const { routing } = traverseElementTree({ - // root, - // discoverers: [childDiscoverer, routeElementDiscoverer], - // collectors: { - // routing: routingV2Collector, - // }, - // }); - // expect(sortedEntries(routing.paths)).toEqual([ + // expect(sortedEntries(info.routePaths)).toEqual([ // [ref1, 'foo'], // [ref2, 'bar/:id'], // [ref3, 'baz'], // [ref4, 'divsoup'], // [ref5, 'blop'], // ]); - // expect(sortedEntries(routing.parents)).toEqual([ + // expect(sortedEntries(info.routeParents)).toEqual([ // [ref1, undefined], // [ref2, ref1], // [ref3, undefined], @@ -266,60 +281,76 @@ describe('discovery', () => { // ]); // }); - // it('should use the route aggregator key to bind child routes to the same path', () => { - // const root = ( - // - // - // - // - //
- // - //
- // HELLO - //
- // }> - // - // - // - // - // - // - //
- //
- // ); + it('should use the route aggregator key to bind child routes to the same path', () => { + const info = routeInfoFromExtensions([ + createTestExtension({ + id: 'foo', + path: 'foo', + }), + createTestExtension({ + id: 'page1', + at: 'foo/children', + routeRef: ref1, + }), + createTestExtension({ + id: 'fooChild', + at: 'foo/children', + }), + createTestExtension({ + id: 'page2', + at: 'fooChild/children', + routeRef: ref2, + }), + createTestExtension({ + id: 'fooEmpty', + }), + createTestExtension({ + id: 'page3', + path: 'bar', + routeRef: ref3, + }), + createTestExtension({ + id: 'page3Child', + at: 'page3/children', + path: '', + }), + createTestExtension({ + id: 'page4', + at: 'page3Child/children', + routeRef: ref4, + }), + createTestExtension({ + id: 'page5', + at: 'page4/children', + routeRef: ref5, + }), + ]); - // const { routing } = traverseElementTree({ - // root, - // discoverers: [childDiscoverer, routeElementDiscoverer], - // collectors: { - // routing: routingV2Collector, - // }, - // }); - // expect(sortedEntries(routing.paths)).toEqual([ - // [ref1, 'foo'], - // [ref2, 'foo'], - // [ref3, 'bar'], - // [ref4, ''], - // [ref5, ''], - // ]); - // expect(sortedEntries(routing.parents)).toEqual([ - // [ref1, undefined], - // [ref2, undefined], - // [ref3, undefined], - // [ref4, ref3], - // [ref5, ref3], - // ]); - // expect(routing.objects).toEqual([ - // routeObj('foo', [ref1, ref2], [], 'gathered', plugin), - // routeObj( - // 'bar', - // [ref3], - // [routeObj('', [ref4, ref5], [], 'gathered', plugin)], - // undefined, - // plugin, - // ), - // ]); - // }); + expect(sortedEntries(info.routePaths)).toEqual([ + [ref1, 'foo'], + [ref2, 'foo'], + [ref3, 'bar'], + [ref4, ''], + [ref5, ''], + ]); + expect(sortedEntries(info.routeParents)).toEqual([ + [ref1, undefined], + [ref2, undefined], + [ref3, undefined], + [ref4, ref3], + [ref5, ref4], + ]); + expect(info.routeObjects).toEqual([ + routeObj('foo', [ref1, ref2], [], 'mounted', expect.any(Object)), + routeObj( + 'bar', + [ref3], + [routeObj('', [ref4, ref5], [], 'mounted', expect.any(Object))], + 'mounted', + expect.any(Object), + ), + ]); + }); // it('should use the route aggregator but stop when encountering explicit path', () => { // const root = ( diff --git a/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.ts b/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.ts index 8d3947b970..2d6af97198 100644 --- a/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.ts +++ b/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.ts @@ -53,15 +53,19 @@ export function extractRouteInfoFromInstanceTree(roots: ExtensionInstance[]): { function visit( current: ExtensionInstance, + collectedPath?: string, + foundRefForCollectedPath: boolean = false, parentRef?: RouteRef, parentObj?: BackstageRouteObject, ) { - const routePath = current.getData(coreExtensionData.routePath) ?? ''; // TODO: need to gather routes instead + const routePath = current.getData(coreExtensionData.routePath); const routeRef = current.getData(coreExtensionData.routeRef); const parentChildren = parentObj?.children ?? routeObjects; let currentObj = parentObj; + let newCollectedPath = collectedPath; + let newFoundRefForCollectedPath = foundRefForCollectedPath; - if (routePath) { + if (routePath !== undefined) { currentObj = { path: routePath, element: 'mounted', @@ -72,6 +76,15 @@ export function extractRouteInfoFromInstanceTree(roots: ExtensionInstance[]): { }; parentChildren.push(currentObj); + + if (newFoundRefForCollectedPath) { + newCollectedPath = routePath; + newFoundRefForCollectedPath = false; + } else { + newCollectedPath = collectedPath + ? joinPaths(collectedPath, routePath) + : routePath; + } } if (routeRef) { @@ -81,7 +94,12 @@ export function extractRouteInfoFromInstanceTree(roots: ExtensionInstance[]): { `Route ref '${routeRefId}' must have the same ID as extension '${current.id}'`, ); } - routePaths.set(routeRef, routePath); + + if (newCollectedPath !== undefined) { + routePaths.set(routeRef, newCollectedPath); + newFoundRefForCollectedPath = true; + } + routeParents.set(routeRef, parentRef); currentObj?.routeRefs.add(routeRef); if (current.source) { @@ -91,7 +109,13 @@ export function extractRouteInfoFromInstanceTree(roots: ExtensionInstance[]): { for (const children of current.attachments.values()) { for (const child of children) { - visit(child, routeRef ?? parentRef, currentObj); + visit( + child, + newCollectedPath, + newFoundRefForCollectedPath, + routeRef ?? parentRef, + currentObj, + ); } } }