cli/new: template load tests + fixes
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -18,6 +18,7 @@ import { NewTemplateLoader } from './NewTemplateLoader';
|
||||
import { NewConfig } from '../config/types';
|
||||
import inquirer from 'inquirer';
|
||||
import { withLogCollector } from '@backstage/test-utils';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('NewTemplateLoader.selectTemplateInteractively', () => {
|
||||
const mockConfig: NewConfig = {
|
||||
@@ -29,6 +30,10 @@ describe('NewTemplateLoader.selectTemplateInteractively', () => {
|
||||
globals: {},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should select a template interactively', async () => {
|
||||
jest.spyOn(inquirer, 'prompt').mockResolvedValueOnce({ id: 'template1' });
|
||||
|
||||
@@ -84,3 +89,100 @@ describe('NewTemplateLoader.selectTemplateInteractively', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('NewTemplateLoader.loadTemplate', () => {
|
||||
describe('NewTemplateLoader.loadTemplate', () => {
|
||||
it('should load a valid template', async () => {
|
||||
const mockDir = createMockDirectory({
|
||||
content: {
|
||||
'path/to/template1.yaml': `
|
||||
template: template1
|
||||
targetPath: plugins
|
||||
`,
|
||||
'path/to/template1/hello.txt': 'hello world',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await NewTemplateLoader.loadTemplate({
|
||||
id: 'template1',
|
||||
target: mockDir.resolve('path/to/template1.yaml'),
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
id: 'template1',
|
||||
templatePath: mockDir.resolve('path/to/template1'),
|
||||
targetPath: 'plugins',
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if template file does not exist', async () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
await expect(
|
||||
NewTemplateLoader.loadTemplate({
|
||||
id: 'template1',
|
||||
target: mockDir.resolve('path/to/template1.yaml'),
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
/^Failed to load template definition from '.*'; caused by Error: ENOENT/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if template definition is invalid', async () => {
|
||||
const mockDir = createMockDirectory({
|
||||
content: {
|
||||
'path/to/template1.yaml': `invalid: definition`,
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
NewTemplateLoader.loadTemplate({
|
||||
id: 'template1',
|
||||
target: mockDir.resolve('path/to/template1.yaml'),
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
/Invalid template definition at '.*'; caused by Validation error/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if target is a remote URL', async () => {
|
||||
await expect(
|
||||
NewTemplateLoader.loadTemplate({
|
||||
id: 'template1',
|
||||
target: 'http://example.com',
|
||||
}),
|
||||
).rejects.toThrow('Remote templates are not supported yet');
|
||||
});
|
||||
|
||||
it('should throw an error if target directory does not exist', async () => {
|
||||
await expect(
|
||||
NewTemplateLoader.loadTemplate({
|
||||
id: 'template1',
|
||||
target: 'http://example.com',
|
||||
}),
|
||||
).rejects.toThrow('Remote templates are not supported yet');
|
||||
});
|
||||
|
||||
it('should throw an error if template directory does not exist', async () => {
|
||||
const mockDir = createMockDirectory({
|
||||
content: {
|
||||
'path/to/template1.yaml': `
|
||||
template: template1
|
||||
targetPath: plugins
|
||||
`,
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
NewTemplateLoader.loadTemplate({
|
||||
id: 'template1',
|
||||
target: mockDir.resolve('path/to/template1.yaml'),
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
`Failed to load template contents from '${mockDir.resolve(
|
||||
'path/to/template1',
|
||||
)}'`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import { z } from 'zod';
|
||||
import fs from 'fs-extra';
|
||||
import inquirer from 'inquirer';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { dirname } from 'node:path';
|
||||
import { parse as parseYaml } from 'yaml';
|
||||
import { paths } from '../../paths';
|
||||
@@ -90,12 +91,15 @@ export class NewTemplateLoader {
|
||||
if (target.match(/https?:\/\//)) {
|
||||
throw new Error('Remote templates are not supported yet');
|
||||
}
|
||||
if (!fs.existsSync(paths.resolveTargetRoot(target))) {
|
||||
throw new Error(`Your CLI template does not exist: ${target}`);
|
||||
}
|
||||
const rawTemplate = parseYaml(
|
||||
fs.readFileSync(paths.resolveTargetRoot(target), 'utf-8'),
|
||||
);
|
||||
const templateContent = await fs
|
||||
.readFile(paths.resolveTargetRoot(target), 'utf-8')
|
||||
.catch(error => {
|
||||
throw new ForwardedError(
|
||||
`Failed to load template definition from '${target}'`,
|
||||
error,
|
||||
);
|
||||
});
|
||||
const rawTemplate = parseYaml(templateContent);
|
||||
|
||||
const parsed = templateDefinitionSchema.safeParse(rawTemplate);
|
||||
if (!parsed.success) {
|
||||
@@ -105,17 +109,14 @@ export class NewTemplateLoader {
|
||||
);
|
||||
}
|
||||
|
||||
const template = parsed.data;
|
||||
const { template, ...templateData } = parsed.data;
|
||||
|
||||
const templatePath = paths.resolveTargetRoot(
|
||||
dirname(target),
|
||||
template.template,
|
||||
);
|
||||
const templatePath = resolvePath(dirname(target), template);
|
||||
if (!fs.existsSync(templatePath)) {
|
||||
throw new Error(
|
||||
`Your CLI template skeleton does not exist: ${templatePath}`,
|
||||
`Failed to load template contents from '${templatePath}'`,
|
||||
);
|
||||
}
|
||||
return { id, templatePath, ...template };
|
||||
return { id, templatePath, ...templateData };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { readCliConfig, verifyTemplate } from './templateSelector';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('verifyTemplate', () => {
|
||||
it('throws an error if template target is a remote URL', () => {
|
||||
expect(() => verifyTemplate({ id: '', target: 'http' })).toThrow(
|
||||
'Remote templates are not supported yet',
|
||||
);
|
||||
});
|
||||
|
||||
it('throws an error if template yaml file does not exist', () => {
|
||||
expect(() => verifyTemplate({ id: '', target: '/foo' })).toThrow(
|
||||
'Your CLI template does not exist: /foo',
|
||||
);
|
||||
});
|
||||
|
||||
it('throws an error if the skeleton of the template does not exist', () => {
|
||||
const mockDir = createMockDirectory({
|
||||
content: { 'template.yaml': 'template: "foo"' },
|
||||
});
|
||||
expect(() =>
|
||||
verifyTemplate({ id: '', target: mockDir.resolve('template.yaml') }),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
it('throws an error if template is missing a targetPath', () => {
|
||||
const mockDir = createMockDirectory({
|
||||
content: { 'template.yaml': 'template: "foo"', foo: 'bar' },
|
||||
});
|
||||
expect(() =>
|
||||
verifyTemplate({ id: '', target: mockDir.resolve('template.yaml') }),
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user