From 35686dc9f79c46f1647badf5c6048bc447c6ff9b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 17 Mar 2026 01:18:13 +0100 Subject: [PATCH] cli: add frontend template filtering coverage Clarify the legacy frontend plugin template label and lock in template selection behavior for legacy and new frontend system apps. Signed-off-by: Patrik Oldsberg Made-with: Cursor --- .../loadPortableTemplateConfig.test.ts | 135 ++++++++++++++++++ .../preparation/loadPortableTemplateConfig.ts | 7 +- 2 files changed, 136 insertions(+), 6 deletions(-) diff --git a/packages/cli-module-new/src/lib/preparation/loadPortableTemplateConfig.test.ts b/packages/cli-module-new/src/lib/preparation/loadPortableTemplateConfig.test.ts index a89d0c3905..454ece648a 100644 --- a/packages/cli-module-new/src/lib/preparation/loadPortableTemplateConfig.test.ts +++ b/packages/cli-module-new/src/lib/preparation/loadPortableTemplateConfig.test.ts @@ -360,6 +360,141 @@ describe('loadPortableTemplateConfig', () => { ); }); + it('should filter out legacy frontend template for new frontend system apps', async () => { + mockDir.setContent({ + 'package.json': JSON.stringify({}), + packages: { + app: { + 'package.json': JSON.stringify({ + dependencies: { + '@backstage/frontend-defaults': '^0.1.0', + }, + }), + }, + }, + node_modules: Object.fromEntries( + defaultTemplates.map(t => { + // Match the real behavior: both frontend-plugin and legacy-frontend-plugin + // have the same template name "frontend-plugin" + const name = t.endsWith('/legacy-frontend-plugin') + ? 'frontend-plugin' + : basename(t); + return [ + t, + { [TEMPLATE_FILE_NAME]: `name: ${name}\nrole: web-library\n` }, + ]; + }), + ), + }); + + const config = await loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + }); + + expect(config.isUsingDefaultTemplates).toBe(true); + + const templateNames = config.templatePointers.map(t => t.name); + expect(templateNames).toContain('frontend-plugin'); + expect(templateNames).toContain('frontend-plugin-module'); + expect(templateNames).toContain('backend-plugin'); + // Legacy template should be filtered out + expect(templateNames).not.toContain('legacy-frontend-plugin'); + + // The frontend-plugin in the list should be from the new template, not legacy + const frontendPlugin = config.templatePointers.find( + t => t.name === 'frontend-plugin', + ); + expect(frontendPlugin?.target).toContain('/frontend-plugin/'); + expect(frontendPlugin?.target).not.toContain('/legacy-frontend-plugin/'); + }); + + it('should filter out new frontend templates for legacy frontend system apps', async () => { + mockDir.setContent({ + 'package.json': JSON.stringify({}), + packages: { + app: { + 'package.json': JSON.stringify({ + dependencies: { + '@backstage/app-defaults': '^0.1.0', + '@backstage/core-app-api': '^0.1.0', + }, + }), + }, + }, + node_modules: Object.fromEntries( + defaultTemplates.map(t => { + const name = t.endsWith('/legacy-frontend-plugin') + ? 'frontend-plugin' + : basename(t); + return [ + t, + { [TEMPLATE_FILE_NAME]: `name: ${name}\nrole: web-library\n` }, + ]; + }), + ), + }); + + const config = await loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + }); + + expect(config.isUsingDefaultTemplates).toBe(true); + + const templateNames = config.templatePointers.map(t => t.name); + // Legacy template should be present (shown as "frontend-plugin") + expect(templateNames).toContain('frontend-plugin'); + expect(templateNames).toContain('backend-plugin'); + // New frontend templates should be filtered out + expect(templateNames).not.toContain('frontend-plugin-module'); + + // The frontend-plugin in the list should be from the legacy template + const frontendPlugin = config.templatePointers.find( + t => t.name === 'frontend-plugin', + ); + expect(frontendPlugin?.target).toContain('/legacy-frontend-plugin/'); + }); + + it('should not filter templates when using explicit configuration', async () => { + mockDir.setContent({ + 'package.json': JSON.stringify({ + backstage: { + cli: { + new: { + templates: ['./my-frontend-plugin', './my-backend-plugin'], + }, + }, + }, + }), + // Even with a new frontend system app, explicit templates aren't filtered + packages: { + app: { + 'package.json': JSON.stringify({ + dependencies: { + '@backstage/frontend-defaults': '^0.1.0', + }, + }), + }, + }, + 'my-frontend-plugin': { + [TEMPLATE_FILE_NAME]: 'name: frontend-plugin\nrole: frontend-plugin\n', + }, + 'my-backend-plugin': { + [TEMPLATE_FILE_NAME]: 'name: backend-plugin\nrole: backend-plugin\n', + }, + }); + + const config = await loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + }); + + expect(config.isUsingDefaultTemplates).toBe(false); + expect(config.templatePointers).toHaveLength(2); + expect(config.templatePointers.map(t => t.name)).toEqual([ + 'frontend-plugin', + 'backend-plugin', + ]); + }); + it('should handle missing backstage.new configuration', async () => { mockDir.setContent({ 'package.json': JSON.stringify({}), diff --git a/packages/cli-module-new/src/lib/preparation/loadPortableTemplateConfig.ts b/packages/cli-module-new/src/lib/preparation/loadPortableTemplateConfig.ts index 3541d5a4cf..d2909ebe98 100644 --- a/packages/cli-module-new/src/lib/preparation/loadPortableTemplateConfig.ts +++ b/packages/cli-module-new/src/lib/preparation/loadPortableTemplateConfig.ts @@ -15,12 +15,7 @@ */ import fs from 'fs-extra'; -import { - resolve as resolvePath, - dirname, - isAbsolute, - join, -} from 'node:path'; +import { resolve as resolvePath, dirname, isAbsolute, join } from 'node:path'; import { targetPaths } from '@backstage/cli-common'; import { defaultTemplates } from '../defaultTemplates'; import {