Merge pull request #20404 from backstage/rugvip/scaffodler-no-mockfs
scaffolder-backend: refactor tests to avoid mock-fs
This commit is contained in:
@@ -452,16 +452,13 @@ describe('backend-plugin-manager', () => {
|
||||
findPaths(__dirname).resolveTargetRoot('package.json'),
|
||||
),
|
||||
'dynamic-plugins-root': {},
|
||||
'dynamic-plugins-root/a-dynamic-plugin': ctx =>
|
||||
ctx.symlink(otherMockDir.resolve('a-dynamic-plugin')),
|
||||
});
|
||||
otherMockDir.setContent({
|
||||
'a-dynamic-plugin': {},
|
||||
});
|
||||
|
||||
fs.symlinkSync(
|
||||
otherMockDir.resolve('a-dynamic-plugin'),
|
||||
mockDir.resolve('dynamic-plugins-root/a-dynamic-plugin'),
|
||||
);
|
||||
|
||||
const fromConfigSpier = jest.spyOn(PluginManager, 'fromConfig');
|
||||
const applyConfigSpier = jest
|
||||
.spyOn(PluginScanner.prototype as any, 'applyConfig')
|
||||
|
||||
@@ -19,10 +19,12 @@ import { JsonObject } from '@backstage/types';
|
||||
import { Logs, MockedLogger } from '../__testUtils__/testUtils';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import * as url from 'url';
|
||||
import { ScannedPluginPackage } from './types';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
import {
|
||||
MockDirectoryContent,
|
||||
createMockDirectory,
|
||||
} from '@backstage/backend-test-utils';
|
||||
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
@@ -233,8 +235,7 @@ Please add '${mockDir.resolve(
|
||||
type TestCase = {
|
||||
name: string;
|
||||
preferAlpha?: boolean;
|
||||
fileSystem?: any;
|
||||
symlinks?: { source: string; target: string }[];
|
||||
fileSystem?: MockDirectoryContent;
|
||||
expectedLogs?: Logs;
|
||||
expectedPluginPackages?: ScannedPluginPackage[];
|
||||
expectedError?: string;
|
||||
@@ -286,7 +287,12 @@ Please add '${mockDir.resolve(
|
||||
name: 'backend plugin found in symlink',
|
||||
fileSystem: {
|
||||
backstageRoot: {
|
||||
'dist-dynamic': {},
|
||||
'dist-dynamic': {
|
||||
'test-backend-plugin': ctx =>
|
||||
ctx.symlink(
|
||||
mockDir.resolve('somewhere-else/test-backend-plugin-target'),
|
||||
),
|
||||
},
|
||||
},
|
||||
'somewhere-else': {
|
||||
'test-backend-plugin-target': {
|
||||
@@ -299,16 +305,6 @@ Please add '${mockDir.resolve(
|
||||
},
|
||||
},
|
||||
},
|
||||
symlinks: [
|
||||
{
|
||||
source: mockDir.resolve(
|
||||
'backstageRoot/dist-dynamic/test-backend-plugin',
|
||||
),
|
||||
target: mockDir.resolve(
|
||||
'somewhere-else/test-backend-plugin-target',
|
||||
),
|
||||
},
|
||||
],
|
||||
expectedPluginPackages: [
|
||||
{
|
||||
location: url.pathToFileURL(
|
||||
@@ -347,22 +343,17 @@ Please add '${mockDir.resolve(
|
||||
name: 'ignored folder child symlink: target is not a directory',
|
||||
fileSystem: {
|
||||
backstageRoot: {
|
||||
'dist-dynamic': {},
|
||||
'dist-dynamic': {
|
||||
'test-backend-plugin': ctx =>
|
||||
ctx.symlink(
|
||||
mockDir.resolve('somewhere-else/test-backend-plugin-target'),
|
||||
),
|
||||
},
|
||||
},
|
||||
'somewhere-else': {
|
||||
'test-backend-plugin-target': '',
|
||||
},
|
||||
},
|
||||
symlinks: [
|
||||
{
|
||||
source: mockDir.resolve(
|
||||
'backstageRoot/dist-dynamic/test-backend-plugin',
|
||||
),
|
||||
target: mockDir.resolve(
|
||||
'somewhere-else/test-backend-plugin-target',
|
||||
),
|
||||
},
|
||||
],
|
||||
expectedPluginPackages: [],
|
||||
expectedLogs: {
|
||||
infos: [
|
||||
@@ -637,11 +628,6 @@ Please add '${mockDir.resolve(
|
||||
if (tc.fileSystem) {
|
||||
mockDir.setContent(tc.fileSystem);
|
||||
}
|
||||
if (tc.symlinks) {
|
||||
for (const { source, target } of tc.symlinks) {
|
||||
fs.symlinkSync(target, source);
|
||||
}
|
||||
}
|
||||
if (tc.expectedError) {
|
||||
/* eslint-disable-next-line jest/no-conditional-expect */
|
||||
expect(toTest).toThrow(tc.expectedError);
|
||||
|
||||
@@ -54,9 +54,24 @@ export interface MockDirectory {
|
||||
|
||||
// @public
|
||||
export type MockDirectoryContent = {
|
||||
[name in string]: MockDirectoryContent | string | Buffer;
|
||||
[name in string]:
|
||||
| MockDirectoryContent
|
||||
| string
|
||||
| Buffer
|
||||
| MockDirectoryContentCallback;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type MockDirectoryContentCallback = (
|
||||
ctx: MockDirectoryContentCallbackContext,
|
||||
) => void;
|
||||
|
||||
// @public
|
||||
export interface MockDirectoryContentCallbackContext {
|
||||
path: string;
|
||||
symlink(target: string): void;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface MockDirectoryContentOptions {
|
||||
path?: string;
|
||||
|
||||
@@ -119,6 +119,20 @@ describe('createMockDirectory', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to use callback for more detailed file system operations', () => {
|
||||
mockDir.setContent({
|
||||
'a.txt': 'a',
|
||||
'b.txt': ctx => ctx.symlink('./a.txt'),
|
||||
'c.txt': ctx => fs.copyFileSync(mockDir.resolve('a.txt'), ctx.path),
|
||||
});
|
||||
|
||||
expect(mockDir.content()).toEqual({
|
||||
'a.txt': 'a',
|
||||
'b.txt': 'a',
|
||||
'c.txt': 'a',
|
||||
});
|
||||
});
|
||||
|
||||
it('should read content from sub dirs', () => {
|
||||
mockDir.setContent({
|
||||
'a.txt': 'a',
|
||||
|
||||
@@ -30,6 +30,28 @@ import {
|
||||
|
||||
const tmpdirMarker = Symbol('os-tmpdir-mock');
|
||||
|
||||
/**
|
||||
* A context that allows for more advanced file system operations when writing mock directory content.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface MockDirectoryContentCallbackContext {
|
||||
/** Absolute path to the location of this piece of content on the filesystem */
|
||||
path: string;
|
||||
|
||||
/** Creates a symbolic link at the current location */
|
||||
symlink(target: string): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback that allows for more advanced file system operations when writing mock directory content.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type MockDirectoryContentCallback = (
|
||||
ctx: MockDirectoryContentCallbackContext,
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* The content of a mock directory represented by a nested object structure.
|
||||
*
|
||||
@@ -54,7 +76,11 @@ const tmpdirMarker = Symbol('os-tmpdir-mock');
|
||||
* @public
|
||||
*/
|
||||
export type MockDirectoryContent = {
|
||||
[name in string]: MockDirectoryContent | string | Buffer;
|
||||
[name in string]:
|
||||
| MockDirectoryContent
|
||||
| string
|
||||
| Buffer
|
||||
| MockDirectoryContentCallback;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -178,6 +204,11 @@ type MockEntry =
|
||||
| {
|
||||
type: 'dir';
|
||||
path: string;
|
||||
}
|
||||
| {
|
||||
type: 'callback';
|
||||
path: string;
|
||||
callback: MockDirectoryContentCallback;
|
||||
};
|
||||
|
||||
/** @internal */
|
||||
@@ -214,10 +245,18 @@ class MockDirectoryImpl {
|
||||
}
|
||||
|
||||
if (entry.type === 'dir') {
|
||||
fs.ensureDirSync(fullPath, { mode: 0o777 });
|
||||
fs.ensureDirSync(fullPath);
|
||||
} else if (entry.type === 'file') {
|
||||
fs.ensureDirSync(dirname(fullPath), { mode: 0o777 });
|
||||
fs.writeFileSync(fullPath, entry.content, { mode: 0o666 });
|
||||
fs.ensureDirSync(dirname(fullPath));
|
||||
fs.writeFileSync(fullPath, entry.content);
|
||||
} else if (entry.type === 'callback') {
|
||||
fs.ensureDirSync(dirname(fullPath));
|
||||
entry.callback({
|
||||
path: fullPath,
|
||||
symlink(target: string) {
|
||||
fs.symlinkSync(target, fullPath);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,6 +326,8 @@ class MockDirectoryImpl {
|
||||
});
|
||||
} else if (node instanceof Buffer) {
|
||||
entries.push({ type: 'file', path, content: node });
|
||||
} else if (typeof node === 'function') {
|
||||
entries.push({ type: 'callback', path, callback: node });
|
||||
} else {
|
||||
entries.push({ type: 'dir', path });
|
||||
for (const [name, child] of Object.entries(node)) {
|
||||
|
||||
@@ -20,4 +20,6 @@ export {
|
||||
type MockDirectoryOptions,
|
||||
type MockDirectoryContent,
|
||||
type MockDirectoryContentOptions,
|
||||
type MockDirectoryContentCallback,
|
||||
type MockDirectoryContentCallbackContext,
|
||||
} from './MockDirectory';
|
||||
|
||||
@@ -107,13 +107,11 @@
|
||||
"@types/fs-extra": "^9.0.1",
|
||||
"@types/git-url-parse": "^9.0.0",
|
||||
"@types/libsodium-wrappers": "^0.7.10",
|
||||
"@types/mock-fs": "^4.13.0",
|
||||
"@types/nunjucks": "^3.1.4",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"@types/zen-observable": "^0.8.0",
|
||||
"esbuild": "^0.19.0",
|
||||
"jest-when": "^3.1.0",
|
||||
"mock-fs": "^5.2.0",
|
||||
"msw": "^1.0.0",
|
||||
"supertest": "^6.1.3",
|
||||
"wait-for-expect": "^3.0.2",
|
||||
|
||||
@@ -14,29 +14,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import mockFs from 'mock-fs';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
import { deserializeDirectoryContents } from './deserializeDirectoryContents';
|
||||
import { serializeDirectoryContents } from './serializeDirectoryContents';
|
||||
|
||||
describe('deserializeDirectoryContents', () => {
|
||||
beforeEach(() => {
|
||||
mockFs({
|
||||
root: {},
|
||||
});
|
||||
});
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
beforeEach(() => {
|
||||
mockDir.clear();
|
||||
});
|
||||
|
||||
it('deserializes contents into a directory', async () => {
|
||||
await deserializeDirectoryContents('root', [
|
||||
await deserializeDirectoryContents(mockDir.path, [
|
||||
{
|
||||
path: 'a.txt',
|
||||
content: Buffer.from('a', 'utf8'),
|
||||
},
|
||||
]);
|
||||
await expect(serializeDirectoryContents('root')).resolves.toEqual([
|
||||
await expect(serializeDirectoryContents(mockDir.path)).resolves.toEqual([
|
||||
{
|
||||
path: 'a.txt',
|
||||
content: Buffer.from('a', 'utf8'),
|
||||
@@ -47,7 +43,7 @@ describe('deserializeDirectoryContents', () => {
|
||||
});
|
||||
|
||||
it('deserializes contents into a deep directory structure', async () => {
|
||||
await deserializeDirectoryContents('root', [
|
||||
await deserializeDirectoryContents(mockDir.path, [
|
||||
{
|
||||
path: 'a.txt',
|
||||
content: Buffer.from('a', 'utf8'),
|
||||
@@ -61,7 +57,7 @@ describe('deserializeDirectoryContents', () => {
|
||||
content: Buffer.from('c', 'utf8'),
|
||||
},
|
||||
]);
|
||||
await expect(serializeDirectoryContents('root')).resolves.toEqual([
|
||||
await expect(serializeDirectoryContents(mockDir.path)).resolves.toEqual([
|
||||
{
|
||||
path: 'a.txt',
|
||||
content: Buffer.from('a', 'utf8'),
|
||||
|
||||
@@ -14,13 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
import { serializeDirectoryContents } from './serializeDirectoryContents';
|
||||
import mockFs from 'mock-fs';
|
||||
|
||||
describe('serializeDirectoryContents', () => {
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
it('should list files in this directory', async () => {
|
||||
await expect(serializeDirectoryContents(__dirname)).resolves.toEqual(
|
||||
@@ -54,25 +52,23 @@ describe('serializeDirectoryContents', () => {
|
||||
});
|
||||
|
||||
it('should list files in a mock directory', async () => {
|
||||
mockFs({
|
||||
root: {
|
||||
'a.txt': 'a',
|
||||
b: {
|
||||
'b1.txt': 'b1',
|
||||
'b2.txt': 'b2',
|
||||
},
|
||||
c: {
|
||||
c1: {
|
||||
'c11.txt': 'c11',
|
||||
c11: {
|
||||
'c111.txt': 'c111',
|
||||
},
|
||||
mockDir.setContent({
|
||||
'a.txt': 'a',
|
||||
b: {
|
||||
'b1.txt': 'b1',
|
||||
'b2.txt': 'b2',
|
||||
},
|
||||
c: {
|
||||
c1: {
|
||||
'c11.txt': 'c11',
|
||||
c11: {
|
||||
'c111.txt': 'c111',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await expect(serializeDirectoryContents('root')).resolves.toEqual([
|
||||
await expect(serializeDirectoryContents(mockDir.path)).resolves.toEqual([
|
||||
{
|
||||
path: 'a.txt',
|
||||
executable: false,
|
||||
@@ -107,16 +103,12 @@ describe('serializeDirectoryContents', () => {
|
||||
});
|
||||
|
||||
it('should ignore symlinked files', async () => {
|
||||
mockFs({
|
||||
root: {
|
||||
'a.txt': 'some text',
|
||||
sym: mockFs.symlink({
|
||||
path: './a.txt',
|
||||
}),
|
||||
},
|
||||
mockDir.setContent({
|
||||
'a.txt': 'some text',
|
||||
sym: ctx => ctx.symlink('./a.txt'),
|
||||
});
|
||||
|
||||
await expect(serializeDirectoryContents('root')).resolves.toEqual([
|
||||
await expect(serializeDirectoryContents(mockDir.path)).resolves.toEqual([
|
||||
{
|
||||
path: 'a.txt',
|
||||
executable: false,
|
||||
@@ -127,18 +119,14 @@ describe('serializeDirectoryContents', () => {
|
||||
});
|
||||
|
||||
it('should pick up broken symlinks', async () => {
|
||||
mockFs({
|
||||
root: {
|
||||
'b.txt': mockFs.symlink({
|
||||
path: './a.txt',
|
||||
}),
|
||||
},
|
||||
mockDir.setContent({
|
||||
'b.txt': ctx => ctx.symlink('./a.txt'),
|
||||
});
|
||||
|
||||
await expect(serializeDirectoryContents('root')).resolves.toEqual([
|
||||
await expect(serializeDirectoryContents(mockDir.path)).resolves.toEqual([
|
||||
{
|
||||
path: 'b.txt',
|
||||
executable: false,
|
||||
executable: true,
|
||||
symlink: true,
|
||||
content: Buffer.from('./a.txt', 'utf8'),
|
||||
},
|
||||
@@ -146,19 +134,15 @@ describe('serializeDirectoryContents', () => {
|
||||
});
|
||||
|
||||
it('should ignore symlinked folder files', async () => {
|
||||
mockFs({
|
||||
root: {
|
||||
'a.txt': 'some text',
|
||||
linkme: {
|
||||
'b.txt': 'lols',
|
||||
},
|
||||
sym: mockFs.symlink({
|
||||
path: './linkme',
|
||||
}),
|
||||
mockDir.setContent({
|
||||
'a.txt': 'some text',
|
||||
linkme: {
|
||||
'b.txt': 'lols',
|
||||
},
|
||||
sym: ctx => ctx.symlink('./linkme'),
|
||||
});
|
||||
|
||||
await expect(serializeDirectoryContents('root')).resolves.toEqual([
|
||||
await expect(serializeDirectoryContents(mockDir.path)).resolves.toEqual([
|
||||
{
|
||||
path: 'a.txt',
|
||||
executable: false,
|
||||
@@ -175,16 +159,14 @@ describe('serializeDirectoryContents', () => {
|
||||
});
|
||||
|
||||
it('should ignore gitignored files', async () => {
|
||||
mockFs({
|
||||
root: {
|
||||
'.gitignore': '*.txt',
|
||||
'a.txt': 'a',
|
||||
'a.log': 'a',
|
||||
},
|
||||
mockDir.setContent({
|
||||
'.gitignore': '*.txt',
|
||||
'a.txt': 'a',
|
||||
'a.log': 'a',
|
||||
});
|
||||
|
||||
await expect(
|
||||
serializeDirectoryContents('root', {
|
||||
serializeDirectoryContents(mockDir.path, {
|
||||
gitignore: true,
|
||||
}),
|
||||
).resolves.toEqual([
|
||||
@@ -204,26 +186,24 @@ describe('serializeDirectoryContents', () => {
|
||||
});
|
||||
|
||||
it('should use custom glob patterns', async () => {
|
||||
mockFs({
|
||||
root: {
|
||||
'.a': 'a',
|
||||
'a.log': 'a',
|
||||
'a.txt': 'a',
|
||||
b: {
|
||||
'.b': 'b',
|
||||
'b.log': 'b',
|
||||
'b.txt': 'b',
|
||||
},
|
||||
c: {
|
||||
'.c': 'c',
|
||||
'c.log': 'c',
|
||||
'c.txt': 'c',
|
||||
},
|
||||
mockDir.setContent({
|
||||
'.a': 'a',
|
||||
'a.log': 'a',
|
||||
'a.txt': 'a',
|
||||
b: {
|
||||
'.b': 'b',
|
||||
'b.log': 'b',
|
||||
'b.txt': 'b',
|
||||
},
|
||||
c: {
|
||||
'.c': 'c',
|
||||
'c.log': 'c',
|
||||
'c.txt': 'c',
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
serializeDirectoryContents('root', {
|
||||
serializeDirectoryContents(mockDir.path, {
|
||||
gitignore: true,
|
||||
globPatterns: ['**/*.txt', '*/.?', '*/*.log', '!c/**/.*', '!b/*.log'],
|
||||
}).then(files => files.sort((a, b) => a.path.localeCompare(b.path))),
|
||||
|
||||
+9
-12
@@ -15,44 +15,41 @@
|
||||
*/
|
||||
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import mockFs from 'mock-fs';
|
||||
import os from 'os';
|
||||
import { Writable } from 'stream';
|
||||
import { createDebugLogAction } from './log';
|
||||
import { join } from 'path';
|
||||
import yaml from 'yaml';
|
||||
import { examples } from './log.examples';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('debug:log examples', () => {
|
||||
const logStream = {
|
||||
write: jest.fn(),
|
||||
} as jest.Mocked<Partial<Writable>> as jest.Mocked<Writable>;
|
||||
|
||||
const mockTmpDir = os.tmpdir();
|
||||
const mockDir = createMockDirectory();
|
||||
const workspacePath = mockDir.resolve('workspace');
|
||||
|
||||
const mockContext = {
|
||||
input: {},
|
||||
baseUrl: 'somebase',
|
||||
workspacePath: mockTmpDir,
|
||||
workspacePath,
|
||||
logger: getVoidLogger(),
|
||||
logStream,
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn().mockResolvedValue(mockTmpDir),
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
|
||||
const action = createDebugLogAction();
|
||||
|
||||
beforeEach(() => {
|
||||
mockFs({
|
||||
[`${mockContext.workspacePath}/README.md`]: '',
|
||||
[`${mockContext.workspacePath}/a-directory/index.md`]: '',
|
||||
mockDir.setContent({
|
||||
[`${workspacePath}/README.md`]: '',
|
||||
[`${workspacePath}/a-directory/index.md`]: '',
|
||||
});
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should log message', async () => {
|
||||
const context = {
|
||||
...mockContext,
|
||||
|
||||
@@ -15,43 +15,40 @@
|
||||
*/
|
||||
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import mockFs from 'mock-fs';
|
||||
import os from 'os';
|
||||
import { Writable } from 'stream';
|
||||
import { createDebugLogAction } from './log';
|
||||
import { join } from 'path';
|
||||
import yaml from 'yaml';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('debug:log', () => {
|
||||
const logStream = {
|
||||
write: jest.fn(),
|
||||
} as jest.Mocked<Partial<Writable>> as jest.Mocked<Writable>;
|
||||
|
||||
const mockTmpDir = os.tmpdir();
|
||||
const mockDir = createMockDirectory();
|
||||
const workspacePath = mockDir.resolve('workspace');
|
||||
|
||||
const mockContext = {
|
||||
input: {},
|
||||
baseUrl: 'somebase',
|
||||
workspacePath: mockTmpDir,
|
||||
workspacePath,
|
||||
logger: getVoidLogger(),
|
||||
logStream,
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn().mockResolvedValue(mockTmpDir),
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
|
||||
const action = createDebugLogAction();
|
||||
|
||||
beforeEach(() => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[`${mockContext.workspacePath}/README.md`]: '',
|
||||
[`${mockContext.workspacePath}/a-directory/index.md`]: '',
|
||||
});
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should do nothing', async () => {
|
||||
await action.handler(mockContext);
|
||||
|
||||
|
||||
+6
-9
@@ -15,12 +15,11 @@
|
||||
*/
|
||||
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import mockFs from 'mock-fs';
|
||||
import { createWaitAction } from './wait';
|
||||
import { Writable } from 'stream';
|
||||
import os from 'os';
|
||||
import { examples } from './wait.examples';
|
||||
import yaml from 'yaml';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('debug:wait examples', () => {
|
||||
const action = createWaitAction();
|
||||
@@ -29,25 +28,23 @@ describe('debug:wait examples', () => {
|
||||
write: jest.fn(),
|
||||
} as jest.Mocked<Partial<Writable>> as jest.Mocked<Writable>;
|
||||
|
||||
const mockTmpDir = os.tmpdir();
|
||||
const mockDir = createMockDirectory();
|
||||
const workspacePath = mockDir.resolve('workspace');
|
||||
|
||||
const mockContext = {
|
||||
input: {},
|
||||
baseUrl: 'somebase',
|
||||
workspacePath: mockTmpDir,
|
||||
workspacePath,
|
||||
logger: getVoidLogger(),
|
||||
logStream,
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn().mockResolvedValue(mockTmpDir),
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should wait for specified period of seconds', async () => {
|
||||
const context = {
|
||||
...mockContext,
|
||||
|
||||
@@ -15,10 +15,9 @@
|
||||
*/
|
||||
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import mockFs from 'mock-fs';
|
||||
import { createWaitAction } from './wait';
|
||||
import { Writable } from 'stream';
|
||||
import os from 'os';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('debug:wait', () => {
|
||||
const action = createWaitAction();
|
||||
@@ -27,25 +26,23 @@ describe('debug:wait', () => {
|
||||
write: jest.fn(),
|
||||
} as jest.Mocked<Partial<Writable>> as jest.Mocked<Writable>;
|
||||
|
||||
const mockTmpDir = os.tmpdir();
|
||||
const mockDir = createMockDirectory();
|
||||
const workspacePath = mockDir.resolve('workspace');
|
||||
|
||||
const mockContext = {
|
||||
input: {},
|
||||
baseUrl: 'somebase',
|
||||
workspacePath: mockTmpDir,
|
||||
workspacePath,
|
||||
logger: getVoidLogger(),
|
||||
logStream,
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn().mockResolvedValue(mockTmpDir),
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should wait for specified period of time', async () => {
|
||||
const context = {
|
||||
...mockContext,
|
||||
|
||||
+21
-42
@@ -14,10 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import os from 'os';
|
||||
import { join as joinPath, sep as pathSep } from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import {
|
||||
getVoidLogger,
|
||||
resolvePackagePath,
|
||||
@@ -33,6 +31,7 @@ import {
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
import { examples } from './template.examples';
|
||||
import yaml from 'yaml';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
jest.mock('@backstage/plugin-scaffolder-node', () => ({
|
||||
...jest.requireActual('@backstage/plugin-scaffolder-node'),
|
||||
@@ -45,16 +44,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',
|
||||
@@ -69,14 +58,8 @@ const mockFetchContents = fetchContents as jest.MockedFunction<
|
||||
describe('fetch:template examples', () => {
|
||||
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();
|
||||
|
||||
@@ -90,24 +73,20 @@ describe('fetch:template examples', () => {
|
||||
logStream: new PassThrough(),
|
||||
logger,
|
||||
workspacePath,
|
||||
createTemporaryDirectory,
|
||||
|
||||
async createTemporaryDirectory() {
|
||||
return fs.mkdtemp(mockDir.resolve('tmp-'));
|
||||
},
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mockFs({
|
||||
...realFiles,
|
||||
});
|
||||
|
||||
mockDir.clear();
|
||||
action = createFetchTemplateAction({
|
||||
reader: Symbol('UrlReader') as unknown as UrlReader,
|
||||
integrations: Symbol('Integrations') as unknown as ScmIntegrations,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
describe('handler', () => {
|
||||
describe('with valid input', () => {
|
||||
let context: ActionContext<FetchTemplateInput>;
|
||||
@@ -116,13 +95,13 @@ describe('fetch:template examples', () => {
|
||||
context = mockContext(yaml.parse(examples[0].example).steps[0].input);
|
||||
|
||||
mockFetchContents.mockImplementation(({ outputPath }) => {
|
||||
mockFs({
|
||||
...realFiles,
|
||||
mockDir.setContent({
|
||||
[outputPath]: {
|
||||
'an-executable.sh': mockFs.file({
|
||||
content: '#!/usr/bin/env bash',
|
||||
mode: parseInt('100755', 8),
|
||||
}),
|
||||
'an-executable.sh': ctx =>
|
||||
fs.writeFileSync(ctx.path, '#!/usr/bin/env bash', {
|
||||
encoding: 'utf8',
|
||||
mode: parseInt('100755', 8),
|
||||
}),
|
||||
'empty-dir-${{ values.count }}': {},
|
||||
'static.txt': 'static content',
|
||||
'${{ values.name }}.txt': 'static content',
|
||||
@@ -132,12 +111,8 @@ describe('fetch:template examples', () => {
|
||||
},
|
||||
'.${{ 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',
|
||||
}),
|
||||
symlink: ctx => ctx.symlink('a-binary-file.png'),
|
||||
brokenSymlink: ctx => ctx.symlink('./not-a-real-file.txt'),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -212,7 +187,11 @@ describe('fetch:template examples', () => {
|
||||
|
||||
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 () => {
|
||||
|
||||
@@ -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 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,12 +272,13 @@ 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',
|
||||
}),
|
||||
'an-executable.sh': ctx =>
|
||||
fs.writeFileSync(ctx.path, '#!/usr/bin/env bash', {
|
||||
encoding: 'utf-8',
|
||||
mode: parseInt('100755', 8),
|
||||
}),
|
||||
symlink: ctx => ctx.symlink('a-binary-file.png'),
|
||||
brokenSymlink: ctx => ctx.symlink('./not-a-real-file.txt'),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -378,7 +353,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 +387,7 @@ describe('fetch:template', () => {
|
||||
});
|
||||
|
||||
mockFetchContents.mockImplementation(({ outputPath }) => {
|
||||
mockFs({
|
||||
...realFiles,
|
||||
mockDir.setContent({
|
||||
[outputPath]: {
|
||||
processed: {
|
||||
'templated-content-${{ values.name }}.txt': '${{ values.count }}',
|
||||
@@ -458,8 +436,7 @@ describe('fetch:template', () => {
|
||||
});
|
||||
|
||||
mockFetchContents.mockImplementation(({ outputPath }) => {
|
||||
mockFs({
|
||||
...realFiles,
|
||||
mockDir.setContent({
|
||||
[outputPath]: {
|
||||
processed: {
|
||||
'templated-content-${{ values.name }}.txt': '${{ values.count }}',
|
||||
@@ -509,8 +486,7 @@ describe('fetch:template', () => {
|
||||
});
|
||||
|
||||
mockFetchContents.mockImplementation(({ outputPath }) => {
|
||||
mockFs({
|
||||
...realFiles,
|
||||
mockDir.setContent({
|
||||
[outputPath]: {
|
||||
'{{ cookiecutter.name }}.txt': 'static content',
|
||||
subdir: {
|
||||
@@ -564,8 +540,7 @@ describe('fetch:template', () => {
|
||||
});
|
||||
|
||||
mockFetchContents.mockImplementation(({ outputPath }) => {
|
||||
mockFs({
|
||||
...realFiles,
|
||||
mockDir.setContent({
|
||||
[outputPath]: {
|
||||
'empty-dir-${{ values.count }}': {},
|
||||
'static.txt': 'static content',
|
||||
@@ -646,8 +621,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 +661,7 @@ describe('fetch:template', () => {
|
||||
});
|
||||
|
||||
mockFetchContents.mockImplementation(({ outputPath }) => {
|
||||
mockFs({
|
||||
...realFiles,
|
||||
mockDir.setContent({
|
||||
[joinPath(workspacePath, 'target')]: {
|
||||
'static-content.txt': 'static-content',
|
||||
},
|
||||
@@ -703,10 +676,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 +697,7 @@ describe('fetch:template', () => {
|
||||
});
|
||||
|
||||
mockFetchContents.mockImplementation(({ outputPath }) => {
|
||||
mockFs({
|
||||
...realFiles,
|
||||
mockDir.setContent({
|
||||
[joinPath(workspacePath, 'target')]: {
|
||||
'static-content.txt': 'static-content',
|
||||
},
|
||||
|
||||
+5
-10
@@ -18,18 +18,17 @@ import { createFilesystemDeleteAction } from './delete';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { PassThrough } from 'stream';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import * as os from 'os';
|
||||
import mockFs from 'mock-fs';
|
||||
import fs from 'fs-extra';
|
||||
import yaml from 'yaml';
|
||||
import { examples } from './delete.examples';
|
||||
|
||||
const root = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir';
|
||||
const workspacePath = resolvePath(root, 'my-workspace');
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('fs:delete examples', () => {
|
||||
const action = createFilesystemDeleteAction();
|
||||
|
||||
const mockDir = createMockDirectory();
|
||||
const workspacePath = resolvePath(mockDir.path, 'workspace');
|
||||
|
||||
const files: string[] = yaml.parse(examples[0].example).steps[0].input.files;
|
||||
|
||||
const mockContext = {
|
||||
@@ -46,7 +45,7 @@ describe('fs:delete examples', () => {
|
||||
beforeEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
[files[0]]: 'hello',
|
||||
[files[1]]: 'world',
|
||||
@@ -57,10 +56,6 @@ describe('fs:delete examples', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should call fs.rm with the correct values', async () => {
|
||||
files.forEach(file => {
|
||||
const filePath = resolvePath(workspacePath, file);
|
||||
|
||||
+5
-10
@@ -14,20 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as os from 'os';
|
||||
import mockFs from 'mock-fs';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { createFilesystemDeleteAction } from './delete';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { PassThrough } from 'stream';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
const root = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir';
|
||||
const workspacePath = resolvePath(root, 'my-workspace');
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('fs:delete', () => {
|
||||
const action = createFilesystemDeleteAction();
|
||||
|
||||
const mockDir = createMockDirectory();
|
||||
const workspacePath = resolvePath(mockDir.path, 'workspace');
|
||||
|
||||
const mockContext = {
|
||||
input: {
|
||||
files: ['unit-test-a.js', 'unit-test-b.js'],
|
||||
@@ -42,7 +41,7 @@ describe('fs:delete', () => {
|
||||
beforeEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
'unit-test-a.js': 'hello',
|
||||
'unit-test-b.js': 'world',
|
||||
@@ -53,10 +52,6 @@ describe('fs:delete', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should throw an error when files is not an array', async () => {
|
||||
await expect(
|
||||
action.handler({
|
||||
|
||||
+5
-10
@@ -14,8 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as os from 'os';
|
||||
import mockFs from 'mock-fs';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { createFilesystemRenameAction } from './rename';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
@@ -23,15 +21,16 @@ import { PassThrough } from 'stream';
|
||||
import fs from 'fs-extra';
|
||||
import yaml from 'yaml';
|
||||
import { examples } from './rename.examples';
|
||||
|
||||
const root = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir';
|
||||
const workspacePath = resolvePath(root, 'my-workspace');
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('fs:rename examples', () => {
|
||||
const action = createFilesystemRenameAction();
|
||||
const files: { from: string; to: string }[] = yaml.parse(examples[0].example)
|
||||
.steps[0].input.files;
|
||||
|
||||
const mockDir = createMockDirectory();
|
||||
const workspacePath = resolvePath(mockDir.path, 'workspace');
|
||||
|
||||
const mockContext = {
|
||||
input: {
|
||||
files: files,
|
||||
@@ -46,7 +45,7 @@ describe('fs:rename examples', () => {
|
||||
beforeEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
[files[0].from]: 'hello',
|
||||
[files[1].from]: 'world',
|
||||
@@ -59,10 +58,6 @@ describe('fs:rename examples', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should call fs.move with the correct values', async () => {
|
||||
mockContext.input.files.forEach(file => {
|
||||
const filePath = resolvePath(workspacePath, file.from);
|
||||
|
||||
+5
-10
@@ -14,20 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as os from 'os';
|
||||
import mockFs from 'mock-fs';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { createFilesystemRenameAction } from './rename';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { PassThrough } from 'stream';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
const root = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir';
|
||||
const workspacePath = resolvePath(root, 'my-workspace');
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('fs:rename', () => {
|
||||
const action = createFilesystemRenameAction();
|
||||
|
||||
const mockDir = createMockDirectory();
|
||||
const workspacePath = resolvePath(mockDir.path, 'workspace');
|
||||
|
||||
const mockInputFiles = [
|
||||
{
|
||||
from: 'unit-test-a.js',
|
||||
@@ -56,7 +55,7 @@ describe('fs:rename', () => {
|
||||
beforeEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
'unit-test-a.js': 'hello',
|
||||
'unit-test-b.js': 'world',
|
||||
@@ -68,10 +67,6 @@ describe('fs:rename', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should throw an error when files is not an array', async () => {
|
||||
await expect(
|
||||
action.handler({
|
||||
|
||||
+28
-28
@@ -24,21 +24,17 @@ import {
|
||||
ActionContext,
|
||||
TemplateAction,
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
import mockFs from 'mock-fs';
|
||||
import os from 'os';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import { Writable } from 'stream';
|
||||
import {
|
||||
createPublishGithubPullRequestAction,
|
||||
OctokitWithPullRequestPluginClient,
|
||||
} from './githubPullRequest';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
// Make sure root logger is initialized ahead of FS mock
|
||||
createRootLogger();
|
||||
|
||||
const root = os.platform() === 'win32' ? 'C:\\root' : '/root';
|
||||
const workspacePath = resolvePath(root, 'my-workspace');
|
||||
|
||||
type GithubPullRequestActionInput = ReturnType<
|
||||
typeof createPublishGithubPullRequestAction
|
||||
> extends TemplateAction<infer U>
|
||||
@@ -54,7 +50,12 @@ describe('createPublishGithubPullRequestAction', () => {
|
||||
};
|
||||
};
|
||||
|
||||
const mockDir = createMockDirectory();
|
||||
const workspacePath = mockDir.resolve('workspace');
|
||||
|
||||
beforeEach(() => {
|
||||
mockDir.clear();
|
||||
|
||||
const integrations = ScmIntegrations.fromConfig(new ConfigReader({}));
|
||||
fakeClient = {
|
||||
createPullRequest: jest.fn(async (_: any) => {
|
||||
@@ -92,7 +93,6 @@ describe('createPublishGithubPullRequestAction', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
@@ -132,7 +132,7 @@ describe('createPublishGithubPullRequestAction', () => {
|
||||
draft: true,
|
||||
};
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: { 'file.txt': 'Hello there!' },
|
||||
});
|
||||
|
||||
@@ -197,7 +197,7 @@ describe('createPublishGithubPullRequestAction', () => {
|
||||
draft: true,
|
||||
};
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: { 'file.txt': 'Hello there!' },
|
||||
});
|
||||
|
||||
@@ -261,7 +261,7 @@ describe('createPublishGithubPullRequestAction', () => {
|
||||
sourcePath: 'source',
|
||||
};
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -323,7 +323,7 @@ describe('createPublishGithubPullRequestAction', () => {
|
||||
description: 'This PR is really good',
|
||||
};
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: { 'file.txt': 'Hello there!' },
|
||||
});
|
||||
|
||||
@@ -385,7 +385,7 @@ describe('createPublishGithubPullRequestAction', () => {
|
||||
teamReviewers: ['team-foo'],
|
||||
};
|
||||
|
||||
mockFs({ [workspacePath]: {} });
|
||||
mockDir.setContent({ [workspacePath]: {} });
|
||||
|
||||
ctx = {
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
@@ -437,7 +437,7 @@ describe('createPublishGithubPullRequestAction', () => {
|
||||
description: 'This PR is really good',
|
||||
};
|
||||
|
||||
mockFs({ [workspacePath]: {} });
|
||||
mockDir.setContent({ [workspacePath]: {} });
|
||||
|
||||
ctx = {
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
@@ -469,11 +469,9 @@ describe('createPublishGithubPullRequestAction', () => {
|
||||
description: 'This PR is really good',
|
||||
};
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
Makefile: mockFs.symlink({
|
||||
path: '../../nothing/yet',
|
||||
}),
|
||||
Makefile: c => c.symlink('../../nothing/yet'),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -523,12 +521,13 @@ describe('createPublishGithubPullRequestAction', () => {
|
||||
description: 'This PR is really good',
|
||||
};
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
'hello.sh': mockFs.file({
|
||||
content: 'echo Hello there!',
|
||||
mode: 0o100755,
|
||||
}),
|
||||
'hello.sh': c =>
|
||||
fs.writeFileSync(c.path, 'echo Hello there!', {
|
||||
encoding: 'utf8',
|
||||
mode: 0o100755,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -588,12 +587,13 @@ describe('createPublishGithubPullRequestAction', () => {
|
||||
description: 'This PR is really good',
|
||||
};
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
'hello.sh': mockFs.file({
|
||||
content: 'echo Hello there!',
|
||||
mode: 0o100775,
|
||||
}),
|
||||
'hello.sh': c =>
|
||||
fs.writeFileSync(c.path, 'echo Hello there!', {
|
||||
encoding: 'utf8',
|
||||
mode: 0o100775,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -654,7 +654,7 @@ describe('createPublishGithubPullRequestAction', () => {
|
||||
commitMessage: 'Create my new app, but in the commit message',
|
||||
};
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: { 'file.txt': 'Hello there!' },
|
||||
});
|
||||
|
||||
|
||||
+21
-25
@@ -17,18 +17,13 @@ import { createRootLogger, getRootLogger } from '@backstage/backend-common';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { TemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import mockFs from 'mock-fs';
|
||||
import os from 'os';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { Writable } from 'stream';
|
||||
import { createPublishGitlabMergeRequestAction } from './gitlabMergeRequest';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
// Make sure root logger is initialized ahead of FS mock
|
||||
createRootLogger();
|
||||
|
||||
const root = os.platform() === 'win32' ? 'C:\\root' : '/root';
|
||||
const workspacePath = resolvePath(root, 'my-workspace');
|
||||
|
||||
const mockGitlabClient = {
|
||||
Namespaces: {
|
||||
show: jest.fn(),
|
||||
@@ -79,7 +74,12 @@ jest.mock('@gitbeaker/node', () => ({
|
||||
describe('createGitLabMergeRequest', () => {
|
||||
let instance: TemplateAction<any>;
|
||||
|
||||
const mockDir = createMockDirectory();
|
||||
const workspacePath = mockDir.resolve('workspace');
|
||||
|
||||
beforeEach(() => {
|
||||
mockDir.clear();
|
||||
|
||||
const config = new ConfigReader({
|
||||
integrations: {
|
||||
gitlab: [
|
||||
@@ -100,10 +100,6 @@ describe('createGitLabMergeRequest', () => {
|
||||
instance = createPublishGitlabMergeRequestAction({ integrations });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
describe('createGitLabMergeRequestWithSpecifiedTargetBranch', () => {
|
||||
it('removeSourceBranch is false by default when not passed in options', async () => {
|
||||
const input = {
|
||||
@@ -114,7 +110,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
description: 'This MR is really good',
|
||||
targetPath: 'Subdirectory',
|
||||
};
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -156,7 +152,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
description: 'This MR is really good',
|
||||
targetPath: 'Subdirectory',
|
||||
};
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -200,7 +196,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
removeSourceBranch: true,
|
||||
targetPath: 'Subdirectory',
|
||||
};
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -235,7 +231,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
removeSourceBranch: false,
|
||||
targetPath: 'Subdirectory',
|
||||
};
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -276,7 +272,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
targetPath: 'Subdirectory',
|
||||
assignee: 'John Smith',
|
||||
};
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -316,7 +312,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
targetPath: 'Subdirectory',
|
||||
assingnee: 'John Doe',
|
||||
};
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -356,7 +352,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
removeSourceBranch: false,
|
||||
targetPath: 'Subdirectory',
|
||||
};
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -395,7 +391,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
targetPath: 'Subdirectory',
|
||||
assignee: 'Unknown',
|
||||
};
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -431,7 +427,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
branchName: 'new-mr',
|
||||
description: 'This MR is really good',
|
||||
};
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -480,7 +476,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
description: 'This MR is really good',
|
||||
targetPath: 'source',
|
||||
};
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -523,7 +519,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
commitAction: 'create',
|
||||
targetPath: 'source',
|
||||
};
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -565,7 +561,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
commitAction: 'update',
|
||||
targetPath: 'source',
|
||||
};
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -607,7 +603,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
commitAction: 'delete',
|
||||
targetPath: 'source',
|
||||
};
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -652,7 +648,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
commitAction: 'create',
|
||||
};
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
@@ -697,7 +693,7 @@ describe('createGitLabMergeRequest', () => {
|
||||
commitAction: 'create',
|
||||
};
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[workspacePath]: {
|
||||
source: { 'foo.txt': 'Hello there!' },
|
||||
irrelevant: { 'bar.txt': 'Nothing to see here' },
|
||||
|
||||
@@ -14,10 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import mockFs from 'mock-fs';
|
||||
import * as winston from 'winston';
|
||||
|
||||
import { getVoidLogger, resolvePackagePath } from '@backstage/backend-common';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { NunjucksWorkflowRunner } from './NunjucksWorkflowRunner';
|
||||
import { TemplateActionRegistry } from '../actions';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
@@ -36,19 +33,7 @@ import {
|
||||
PermissionEvaluator,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import { RESOURCE_TYPE_SCAFFOLDER_ACTION } from '@backstage/plugin-scaffolder-common/alpha';
|
||||
|
||||
// The Stream module is lazy loaded, so make sure it's in the module cache before mocking fs
|
||||
void winston.transports.Stream;
|
||||
|
||||
const realFiles = Object.fromEntries(
|
||||
[
|
||||
resolvePackagePath(
|
||||
'@backstage/plugin-scaffolder-backend',
|
||||
'assets',
|
||||
'nunjucks.js.txt',
|
||||
),
|
||||
].map(k => [k, mockFs.load(k)]),
|
||||
);
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('DefaultWorkflowRunner', () => {
|
||||
const logger = getVoidLogger();
|
||||
@@ -56,6 +41,8 @@ describe('DefaultWorkflowRunner', () => {
|
||||
let runner: NunjucksWorkflowRunner;
|
||||
let fakeActionHandler: jest.Mock;
|
||||
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
const mockedPermissionApi: jest.Mocked<PermissionEvaluator> = {
|
||||
authorizeConditional: jest.fn(),
|
||||
} as unknown as jest.Mocked<PermissionEvaluator>;
|
||||
@@ -84,11 +71,7 @@ describe('DefaultWorkflowRunner', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
winston.format.simple(); // put logform in the require.cache before mocking fs
|
||||
mockFs({
|
||||
'/tmp': mockFs.directory(),
|
||||
...realFiles,
|
||||
});
|
||||
mockDir.clear();
|
||||
|
||||
jest.resetAllMocks();
|
||||
actionRegistry = new TemplateActionRegistry();
|
||||
@@ -148,16 +131,12 @@ describe('DefaultWorkflowRunner', () => {
|
||||
runner = new NunjucksWorkflowRunner({
|
||||
actionRegistry,
|
||||
integrations,
|
||||
workingDirectory: '/tmp',
|
||||
workingDirectory: mockDir.path,
|
||||
logger,
|
||||
permissions: mockedPermissionApi,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should throw an error if the action does not exist', async () => {
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'scaffolder.backstage.io/v1beta3',
|
||||
|
||||
@@ -8722,7 +8722,6 @@ __metadata:
|
||||
"@types/git-url-parse": ^9.0.0
|
||||
"@types/libsodium-wrappers": ^0.7.10
|
||||
"@types/luxon": ^3.0.0
|
||||
"@types/mock-fs": ^4.13.0
|
||||
"@types/nunjucks": ^3.1.4
|
||||
"@types/supertest": ^2.0.8
|
||||
"@types/zen-observable": ^0.8.0
|
||||
@@ -8745,7 +8744,6 @@ __metadata:
|
||||
libsodium-wrappers: ^0.7.11
|
||||
lodash: ^4.17.21
|
||||
luxon: ^3.0.0
|
||||
mock-fs: ^5.2.0
|
||||
morgan: ^1.10.0
|
||||
msw: ^1.0.0
|
||||
node-fetch: ^2.6.7
|
||||
|
||||
Reference in New Issue
Block a user