scaffolder-backend: refactor template action tests to avoid mock-fs

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-10-05 17:25:01 +02:00
parent f277d1b7b4
commit eb42e005cb
@@ -19,10 +19,8 @@ jest.mock('@backstage/plugin-scaffolder-node', () => {
return { ...actual, fetchContents: jest.fn() };
});
import os from 'os';
import { join as joinPath, sep as pathSep } from 'path';
import { join as joinPath, resolve as resolvePath, sep as pathSep } from 'path';
import fs from 'fs-extra';
import mockFs from 'mock-fs';
import {
getVoidLogger,
resolvePackagePath,
@@ -36,6 +34,7 @@ import {
ActionContext,
TemplateAction,
} from '@backstage/plugin-scaffolder-node';
import { createMockDirectory } from '@backstage/backend-test-utils';
type FetchTemplateInput = ReturnType<
typeof createFetchTemplateAction
@@ -43,16 +42,6 @@ type FetchTemplateInput = ReturnType<
? U
: never;
const realFiles = Object.fromEntries(
[
resolvePackagePath(
'@backstage/plugin-scaffolder-backend',
'assets',
'nunjucks.js.txt',
),
].map(k => [k, mockFs.load(k)]),
);
const aBinaryFile = fs.readFileSync(
resolvePackagePath(
'@backstage/plugin-scaffolder-backend',
@@ -67,14 +56,8 @@ const mockFetchContents = fetchContents as jest.MockedFunction<
describe('fetch:template', () => {
let action: TemplateAction<any>;
const workspacePath = os.tmpdir();
const createTemporaryDirectory: jest.MockedFunction<
ActionContext<FetchTemplateInput>['createTemporaryDirectory']
> = jest.fn(() =>
Promise.resolve(
joinPath(workspacePath, `${createTemporaryDirectory.mock.calls.length}`),
),
);
const mockDir = createMockDirectory();
const workspacePath = mockDir.resolve('workspace');
const logger = getVoidLogger();
@@ -95,24 +78,21 @@ describe('fetch:template', () => {
logStream: new PassThrough(),
logger,
workspacePath,
createTemporaryDirectory,
async createTemporaryDirectory() {
return fs.mkdtemp(mockDir.resolve('tmp-'));
},
});
beforeEach(() => {
mockFs({
...realFiles,
mockDir.setContent({
workspace: {},
});
action = createFetchTemplateAction({
reader: Symbol('UrlReader') as unknown as UrlReader,
integrations: Symbol('Integrations') as unknown as ScmIntegrations,
});
});
afterEach(() => {
mockFs.restore();
});
it(`returns a TemplateAction with the id 'fetch:template'`, () => {
expect(action.id).toEqual('fetch:template');
});
@@ -190,8 +170,7 @@ describe('fetch:template', () => {
});
mockFetchContents.mockImplementation(({ outputPath }) => {
mockFs({
...realFiles,
mockDir.setContent({
[outputPath]: {
'{% if values.showDummyFile %}dummy-file.txt{% else %}{% endif %}':
'dummy file',
@@ -282,13 +261,8 @@ describe('fetch:template', () => {
});
mockFetchContents.mockImplementation(({ outputPath }) => {
mockFs({
...realFiles,
mockDir.setContent({
[outputPath]: {
'an-executable.sh': mockFs.file({
content: '#!/usr/bin/env bash',
mode: parseInt('100755', 8),
}),
'empty-dir-${{ values.count }}': {},
'static.txt': 'static content',
'${{ values.name }}.txt': 'static content',
@@ -298,15 +272,27 @@ describe('fetch:template', () => {
},
'.${{ values.name }}': '${{ values.itemList | dump }}',
'a-binary-file.png': aBinaryFile,
symlink: mockFs.symlink({
path: 'a-binary-file.png',
}),
brokenSymlink: mockFs.symlink({
path: './not-a-real-file.txt',
}),
},
});
fs.writeFileSync(
resolvePath(outputPath, 'an-executable.sh'),
'#!/usr/bin/env bash',
{
encoding: 'utf-8',
mode: parseInt('100755', 8),
},
);
fs.symlinkSync(
'a-binary-file.png',
resolvePath(outputPath, 'symlink'),
);
fs.symlinkSync(
'./not-a-real-file.txt',
resolvePath(outputPath, 'brokenSymlink'),
);
return Promise.resolve();
});
@@ -378,7 +364,11 @@ describe('fetch:template', () => {
await expect(
fs.realpath(`${workspacePath}/target/symlink`),
).resolves.toBe(joinPath(workspacePath, 'target', 'a-binary-file.png'));
).resolves.toBe(
fs.realpathSync(
joinPath(workspacePath, 'target', 'a-binary-file.png'),
),
);
});
it('copies broken symlinks as-is without processing them', async () => {
@@ -408,8 +398,7 @@ describe('fetch:template', () => {
});
mockFetchContents.mockImplementation(({ outputPath }) => {
mockFs({
...realFiles,
mockDir.setContent({
[outputPath]: {
processed: {
'templated-content-${{ values.name }}.txt': '${{ values.count }}',
@@ -458,8 +447,7 @@ describe('fetch:template', () => {
});
mockFetchContents.mockImplementation(({ outputPath }) => {
mockFs({
...realFiles,
mockDir.setContent({
[outputPath]: {
processed: {
'templated-content-${{ values.name }}.txt': '${{ values.count }}',
@@ -509,8 +497,7 @@ describe('fetch:template', () => {
});
mockFetchContents.mockImplementation(({ outputPath }) => {
mockFs({
...realFiles,
mockDir.setContent({
[outputPath]: {
'{{ cookiecutter.name }}.txt': 'static content',
subdir: {
@@ -564,8 +551,7 @@ describe('fetch:template', () => {
});
mockFetchContents.mockImplementation(({ outputPath }) => {
mockFs({
...realFiles,
mockDir.setContent({
[outputPath]: {
'empty-dir-${{ values.count }}': {},
'static.txt': 'static content',
@@ -646,8 +632,7 @@ describe('fetch:template', () => {
});
mockFetchContents.mockImplementation(({ outputPath }) => {
mockFs({
...realFiles,
mockDir.setContent({
[outputPath]: {
'${{ values.name }}.njk': '${{ values.name }}: ${{ values.count }}',
'${{ values.name }}.txt.jinja2':
@@ -687,8 +672,7 @@ describe('fetch:template', () => {
});
mockFetchContents.mockImplementation(({ outputPath }) => {
mockFs({
...realFiles,
mockDir.setContent({
[joinPath(workspacePath, 'target')]: {
'static-content.txt': 'static-content',
},
@@ -703,10 +687,6 @@ describe('fetch:template', () => {
await action.handler(context);
});
afterEach(() => {
mockFs.restore();
});
it('overwrites existing file', async () => {
await expect(
fs.readFile(`${workspacePath}/target/static-content.txt`, 'utf-8'),
@@ -728,8 +708,7 @@ describe('fetch:template', () => {
});
mockFetchContents.mockImplementation(({ outputPath }) => {
mockFs({
...realFiles,
mockDir.setContent({
[joinPath(workspacePath, 'target')]: {
'static-content.txt': 'static-content',
},