frontend-*-api: test updates for extension ID change

Co-authored-by: Camila Belo <camilaibs@gmail.com>
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-11-22 16:59:32 +01:00
parent 710260b849
commit c387f809d3
9 changed files with 233 additions and 185 deletions
@@ -20,7 +20,7 @@ import { extractRouteInfoFromAppNode } from './extractRouteInfoFromAppNode';
import {
AnyRouteRefParams,
AppNode,
Extension,
ExtensionDefinition,
RouteRef,
coreExtensionData,
createExtension,
@@ -40,16 +40,16 @@ const ref5 = createRouteRef();
const refOrder: RouteRef<AnyRouteRefParams>[] = [ref1, ref2, ref3, ref4, ref5];
function createTestExtension(options: {
id: string;
name: string;
parent?: string;
path?: string;
routeRef?: RouteRef;
}) {
return createExtension({
id: options.id,
name: options.name,
attachTo: options.parent
? { id: options.parent, input: 'children' }
: { id: 'core.routes', input: 'routes' },
? { id: `test/${options.parent}`, input: 'children' }
: { id: 'core/routes', input: 'routes' },
output: {
element: coreExtensionData.reactElement,
path: coreExtensionData.routePath.optional(),
@@ -70,14 +70,14 @@ function createTestExtension(options: {
});
}
function routeInfoFromExtensions(extensions: Extension<unknown>[]) {
function routeInfoFromExtensions(extensions: ExtensionDefinition<unknown>[]) {
const plugin = createPlugin({
id: 'test',
extensions,
});
const tree = createAppTree({
builtinExtensions,
config: new MockConfigApi({}),
builtinExtensions,
features: [plugin],
});
@@ -122,33 +122,33 @@ describe('discovery', () => {
it('should collect routes', () => {
const info = routeInfoFromExtensions([
createTestExtension({
id: 'nothing',
name: 'nothing',
path: 'nothing',
}),
createTestExtension({
id: 'page1',
name: 'page1',
path: 'foo',
routeRef: ref1,
}),
createTestExtension({
id: 'page2',
name: 'page2',
parent: 'page1',
path: 'bar/:id',
routeRef: ref2,
}),
createTestExtension({
id: 'page3',
name: 'page3',
parent: 'page2',
path: 'baz',
routeRef: ref3,
}),
createTestExtension({
id: 'page4',
name: 'page4',
path: 'divsoup',
routeRef: ref4,
}),
createTestExtension({
id: 'page5',
name: 'page5',
parent: 'page1',
path: 'blop',
routeRef: ref5,
@@ -226,29 +226,29 @@ describe('discovery', () => {
it('should handle all react router Route patterns', () => {
const info = routeInfoFromExtensions([
createTestExtension({
id: 'page1',
name: 'page1',
path: 'foo',
routeRef: ref1,
}),
createTestExtension({
id: 'page2',
name: 'page2',
parent: 'page1',
path: 'bar/:id',
routeRef: ref2,
}),
createTestExtension({
id: 'page3',
name: 'page3',
path: 'baz',
routeRef: ref3,
}),
createTestExtension({
id: 'page4',
name: 'page4',
parent: 'page3',
path: 'divsoup',
routeRef: ref4,
}),
createTestExtension({
id: 'page5',
name: 'page5',
parent: 'page3',
path: 'blop',
routeRef: ref5,
@@ -274,29 +274,29 @@ describe('discovery', () => {
it('should strip leading slashes in route paths', () => {
const info = routeInfoFromExtensions([
createTestExtension({
id: 'page1',
name: 'page1',
path: '/foo',
routeRef: ref1,
}),
createTestExtension({
id: 'page2',
name: 'page2',
parent: 'page1',
path: '/bar/:id',
routeRef: ref2,
}),
createTestExtension({
id: 'page3',
name: 'page3',
path: '/baz',
routeRef: ref3,
}),
createTestExtension({
id: 'page4',
name: 'page4',
parent: 'page3',
path: '/divsoup',
routeRef: ref4,
}),
createTestExtension({
id: 'page5',
name: 'page5',
parent: 'page3',
path: '/blop',
routeRef: ref5,
@@ -322,44 +322,44 @@ describe('discovery', () => {
it('should use the route aggregator key to bind child routes to the same path', () => {
const info = routeInfoFromExtensions([
createTestExtension({
id: 'foo',
name: 'foo',
path: 'foo',
}),
createTestExtension({
id: 'page1',
name: 'page1',
parent: 'foo',
routeRef: ref1,
}),
createTestExtension({
id: 'fooChild',
name: 'fooChild',
parent: 'foo',
}),
createTestExtension({
id: 'page2',
name: 'page2',
parent: 'fooChild',
routeRef: ref2,
}),
createTestExtension({
id: 'fooEmpty',
name: 'fooEmpty',
parent: 'foo',
}),
createTestExtension({
id: 'page3',
name: 'page3',
path: 'bar',
routeRef: ref3,
}),
createTestExtension({
id: 'page3Child',
name: 'page3Child',
parent: 'page3',
path: '',
}),
createTestExtension({
id: 'page4',
name: 'page4',
parent: 'page3Child',
routeRef: ref4,
}),
createTestExtension({
id: 'page5',
name: 'page5',
parent: 'page4',
routeRef: ref5,
}),
@@ -411,34 +411,34 @@ describe('discovery', () => {
it('should use the route aggregator but stop when encountering explicit path', () => {
const info = routeInfoFromExtensions([
createTestExtension({
id: 'page1',
name: 'page1',
path: 'foo',
routeRef: ref1,
}),
createTestExtension({
id: 'page1Child',
name: 'page1Child',
parent: 'page1',
path: 'bar',
}),
createTestExtension({
id: 'page2',
name: 'page2',
parent: 'page1Child',
routeRef: ref2,
}),
createTestExtension({
id: 'page3',
name: 'page3',
parent: 'page2',
path: 'baz',
routeRef: ref3,
}),
createTestExtension({
id: 'page4',
name: 'page4',
parent: 'page3',
path: '/blop',
routeRef: ref4,
}),
createTestExtension({
id: 'page5',
name: 'page5',
parent: 'page2',
routeRef: ref5,
}),
@@ -500,34 +500,34 @@ describe('discovery', () => {
it('should account for loose route paths', () => {
const info = routeInfoFromExtensions([
createTestExtension({
id: 'r',
name: 'r',
path: 'r',
}),
createTestExtension({
id: 'page1',
name: 'page1',
parent: 'r',
path: 'x',
routeRef: ref1,
}),
createTestExtension({
id: 'y',
name: 'y',
path: 'y',
parent: 'r',
}),
createTestExtension({
id: 'page2',
name: 'page2',
parent: 'y',
path: '1',
routeRef: ref2,
}),
createTestExtension({
id: 'page3',
name: 'page3',
parent: 'page2',
path: 'a',
routeRef: ref3,
}),
createTestExtension({
id: 'page4',
name: 'page4',
parent: 'page2',
path: 'b',
routeRef: ref4,
@@ -54,12 +54,11 @@ describe('createAppTree', () => {
it('throws an error when a core extension is overridden', () => {
const config = new MockConfigApi({});
const features = [
createPlugin({
id: 'plugin',
createExtensionOverrides({
extensions: [
createExtension({
id: 'core',
attachTo: { id: 'core.routes', input: 'route' },
name: 'core',
attachTo: { id: 'core/routes', input: 'route' },
inputs: {},
output: {},
factory: () => ({}),
@@ -70,33 +69,7 @@ describe('createAppTree', () => {
expect(() =>
createAppTree({ features, config, builtinExtensions: [] }),
).toThrow(
"It is forbidden to override the following extension(s): 'core', which is done by the following plugin(s): 'plugin'",
);
});
it('throws an error when duplicated extensions are detected', () => {
const config = new MockConfigApi({});
const ExtensionA = createExtension({ ...extBase, id: 'A' });
const ExtensionB = createExtension({ ...extBase, id: 'B' });
const PluginA = createPlugin({
id: 'A',
extensions: [ExtensionA, ExtensionA],
});
const PluginB = createPlugin({
id: 'B',
extensions: [ExtensionA, ExtensionB, ExtensionB],
});
const features = [PluginA, PluginB];
expect(() =>
createAppTree({ features, config, builtinExtensions: [] }),
).toThrow(
"The following extensions are duplicated: The extension 'A' was provided 2 time(s) by the plugin 'A' and 1 time(s) by the plugin 'B', The extension 'B' was provided 2 time(s) by the plugin 'B'",
"It is forbidden to override the following extension(s): 'core', which is done by one or more extension overrides",
);
});
@@ -106,13 +79,13 @@ describe('createAppTree', () => {
features: [
createExtensionOverrides({
extensions: [
createExtension({ ...extBase, id: 'a' }),
createExtension({ ...extBase, id: 'a' }),
createExtension({ ...extBase, id: 'b' }),
createExtension({ ...extBase, name: 'a' }),
createExtension({ ...extBase, name: 'a' }),
createExtension({ ...extBase, name: 'b' }),
],
}),
createExtensionOverrides({
extensions: [createExtension({ ...extBase, id: 'b' })],
extensions: [createExtension({ ...extBase, name: 'b' })],
}),
],
config: new MockConfigApi({}),
@@ -25,18 +25,18 @@ describe('readAppExtensionsConfig', () => {
it('should disable extension with shorthand notation', () => {
expect(
readAppExtensionsConfig(
new ConfigReader({ app: { extensions: [{ 'core.router': false }] } }),
new ConfigReader({ app: { extensions: [{ 'core/router': false }] } }),
),
).toEqual([
{
id: 'core.router',
id: 'core/router',
disabled: true,
},
]);
expect(
readAppExtensionsConfig(
new ConfigReader({
app: { extensions: [{ 'core.router': { disabled: true } }] },
app: { extensions: [{ 'core/router': { disabled: true } }] },
}),
),
).toEqual([
@@ -44,7 +44,7 @@ describe('readAppExtensionsConfig', () => {
at: undefined,
config: undefined,
disabled: true,
id: 'core.router',
id: 'core/router',
},
]);
});
@@ -52,33 +52,33 @@ describe('readAppExtensionsConfig', () => {
it('should enable extension with shorthand notation', () => {
expect(
readAppExtensionsConfig(
new ConfigReader({ app: { extensions: ['core.router'] } }),
new ConfigReader({ app: { extensions: ['core/router'] } }),
),
).toEqual([
{
id: 'core.router',
id: 'core/router',
disabled: false,
},
]);
expect(
readAppExtensionsConfig(
new ConfigReader({ app: { extensions: [{ 'core.router': true }] } }),
new ConfigReader({ app: { extensions: [{ 'core/router': true }] } }),
),
).toEqual([
{
id: 'core.router',
id: 'core/router',
disabled: false,
},
]);
expect(
readAppExtensionsConfig(
new ConfigReader({
app: { extensions: [{ 'core.router': { disabled: false } }] },
app: { extensions: [{ 'core/router': { disabled: false } }] },
}),
),
).toEqual([
{
id: 'core.router',
id: 'core/router',
disabled: false,
},
]);
@@ -89,12 +89,12 @@ describe('readAppExtensionsConfig', () => {
readAppExtensionsConfig(
new ConfigReader({
app: {
extensions: [{ 'core.router': 'some-string' }],
extensions: [{ 'core/router': 'some-string' }],
},
}),
),
).toThrow(
'Invalid extension configuration at app.extensions[0][core.router], value must be a boolean or object',
'Invalid extension configuration at app.extensions[0][core/router], value must be a boolean or object',
);
});
@@ -156,8 +156,8 @@ describe('expandShorthandExtensionParameters', () => {
});
it('supports string key', () => {
expect(run('core.router')).toEqual({
id: 'core.router',
expect(run('core/router')).toEqual({
id: 'core/router',
disabled: false,
});
expect(() => run('')).toThrowErrorMatchingInlineSnapshot(
@@ -170,96 +170,96 @@ describe('expandShorthandExtensionParameters', () => {
it('supports null value', () => {
// this is the result of typing:
// - core.router:
// - core/router:
// The missing value is interpreted as null by the yaml parser so we deal with that
expect(run({ 'core.router': null })).toEqual({
id: 'core.router',
expect(run({ 'core/router': null })).toEqual({
id: 'core/router',
disabled: false,
});
});
it('supports boolean value', () => {
expect(run({ 'core.router': true })).toEqual({
id: 'core.router',
expect(run({ 'core/router': true })).toEqual({
id: 'core/router',
disabled: false,
});
expect(run({ 'core.router': false })).toEqual({
id: 'core.router',
expect(run({ 'core/router': false })).toEqual({
id: 'core/router',
disabled: true,
});
});
it('should not support string values', () => {
expect(() =>
run({ 'core.router': 'example-package#MyRouter' }),
run({ 'core/router': 'example-package#MyRouter' }),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core.router], value must be a boolean or object"`,
`"Invalid extension configuration at app.extensions[1][core/router], value must be a boolean or object"`,
);
});
it('supports object id only in the key', () => {
expect(() =>
run({ 'core.router': { id: 'some.id' } }),
run({ 'core/router': { id: 'some.id' } }),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core.router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
`"Invalid extension configuration at app.extensions[1][core/router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
);
});
it('supports object attachTo', () => {
expect(
run({
'core.router': { attachTo: { id: 'other.root', input: 'inputs' } },
'core/router': { attachTo: { id: 'other.root', input: 'inputs' } },
}),
).toEqual({
id: 'core.router',
id: 'core/router',
attachTo: { id: 'other.root', input: 'inputs' },
});
expect(() =>
run({
'core.router': {
'core/router': {
id: 'other-id',
},
}),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core.router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
`"Invalid extension configuration at app.extensions[1][core/router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
);
});
it('supports object disabled', () => {
expect(run({ 'core.router': { disabled: true } })).toEqual({
id: 'core.router',
expect(run({ 'core/router': { disabled: true } })).toEqual({
id: 'core/router',
disabled: true,
});
expect(run({ 'core.router': { disabled: false } })).toEqual({
id: 'core.router',
expect(run({ 'core/router': { disabled: false } })).toEqual({
id: 'core/router',
disabled: false,
});
expect(() =>
run({ 'core.router': { disabled: 0 } }),
run({ 'core/router': { disabled: 0 } }),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core.router].disabled, must be a boolean"`,
`"Invalid extension configuration at app.extensions[1][core/router].disabled, must be a boolean"`,
);
});
it('supports object config', () => {
expect(
run({ 'core.router': { config: { disableRedirects: true } } }),
run({ 'core/router': { config: { disableRedirects: true } } }),
).toEqual({
id: 'core.router',
id: 'core/router',
config: { disableRedirects: true },
});
expect(() =>
run({ 'core.router': { config: 0 } }),
run({ 'core/router': { config: 0 } }),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core.router].config, must be an object"`,
`"Invalid extension configuration at app.extensions[1][core/router].config, must be an object"`,
);
});
it('rejects unknown object keys', () => {
expect(() =>
run({ 'core.router': { foo: { settings: true } } }),
run({ 'core/router': { foo: { settings: true } } }),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core.router].foo, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
`"Invalid extension configuration at app.extensions[1][core/router].foo, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
);
});
});
@@ -18,6 +18,7 @@ import {
createExtensionOverrides,
createPlugin,
Extension,
ExtensionDefinition,
} from '@backstage/frontend-plugin-api';
import { resolveAppNodeSpecs } from './resolveAppNodeSpecs';
@@ -27,12 +28,26 @@ function makeExt(
attachId: string = 'root',
) {
return {
$$type: '@backstage/Extension',
id,
attachTo: { id: attachId, input: 'default' },
disabled: status === 'disabled',
} as Extension<unknown>;
}
function makeExtDef(
name: string,
status: 'disabled' | 'enabled' = 'enabled',
attachId: string = 'root',
) {
return {
$$type: '@backstage/ExtensionDefinition',
name,
attachTo: { id: attachId, input: 'default' },
disabled: status === 'disabled',
} as ExtensionDefinition<unknown>;
}
describe('resolveAppNodeSpecs', () => {
it('should not filter out disabled extension instances', () => {
const a = makeExt('a', 'disabled');
@@ -78,9 +93,8 @@ describe('resolveAppNodeSpecs', () => {
});
it('should override attachment points', () => {
const a = makeExt('a');
const b = makeExt('b');
const pluginA = createPlugin({ id: 'test', extensions: [a] });
const pluginA = createPlugin({ id: 'test', extensions: [makeExtDef('a')] });
expect(
resolveAppNodeSpecs({
features: [pluginA],
@@ -94,8 +108,8 @@ describe('resolveAppNodeSpecs', () => {
}),
).toEqual([
{
id: 'a',
extension: a,
id: 'test/a',
extension: makeExt('test/a'),
attachTo: { id: 'root', input: 'default' },
source: pluginA,
disabled: false,
@@ -110,31 +124,34 @@ describe('resolveAppNodeSpecs', () => {
});
it('should fully override configuration and duplicate', () => {
const a = makeExt('a');
const b = makeExt('b');
const plugin = createPlugin({ id: 'test', extensions: [a, b] });
const a = makeExt('test/a');
const b = makeExt('test/b');
const plugin = createPlugin({
id: 'test',
extensions: [makeExtDef('a'), makeExtDef('b')],
});
expect(
resolveAppNodeSpecs({
features: [plugin],
builtinExtensions: [],
parameters: [
{
id: 'a',
id: 'test/a',
config: { foo: { bar: 1 } },
},
{
id: 'b',
id: 'test/b',
config: { foo: { bar: 2 } },
},
{
id: 'b',
id: 'test/b',
config: { foo: { qux: 3 } },
},
],
}),
).toEqual([
{
id: 'a',
id: 'test/a',
extension: a,
attachTo: { id: 'root', input: 'default' },
source: plugin,
@@ -142,7 +159,7 @@ describe('resolveAppNodeSpecs', () => {
disabled: false,
},
{
id: 'b',
id: 'test/b',
extension: b,
attachTo: { id: 'root', input: 'default' },
source: plugin,
@@ -187,11 +204,12 @@ describe('resolveAppNodeSpecs', () => {
});
it('should apply extension overrides', () => {
const a = makeExt('a');
const b = makeExt('b');
const plugin = createPlugin({ id: 'test', extensions: [a, b] });
const aOverride = makeExt('a', 'enabled', 'other');
const bOverride = makeExt('b', 'disabled', 'other');
const plugin = createPlugin({
id: 'test',
extensions: [makeExtDef('a'), makeExtDef('b')],
});
const aOverride = makeExt('test/a', 'enabled', 'other');
const bOverride = makeExt('test/b', 'disabled', 'other');
const cOverride = makeExt('c');
expect(
@@ -199,7 +217,11 @@ describe('resolveAppNodeSpecs', () => {
features: [
plugin,
createExtensionOverrides({
extensions: [aOverride, bOverride, cOverride],
extensions: [
makeExtDef('test/a', 'enabled', 'other'),
makeExtDef('test/b', 'disabled', 'other'),
makeExtDef('c'),
],
}),
],
builtinExtensions: [],
@@ -207,14 +229,14 @@ describe('resolveAppNodeSpecs', () => {
}),
).toEqual([
{
id: 'a',
id: 'test/a',
extension: aOverride,
attachTo: { id: 'other', input: 'default' },
source: plugin,
disabled: false,
},
{
id: 'b',
id: 'test/b',
extension: bOverride,
attachTo: { id: 'other', input: 'default' },
source: plugin,
@@ -231,39 +253,50 @@ describe('resolveAppNodeSpecs', () => {
});
it('should use order from configuration when rather than overrides', () => {
const a = makeExt('a', 'disabled');
const b = makeExt('b', 'disabled');
const c = makeExt('c', 'disabled');
const aOverride = makeExt('c', 'disabled');
const bOverride = makeExt('b', 'disabled');
const cOverride = makeExt('a', 'disabled');
const result = resolveAppNodeSpecs({
features: [
createPlugin({ id: 'test', extensions: [a, b, c] }),
createPlugin({
id: 'test',
extensions: [
makeExtDef('a', 'disabled'),
makeExtDef('b', 'disabled'),
makeExtDef('c', 'disabled'),
],
}),
createExtensionOverrides({
extensions: [cOverride, bOverride, aOverride],
extensions: [
makeExtDef('test/c', 'disabled'),
makeExtDef('test/b', 'disabled'),
makeExtDef('test/a', 'disabled'),
],
}),
],
builtinExtensions: [],
parameters: ['b', 'c', 'a'].map(id => ({ id, disabled: false })),
parameters: ['test/b', 'test/c', 'test/a'].map(id => ({
id,
disabled: false,
})),
});
expect(result.map(r => r.extension.id)).toEqual(['b', 'c', 'a']);
expect(result.map(r => r.extension.id)).toEqual([
'test/b',
'test/c',
'test/a',
]);
});
it('throws an error when a forbidden extension is overridden by a plugin', () => {
expect(() =>
resolveAppNodeSpecs({
features: [
createPlugin({ id: 'test', extensions: [makeExt('forbidden')] }),
createPlugin({ id: 'test', extensions: [makeExtDef('forbidden')] }),
],
builtinExtensions: [],
parameters: [],
forbidden: new Set(['forbidden']),
forbidden: new Set(['test/forbidden']),
}),
).toThrow(
"It is forbidden to override the following extension(s): 'forbidden', which is done by the following plugin(s): 'test'",
"It is forbidden to override the following extension(s): 'test/forbidden', which is done by the following plugin(s): 'test'",
);
});
@@ -271,7 +304,7 @@ describe('resolveAppNodeSpecs', () => {
expect(() =>
resolveAppNodeSpecs({
features: [
createExtensionOverrides({ extensions: [makeExt('forbidden')] }),
createExtensionOverrides({ extensions: [makeExtDef('forbidden')] }),
],
builtinExtensions: [],
parameters: [],
@@ -16,15 +16,17 @@
import { createExtension } from '@backstage/frontend-plugin-api';
import { resolveAppTree } from './resolveAppTree';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';
const extBaseConfig = {
id: 'test',
attachTo: { id: 'nonexistent', input: 'nonexistent' },
output: {},
factory: () => ({}),
};
const extension = createExtension(extBaseConfig);
const extension = resolveExtensionDefinition(
createExtension({
name: 'test',
attachTo: { id: 'nonexistent', input: 'nonexistent' },
output: {},
factory: () => ({}),
}),
);
const baseSpec = {
extension,
@@ -36,7 +36,10 @@ describe('createApp', () => {
configLoader: async () =>
new MockConfigApi({
app: {
extensions: [{ 'themes.light': false }, { 'themes.dark': false }],
extensions: [
{ 'theme:app/light': false },
{ 'theme:app/dark': false },
],
},
}),
features: [
@@ -68,7 +71,6 @@ describe('createApp', () => {
id: duplicatedFeatureId,
extensions: [
createPageExtension({
id: 'test.page.first',
defaultPath: '/',
loader: async () => <div>First Page</div>,
}),
@@ -78,7 +80,6 @@ describe('createApp', () => {
id: duplicatedFeatureId,
extensions: [
createPageExtension({
id: 'test.page.last',
defaultPath: '/',
loader: async () => <div>Last Page</div>,
}),
@@ -159,7 +160,6 @@ describe('createApp', () => {
id: 'my-plugin',
extensions: [
createPageExtension({
id: 'plugin.my-plugin.page',
defaultPath: '/',
loader: async () => {
const Component = () => {
@@ -182,22 +182,22 @@ describe('createApp', () => {
expect(String(tree.root)).toMatchInlineSnapshot(`
"<core out=[core.reactElement]>
root [
<core.router out=[core.reactElement]>
<core/router out=[core.reactElement]>
children [
<core.layout out=[core.reactElement]>
<core/layout out=[core.reactElement]>
content [
<core.routes out=[core.reactElement]>
<core/routes out=[core.reactElement]>
routes [
<plugin.my-plugin.page out=[core.routing.path, core.routing.ref, core.reactElement] />
<page:my-plugin out=[core.routing.path, core.routing.ref, core.reactElement] />
]
</core.routes>
</core/routes>
]
nav [
<core.nav out=[core.reactElement] />
<core/nav out=[core.reactElement] />
]
</core.layout>
</core/layout>
]
</core.router>
</core/router>
]
components [
<core.components.progress out=[component.ref] />
@@ -206,8 +206,8 @@ describe('createApp', () => {
<core.components.notFoundErrorPage out=[component.ref] />
]
themes [
<themes.light out=[core.theme] />
<themes.dark out=[core.theme] />
<theme:app/light out=[core.theme] />
<theme:app/dark out=[core.theme] />
]
</core>"
`);
@@ -176,7 +176,7 @@ export function createExtensionTree(options: {
);
},
getRootRoutes(): JSX.Element[] {
return this.getExtensionAttachments('core.routes', 'routes').map(node => {
return this.getExtensionAttachments('core/routes', 'routes').map(node => {
const path = node.getData(coreExtensionData.routePath);
const element = node.getData(coreExtensionData.reactElement);
const routeRef = node.getData(coreExtensionData.routeRef);
@@ -203,7 +203,7 @@ export function createExtensionTree(options: {
);
};
return this.getExtensionAttachments('core.nav', 'items')
return this.getExtensionAttachments('core/nav', 'items')
.map((node, index) => {
const target = node.getData(coreExtensionData.navTarget);
if (!target) {
@@ -24,11 +24,10 @@ import { createRouteRef } from '../routing';
import { createExtensionTester } from '@backstage/frontend-test-utils';
const wrapInBoundaryExtension = (element: JSX.Element) => {
const id = 'plugin.extension';
const routeRef = createRouteRef();
return createExtension({
id,
attachTo: { id: 'core.routes', input: 'routes' },
name: 'test',
attachTo: { id: 'core/routes', input: 'routes' },
output: {
element: coreExtensionData.reactElement,
path: coreExtensionData.routePath,
@@ -89,7 +88,7 @@ describe('ExtensionBoundary', () => {
action,
subject,
context: {
extension: 'plugin.extension',
extension: 'test',
routeRef: 'unknown',
},
}),
@@ -37,7 +37,21 @@ describe('createExtensionOverrides', () => {
createExtensionOverrides({
extensions: [
createExtension({
id: 'a',
name: 'a',
attachTo: { id: 'core', input: 'apis' },
output: {},
factory: () => ({}),
}),
createExtension({
namespace: 'b',
attachTo: { id: 'core', input: 'apis' },
output: {},
factory: () => ({}),
}),
createExtension({
kind: 'k',
namespace: 'c',
name: 'n',
attachTo: { id: 'core', input: 'apis' },
output: {},
factory: () => ({}),
@@ -54,12 +68,39 @@ describe('createExtensionOverrides', () => {
"id": "core",
"input": "apis",
},
"configSchema": undefined,
"disabled": false,
"factory": [Function],
"id": "a",
"inputs": {},
"output": {},
},
{
"$$type": "@backstage/Extension",
"attachTo": {
"id": "core",
"input": "apis",
},
"configSchema": undefined,
"disabled": false,
"factory": [Function],
"id": "b",
"inputs": {},
"output": {},
},
{
"$$type": "@backstage/Extension",
"attachTo": {
"id": "core",
"input": "apis",
},
"configSchema": undefined,
"disabled": false,
"factory": [Function],
"id": "k:c/n",
"inputs": {},
"output": {},
},
],
"featureFlags": [],
"version": "v1",
@@ -71,7 +112,7 @@ describe('createExtensionOverrides', () => {
const overrides = createExtensionOverrides({
extensions: [
createExtension({
id: 'a',
namespace: 'a',
attachTo: { id: 'core', input: 'apis' },
output: {},
factory: () => ({}),