Replace mock-fs with createMockDirectory in the cli packages.
Signed-off-by: Johan Persson <johanp@spotify.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
The scaffolder-module template now recommends usage of `createMockDirectory` instead of `mock-fs`.
|
||||
@@ -158,7 +158,6 @@
|
||||
"@types/http-proxy": "^1.17.4",
|
||||
"@types/inquirer": "^8.1.3",
|
||||
"@types/minimatch": "^5.0.0",
|
||||
"@types/mock-fs": "^4.13.0",
|
||||
"@types/node": "^18.17.8",
|
||||
"@types/npm-packlist": "^3.0.0",
|
||||
"@types/recursive-readdir": "^2.2.0",
|
||||
@@ -169,7 +168,6 @@
|
||||
"@types/terser-webpack-plugin": "^5.0.4",
|
||||
"@types/yarnpkg__lockfile": "^1.1.4",
|
||||
"del": "^7.0.0",
|
||||
"mock-fs": "^5.2.0",
|
||||
"msw": "^1.0.0",
|
||||
"nodemon": "^3.0.1",
|
||||
"ts-node": "^10.0.0",
|
||||
|
||||
@@ -16,23 +16,21 @@
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import mockFs from 'mock-fs';
|
||||
import { movePlugin } from './createPlugin';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
const id = 'testPluginMock';
|
||||
|
||||
describe('createPlugin', () => {
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
describe('movePlugin', () => {
|
||||
it('should move the temporary plugin directory to its final place', async () => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[id]: {},
|
||||
});
|
||||
const tempDir = id;
|
||||
const pluginDir = path.join('test-temp', 'plugins', id);
|
||||
const tempDir = mockDir.resolve(id);
|
||||
const pluginDir = mockDir.resolve('test-temp/plugins', id);
|
||||
|
||||
await movePlugin(tempDir, pluginDir, id);
|
||||
await expect(fs.pathExists(pluginDir)).resolves.toBe(true);
|
||||
|
||||
@@ -15,10 +15,7 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import { Command } from 'commander';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { paths } from '../../lib/paths';
|
||||
import * as runObj from '../../lib/run';
|
||||
import bump, { bumpBackstageJsonVersion, createVersionFinder } from './bump';
|
||||
import {
|
||||
@@ -30,6 +27,10 @@ import { setupServer } from 'msw/node';
|
||||
import { rest } from 'msw';
|
||||
import { NotFoundError } from '@backstage/errors';
|
||||
import { Lockfile } from '../../lib/versioning/Lockfile';
|
||||
import {
|
||||
MockDirectory,
|
||||
createMockDirectory,
|
||||
} from '@backstage/backend-test-utils';
|
||||
|
||||
// Avoid mutating the global http(s) agent used in other tests
|
||||
jest.mock('global-agent/bootstrap', () => {});
|
||||
@@ -56,6 +57,19 @@ jest.mock('ora', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
let mockDir: MockDirectory;
|
||||
|
||||
jest.mock('../../lib/paths', () => ({
|
||||
paths: {
|
||||
resolveTargetRoot(filename: string) {
|
||||
return mockDir.resolve(filename);
|
||||
},
|
||||
get targetDir() {
|
||||
return mockDir.path;
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('../../lib/run', () => {
|
||||
return {
|
||||
run: jest.fn(),
|
||||
@@ -117,7 +131,17 @@ const lockfileMockResult = `${HEADER}
|
||||
version "1.0.0"
|
||||
`;
|
||||
|
||||
// Avoid flakes by comparing sorted log lines. File system access is async, which leads to the log line order being indeterministic
|
||||
const expectLogsToMatch = (
|
||||
recievedLogs: String[],
|
||||
expected: String[],
|
||||
): void => {
|
||||
expect(recievedLogs.filter(Boolean).sort()).toEqual(expected.sort());
|
||||
};
|
||||
|
||||
describe('bump', () => {
|
||||
mockDir = createMockDirectory();
|
||||
|
||||
beforeEach(() => {
|
||||
mockFetchPackageInfo.mockImplementation(async name => ({
|
||||
name: name,
|
||||
@@ -128,7 +152,6 @@ describe('bump', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
@@ -136,31 +159,34 @@ describe('bump', () => {
|
||||
setupRequestMockHandlers(worker);
|
||||
|
||||
it('should bump backstage dependencies', async () => {
|
||||
mockFs({
|
||||
'/yarn.lock': lockfileMock,
|
||||
'/package.json': JSON.stringify({
|
||||
mockDir.setContent({
|
||||
'yarn.lock': lockfileMock,
|
||||
'package.json': JSON.stringify({
|
||||
workspaces: {
|
||||
packages: ['packages/*'],
|
||||
},
|
||||
}),
|
||||
'/packages/a/package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
packages: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
'/packages/b/package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
b: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
jest
|
||||
.spyOn(paths, 'resolveTargetRoot')
|
||||
.mockImplementation((...path) => resolvePath('/', ...path));
|
||||
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
|
||||
worker.use(
|
||||
rest.get(
|
||||
@@ -177,7 +203,7 @@ describe('bump', () => {
|
||||
const { log: logs } = await withLogCollector(['log'], async () => {
|
||||
await bump({ pattern: null, release: 'main' } as unknown as Command);
|
||||
});
|
||||
expect(logs.filter(Boolean)).toEqual([
|
||||
expectLogsToMatch(logs, [
|
||||
'Using default pattern glob @backstage/*',
|
||||
'Checking for updates of @backstage/core',
|
||||
'Checking for updates of @backstage/theme',
|
||||
@@ -208,17 +234,24 @@ describe('bump', () => {
|
||||
expect.any(Object),
|
||||
);
|
||||
|
||||
const lockfileContents = await fs.readFile('/yarn.lock', 'utf8');
|
||||
const lockfileContents = await fs.readFile(
|
||||
mockDir.resolve('yarn.lock'),
|
||||
'utf8',
|
||||
);
|
||||
expect(lockfileContents).toBe(lockfileMockResult);
|
||||
|
||||
const packageA = await fs.readJson('/packages/a/package.json');
|
||||
const packageA = await fs.readJson(
|
||||
mockDir.resolve('packages/a/package.json'),
|
||||
);
|
||||
expect(packageA).toEqual({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.6',
|
||||
},
|
||||
});
|
||||
const packageB = await fs.readJson('/packages/b/package.json');
|
||||
const packageB = await fs.readJson(
|
||||
mockDir.resolve('packages/b/package.json'),
|
||||
);
|
||||
expect(packageB).toEqual({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
@@ -229,31 +262,34 @@ describe('bump', () => {
|
||||
});
|
||||
|
||||
it('should bump backstage dependencies but not install them', async () => {
|
||||
mockFs({
|
||||
'/yarn.lock': lockfileMock,
|
||||
'/package.json': JSON.stringify({
|
||||
mockDir.setContent({
|
||||
'yarn.lock': lockfileMock,
|
||||
'package.json': JSON.stringify({
|
||||
workspaces: {
|
||||
packages: ['packages/*'],
|
||||
},
|
||||
}),
|
||||
'/packages/a/package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
packages: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
'/packages/b/package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
b: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
jest
|
||||
.spyOn(paths, 'resolveTargetRoot')
|
||||
.mockImplementation((...path) => resolvePath('/', ...path));
|
||||
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
|
||||
worker.use(
|
||||
rest.get(
|
||||
@@ -274,7 +310,7 @@ describe('bump', () => {
|
||||
skipInstall: true,
|
||||
} as unknown as Command);
|
||||
});
|
||||
expect(logs.filter(Boolean)).toEqual([
|
||||
expectLogsToMatch(logs, [
|
||||
'Using default pattern glob @backstage/*',
|
||||
'Checking for updates of @backstage/core',
|
||||
'Checking for updates of @backstage/theme',
|
||||
@@ -304,17 +340,24 @@ describe('bump', () => {
|
||||
expect.any(Object),
|
||||
);
|
||||
|
||||
const lockfileContents = await fs.readFile('/yarn.lock', 'utf8');
|
||||
const lockfileContents = await fs.readFile(
|
||||
mockDir.resolve('yarn.lock'),
|
||||
'utf8',
|
||||
);
|
||||
expect(lockfileContents).toBe(lockfileMockResult);
|
||||
|
||||
const packageA = await fs.readJson('/packages/a/package.json');
|
||||
const packageA = await fs.readJson(
|
||||
mockDir.resolve('packages/a/package.json'),
|
||||
);
|
||||
expect(packageA).toEqual({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.6',
|
||||
},
|
||||
});
|
||||
const packageB = await fs.readJson('/packages/b/package.json');
|
||||
const packageB = await fs.readJson(
|
||||
mockDir.resolve('packages/b/package.json'),
|
||||
);
|
||||
expect(packageB).toEqual({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
@@ -325,31 +368,34 @@ describe('bump', () => {
|
||||
});
|
||||
|
||||
it('should prefer dependency versions from release manifest', async () => {
|
||||
mockFs({
|
||||
'/yarn.lock': lockfileMock,
|
||||
'/package.json': JSON.stringify({
|
||||
mockDir.setContent({
|
||||
'yarn.lock': lockfileMock,
|
||||
'package.json': JSON.stringify({
|
||||
workspaces: {
|
||||
packages: ['packages/*'],
|
||||
},
|
||||
}),
|
||||
'/packages/a/package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
packages: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
'/packages/b/package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
b: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
jest
|
||||
.spyOn(paths, 'resolveTargetRoot')
|
||||
.mockImplementation((...path) => resolvePath('/', ...path));
|
||||
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
|
||||
worker.use(
|
||||
rest.get(
|
||||
@@ -376,7 +422,7 @@ describe('bump', () => {
|
||||
const { log: logs } = await withLogCollector(['log'], async () => {
|
||||
await bump({ pattern: null, release: 'main' } as unknown as Command);
|
||||
});
|
||||
expect(logs.filter(Boolean)).toEqual([
|
||||
expectLogsToMatch(logs, [
|
||||
'Using default pattern glob @backstage/*',
|
||||
'Checking for updates of @backstage/core',
|
||||
'Checking for updates of @backstage/theme',
|
||||
@@ -408,17 +454,24 @@ describe('bump', () => {
|
||||
expect.any(Object),
|
||||
);
|
||||
|
||||
const lockfileContents = await fs.readFile('/yarn.lock', 'utf8');
|
||||
const lockfileContents = await fs.readFile(
|
||||
mockDir.resolve('yarn.lock'),
|
||||
'utf8',
|
||||
);
|
||||
expect(lockfileContents).toBe(lockfileMockResult);
|
||||
|
||||
const packageA = await fs.readJson('/packages/a/package.json');
|
||||
const packageA = await fs.readJson(
|
||||
mockDir.resolve('packages/a/package.json'),
|
||||
);
|
||||
expect(packageA).toEqual({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.6',
|
||||
},
|
||||
});
|
||||
const packageB = await fs.readJson('/packages/b/package.json');
|
||||
const packageB = await fs.readJson(
|
||||
mockDir.resolve('packages/b/package.json'),
|
||||
);
|
||||
expect(packageB).toEqual({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
@@ -429,30 +482,33 @@ describe('bump', () => {
|
||||
});
|
||||
|
||||
it('should only bump packages in the manifest when a specific release is specified', async () => {
|
||||
mockFs({
|
||||
'/yarn.lock': lockfileMock,
|
||||
'/package.json': JSON.stringify({
|
||||
mockDir.setContent({
|
||||
'yarn.lock': lockfileMock,
|
||||
'package.json': JSON.stringify({
|
||||
workspaces: {
|
||||
packages: ['packages/*'],
|
||||
},
|
||||
}),
|
||||
'/packages/a/package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
packages: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
'/packages/b/package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
b: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
jest
|
||||
.spyOn(paths, 'resolveTargetRoot')
|
||||
.mockImplementation((...path) => resolvePath('/', ...path));
|
||||
|
||||
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
|
||||
worker.use(
|
||||
@@ -472,14 +528,18 @@ describe('bump', () => {
|
||||
|
||||
expect(runObj.run).toHaveBeenCalledTimes(0);
|
||||
|
||||
const packageA = await fs.readJson('/packages/a/package.json');
|
||||
const packageA = await fs.readJson(
|
||||
mockDir.resolve('packages/a/package.json'),
|
||||
);
|
||||
expect(packageA).toEqual({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
},
|
||||
});
|
||||
const packageB = await fs.readJson('/packages/b/package.json');
|
||||
const packageB = await fs.readJson(
|
||||
mockDir.resolve('packages/b/package.json'),
|
||||
);
|
||||
expect(packageB).toEqual({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
@@ -489,32 +549,36 @@ describe('bump', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('should prefer versions from the highest manifest version when main is not specified', async () => {
|
||||
mockFs({
|
||||
'/yarn.lock': lockfileMock,
|
||||
'/package.json': JSON.stringify({
|
||||
mockDir.setContent({
|
||||
'yarn.lock': lockfileMock,
|
||||
'package.json': JSON.stringify({
|
||||
workspaces: {
|
||||
packages: ['packages/*'],
|
||||
},
|
||||
}),
|
||||
'/packages/a/package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
packages: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
'/packages/b/package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
b: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
jest
|
||||
.spyOn(paths, 'resolveTargetRoot')
|
||||
.mockImplementation((...path) => resolvePath('/', ...path));
|
||||
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
|
||||
worker.use(
|
||||
rest.get(
|
||||
@@ -561,7 +625,7 @@ describe('bump', () => {
|
||||
const { log: logs } = await withLogCollector(['log'], async () => {
|
||||
await bump({ pattern: null, release: 'next' } as unknown as Command);
|
||||
});
|
||||
expect(logs.filter(Boolean)).toEqual([
|
||||
expectLogsToMatch(logs, [
|
||||
'Using default pattern glob @backstage/*',
|
||||
'Checking for updates of @backstage/core',
|
||||
'Checking for updates of @backstage/theme',
|
||||
@@ -609,35 +673,38 @@ describe('bump', () => {
|
||||
"@backstage/theme@^1.0.0":
|
||||
version "1.0.0"
|
||||
`;
|
||||
mockFs({
|
||||
'/yarn.lock': customLockfileMock,
|
||||
'/package.json': JSON.stringify({
|
||||
mockDir.setContent({
|
||||
'yarn.lock': customLockfileMock,
|
||||
'package.json': JSON.stringify({
|
||||
workspaces: {
|
||||
packages: ['packages/*'],
|
||||
},
|
||||
}),
|
||||
'/packages/a/package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
'@backstage-extra/custom': '^1.0.1',
|
||||
'@backstage-extra/custom-two': '^1.0.0',
|
||||
packages: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
'@backstage-extra/custom': '^1.0.1',
|
||||
'@backstage-extra/custom-two': '^1.0.0',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
'/packages/b/package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
'@backstage-extra/custom': '^1.1.0',
|
||||
'@backstage-extra/custom-two': '^1.0.0',
|
||||
b: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
'@backstage-extra/custom': '^1.1.0',
|
||||
'@backstage-extra/custom-two': '^1.0.0',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
jest
|
||||
.spyOn(paths, 'resolveTargetRoot')
|
||||
.mockImplementation((...path) => resolvePath('/', ...path));
|
||||
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
|
||||
worker.use(
|
||||
rest.get(
|
||||
@@ -657,7 +724,7 @@ describe('bump', () => {
|
||||
release: 'main',
|
||||
} as any);
|
||||
});
|
||||
expect(logs.filter(Boolean)).toEqual([
|
||||
expectLogsToMatch(logs, [
|
||||
'Using custom pattern glob @{backstage,backstage-extra}/*',
|
||||
'Checking for updates of @backstage/core',
|
||||
'Checking for updates of @backstage-extra/custom',
|
||||
@@ -696,10 +763,15 @@ describe('bump', () => {
|
||||
expect.any(Object),
|
||||
);
|
||||
|
||||
const lockfileContents = await fs.readFile('/yarn.lock', 'utf8');
|
||||
const lockfileContents = await fs.readFile(
|
||||
mockDir.resolve('yarn.lock'),
|
||||
'utf8',
|
||||
);
|
||||
expect(lockfileContents).toEqual(customLockfileMockResult);
|
||||
|
||||
const packageA = await fs.readJson('/packages/a/package.json');
|
||||
const packageA = await fs.readJson(
|
||||
mockDir.resolve('packages/a/package.json'),
|
||||
);
|
||||
expect(packageA).toEqual({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
@@ -708,7 +780,9 @@ describe('bump', () => {
|
||||
'@backstage/core': '^1.0.6',
|
||||
},
|
||||
});
|
||||
const packageB = await fs.readJson('/packages/b/package.json');
|
||||
const packageB = await fs.readJson(
|
||||
mockDir.resolve('packages/b/package.json'),
|
||||
);
|
||||
expect(packageB).toEqual({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
@@ -721,31 +795,34 @@ describe('bump', () => {
|
||||
});
|
||||
|
||||
it('should ignore not found packages', async () => {
|
||||
mockFs({
|
||||
'/yarn.lock': lockfileMockResult,
|
||||
'/package.json': JSON.stringify({
|
||||
mockDir.setContent({
|
||||
'yarn.lock': lockfileMockResult,
|
||||
'package.json': JSON.stringify({
|
||||
workspaces: {
|
||||
packages: ['packages/*'],
|
||||
},
|
||||
}),
|
||||
'/packages/a/package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
packages: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
'/packages/b/package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^2.0.0',
|
||||
b: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^2.0.0',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
jest
|
||||
.spyOn(paths, 'resolveTargetRoot')
|
||||
.mockImplementation((...path) => resolvePath('/', ...path));
|
||||
mockFetchPackageInfo.mockRejectedValue(new NotFoundError('Nope'));
|
||||
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
|
||||
worker.use(
|
||||
@@ -763,7 +840,7 @@ describe('bump', () => {
|
||||
const { log: logs } = await withLogCollector(['log'], async () => {
|
||||
await bump({ pattern: null, release: 'main' } as unknown as Command);
|
||||
});
|
||||
expect(logs.filter(Boolean)).toEqual([
|
||||
expectLogsToMatch(logs, [
|
||||
'Using default pattern glob @backstage/*',
|
||||
'Checking for updates of @backstage/core',
|
||||
'Checking for updates of @backstage/theme',
|
||||
@@ -778,17 +855,24 @@ describe('bump', () => {
|
||||
|
||||
expect(runObj.run).toHaveBeenCalledTimes(0);
|
||||
|
||||
const lockfileContents = await fs.readFile('/yarn.lock', 'utf8');
|
||||
const lockfileContents = await fs.readFile(
|
||||
mockDir.resolve('yarn.lock'),
|
||||
'utf8',
|
||||
);
|
||||
expect(lockfileContents).toBe(lockfileMockResult);
|
||||
|
||||
const packageA = await fs.readJson('/packages/a/package.json');
|
||||
const packageA = await fs.readJson(
|
||||
mockDir.resolve('packages/a/package.json'),
|
||||
);
|
||||
expect(packageA).toEqual({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5', // not bumped
|
||||
},
|
||||
});
|
||||
const packageB = await fs.readJson('/packages/b/package.json');
|
||||
const packageB = await fs.readJson(
|
||||
mockDir.resolve('packages/b/package.json'),
|
||||
);
|
||||
expect(packageB).toEqual({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
@@ -798,6 +882,7 @@ describe('bump', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('should log duplicates', async () => {
|
||||
jest.spyOn(Lockfile.prototype, 'analyze').mockReturnValue({
|
||||
invalidRanges: [],
|
||||
@@ -826,31 +911,34 @@ describe('bump', () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
mockFs({
|
||||
'/yarn.lock': lockfileMock,
|
||||
'/package.json': JSON.stringify({
|
||||
mockDir.setContent({
|
||||
'yarn.lock': lockfileMock,
|
||||
'package.json': JSON.stringify({
|
||||
workspaces: {
|
||||
packages: ['packages/*'],
|
||||
},
|
||||
}),
|
||||
'/packages/a/package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
packages: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
'/packages/b/package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
b: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
jest
|
||||
.spyOn(paths, 'resolveTargetRoot')
|
||||
.mockImplementation((...path) => resolvePath('/', ...path));
|
||||
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
|
||||
worker.use(
|
||||
rest.get(
|
||||
@@ -867,7 +955,7 @@ describe('bump', () => {
|
||||
const { log: logs } = await withLogCollector(['log'], async () => {
|
||||
await bump({ pattern: null, release: 'main' } as unknown as Command);
|
||||
});
|
||||
expect(logs.filter(Boolean)).toEqual([
|
||||
expectLogsToMatch(logs, [
|
||||
'Using default pattern glob @backstage/*',
|
||||
'Checking for updates of @backstage/core',
|
||||
'Checking for updates of @backstage/theme',
|
||||
@@ -891,24 +979,23 @@ describe('bump', () => {
|
||||
});
|
||||
|
||||
describe('bumpBackstageJsonVersion', () => {
|
||||
mockDir = createMockDirectory();
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should bump version in backstage.json', async () => {
|
||||
mockFs({
|
||||
'/backstage.json': JSON.stringify({ version: '0.0.1' }),
|
||||
mockDir.setContent({
|
||||
'backstage.json': JSON.stringify({ version: '0.0.1' }),
|
||||
});
|
||||
paths.targetDir = '/';
|
||||
jest
|
||||
.spyOn(paths, 'resolveTargetRoot')
|
||||
.mockImplementation((...path) => resolvePath('/', ...path));
|
||||
|
||||
const { log } = await withLogCollector(async () => {
|
||||
await bumpBackstageJsonVersion('1.4.1');
|
||||
});
|
||||
expect(await fs.readJson('/backstage.json')).toEqual({ version: '1.4.1' });
|
||||
expect(await fs.readJson(mockDir.resolve('backstage.json'))).toEqual({
|
||||
version: '1.4.1',
|
||||
});
|
||||
expect(log).toEqual([
|
||||
'Upgraded from release 0.0.1 to 1.4.1, please review these template changes:',
|
||||
undefined,
|
||||
@@ -918,17 +1005,15 @@ describe('bumpBackstageJsonVersion', () => {
|
||||
});
|
||||
|
||||
it("should create backstage.json if doesn't exist", async () => {
|
||||
mockFs({});
|
||||
paths.targetDir = '/';
|
||||
mockDir.clear(); // empty temp test folder
|
||||
const latest = '1.4.1';
|
||||
jest
|
||||
.spyOn(paths, 'resolveTargetRoot')
|
||||
.mockImplementation((...path) => resolvePath('/', ...path));
|
||||
|
||||
const { log } = await withLogCollector(async () => {
|
||||
await bumpBackstageJsonVersion(latest);
|
||||
});
|
||||
expect(await fs.readJson('/backstage.json')).toEqual({ version: latest });
|
||||
expect(await fs.readJson(mockDir.resolve('backstage.json'))).toEqual({
|
||||
version: latest,
|
||||
});
|
||||
expect(log).toEqual([
|
||||
'Your project is now at version 1.4.1, which has been written to backstage.json',
|
||||
]);
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import {
|
||||
NormalizedOutputOptions,
|
||||
OutputAsset,
|
||||
@@ -24,6 +23,7 @@ import {
|
||||
} from 'rollup';
|
||||
|
||||
import { forwardFileImports } from './plugins';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
const context = {
|
||||
meta: {
|
||||
@@ -91,16 +91,18 @@ describe('forwardFileImports', () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe('with mock fs', () => {
|
||||
beforeEach(() => {
|
||||
mockFs({
|
||||
'/dev/src/my-module.ts': '',
|
||||
'/dev/src/dir/my-image.png': 'my-image',
|
||||
});
|
||||
});
|
||||
describe('with createMockDirectory', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
beforeEach(() => {
|
||||
mockDir.setContent({
|
||||
dev: {
|
||||
src: {
|
||||
'my-module.ts': '',
|
||||
dir: { 'my-image.png': 'my-image' },
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should extract files', async () => {
|
||||
@@ -111,23 +113,33 @@ describe('forwardFileImports', () => {
|
||||
throw new Error('options.external is not a function');
|
||||
}
|
||||
|
||||
expect(options.external('./my-module', '/dev/src/index.ts', false)).toBe(
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
options.external('./my-image.png', '/dev/src/dir/index.ts', false),
|
||||
options.external(
|
||||
'./my-module',
|
||||
mockDir.resolve('dev/src/index.ts'),
|
||||
false,
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
options.external(
|
||||
'./my-image.png',
|
||||
mockDir.resolve('dev', 'src', 'dir', 'index.ts'),
|
||||
false,
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
const outPath = '/dev/dist/dir/my-image.png';
|
||||
const outPath = mockDir.resolve('dev', 'dist', 'dir', 'my-image.png');
|
||||
await expect(fs.pathExists(outPath)).resolves.toBe(false);
|
||||
|
||||
await plugin.generateBundle?.call(
|
||||
context,
|
||||
{ dir: '/dev/dist' } as NormalizedOutputOptions,
|
||||
{
|
||||
dir: mockDir.resolve('dev/dist'),
|
||||
} as NormalizedOutputOptions,
|
||||
{
|
||||
['index.js']: {
|
||||
type: 'chunk',
|
||||
facadeModuleId: '/dev/src/index.ts',
|
||||
facadeModuleId: mockDir.resolve('dev/src/index.ts'),
|
||||
} as OutputChunk,
|
||||
},
|
||||
false, // isWrite = false -> no write
|
||||
@@ -136,7 +148,9 @@ describe('forwardFileImports', () => {
|
||||
|
||||
await plugin.generateBundle?.call(
|
||||
context,
|
||||
{ dir: '/dev/dist' } as NormalizedOutputOptions,
|
||||
{
|
||||
dir: mockDir.resolve('dev/dist'),
|
||||
} as NormalizedOutputOptions,
|
||||
{
|
||||
// output assets should not cause a write
|
||||
['index.js']: { type: 'asset' } as OutputAsset,
|
||||
@@ -150,11 +164,13 @@ describe('forwardFileImports', () => {
|
||||
// output chunk + isWrite -> generate files
|
||||
await plugin.generateBundle?.call(
|
||||
context,
|
||||
{ dir: '/dev/dist' } as NormalizedOutputOptions,
|
||||
{
|
||||
dir: mockDir.resolve('dev/dist'),
|
||||
} as NormalizedOutputOptions,
|
||||
{
|
||||
['index.js']: {
|
||||
type: 'chunk',
|
||||
facadeModuleId: '/dev/src/index.ts',
|
||||
facadeModuleId: mockDir.resolve('dev/src/index.ts'),
|
||||
} as OutputChunk,
|
||||
},
|
||||
true,
|
||||
@@ -164,11 +180,13 @@ describe('forwardFileImports', () => {
|
||||
// should not break when triggering another write
|
||||
await plugin.generateBundle?.call(
|
||||
context,
|
||||
{ file: '/dev/dist/my-output.js' } as NormalizedOutputOptions,
|
||||
{
|
||||
file: mockDir.resolve('dev/dist/my-output.js'),
|
||||
} as NormalizedOutputOptions,
|
||||
{
|
||||
['index.js']: {
|
||||
type: 'chunk',
|
||||
facadeModuleId: '/dev/src/index.ts',
|
||||
facadeModuleId: mockDir.resolve('dev/src/index.ts'),
|
||||
} as OutputChunk,
|
||||
},
|
||||
true,
|
||||
|
||||
@@ -15,39 +15,38 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import { sep, resolve as resolvePath } from 'path';
|
||||
import { paths } from '../../paths';
|
||||
import { sep } from 'path';
|
||||
import { Task } from '../../tasks';
|
||||
import { FactoryRegistry } from '../FactoryRegistry';
|
||||
import { createMockOutputStream, mockPaths } from './common/testUtils';
|
||||
import {
|
||||
createMockOutputStream,
|
||||
expectLogsToMatch,
|
||||
mockPaths,
|
||||
} from './common/testUtils';
|
||||
import { backendModule } from './backendModule';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('backendModule factory', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
beforeEach(() => {
|
||||
mockPaths({
|
||||
targetRoot: '/root',
|
||||
targetRoot: mockDir.path,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should create a backend plugin', async () => {
|
||||
mockFs({
|
||||
'/root': {
|
||||
packages: {
|
||||
backend: {
|
||||
'package.json': JSON.stringify({}),
|
||||
},
|
||||
mockDir.setContent({
|
||||
packages: {
|
||||
backend: {
|
||||
'package.json': JSON.stringify({}),
|
||||
},
|
||||
plugins: mockFs.directory(),
|
||||
},
|
||||
[paths.resolveOwn('templates')]: mockFs.load(
|
||||
paths.resolveOwn('templates'),
|
||||
),
|
||||
plugins: {},
|
||||
});
|
||||
|
||||
const options = await FactoryRegistry.populateOptions(backendModule, {
|
||||
@@ -73,8 +72,7 @@ describe('backendModule factory', () => {
|
||||
|
||||
expect(modified).toBe(true);
|
||||
|
||||
expect(output).toEqual([
|
||||
'',
|
||||
expectLogsToMatch(output, [
|
||||
'Creating backend module backstage-plugin-test-backend-module-tester-two',
|
||||
'Checking Prerequisites:',
|
||||
`availability plugins${sep}test-backend-module-tester-two`,
|
||||
@@ -91,14 +89,14 @@ describe('backendModule factory', () => {
|
||||
]);
|
||||
|
||||
await expect(
|
||||
fs.readJson('/root/packages/backend/package.json'),
|
||||
fs.readJson(mockDir.resolve('packages/backend/package.json')),
|
||||
).resolves.toEqual({
|
||||
dependencies: {
|
||||
'backstage-plugin-test-backend-module-tester-two': '^1.0.0',
|
||||
},
|
||||
});
|
||||
const moduleFile = await fs.readFile(
|
||||
'/root/plugins/test-backend-module-tester-two/src/module.ts',
|
||||
mockDir.resolve('plugins/test-backend-module-tester-two/src/module.ts'),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
@@ -110,11 +108,11 @@ describe('backendModule factory', () => {
|
||||
|
||||
expect(Task.forCommand).toHaveBeenCalledTimes(2);
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn install', {
|
||||
cwd: resolvePath('/root/plugins/test-backend-module-tester-two'),
|
||||
cwd: mockDir.resolve('plugins/test-backend-module-tester-two'),
|
||||
optional: true,
|
||||
});
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', {
|
||||
cwd: resolvePath('/root/plugins/test-backend-module-tester-two'),
|
||||
cwd: mockDir.resolve('plugins/test-backend-module-tester-two'),
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,39 +15,38 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import { sep, resolve as resolvePath } from 'path';
|
||||
import { paths } from '../../paths';
|
||||
import { sep } from 'path';
|
||||
import { Task } from '../../tasks';
|
||||
import { FactoryRegistry } from '../FactoryRegistry';
|
||||
import { createMockOutputStream, mockPaths } from './common/testUtils';
|
||||
import {
|
||||
createMockOutputStream,
|
||||
expectLogsToMatch,
|
||||
mockPaths,
|
||||
} from './common/testUtils';
|
||||
import { backendPlugin } from './backendPlugin';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('backendPlugin factory', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
beforeEach(() => {
|
||||
mockPaths({
|
||||
targetRoot: '/root',
|
||||
targetRoot: mockDir.path,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should create a backend plugin', async () => {
|
||||
mockFs({
|
||||
'/root': {
|
||||
packages: {
|
||||
backend: {
|
||||
'package.json': JSON.stringify({}),
|
||||
},
|
||||
mockDir.setContent({
|
||||
packages: {
|
||||
backend: {
|
||||
'package.json': JSON.stringify({}),
|
||||
},
|
||||
plugins: mockFs.directory(),
|
||||
},
|
||||
[paths.resolveOwn('templates')]: mockFs.load(
|
||||
paths.resolveOwn('templates'),
|
||||
),
|
||||
plugins: {},
|
||||
});
|
||||
|
||||
const options = await FactoryRegistry.populateOptions(backendPlugin, {
|
||||
@@ -72,8 +71,7 @@ describe('backendPlugin factory', () => {
|
||||
|
||||
expect(modified).toBe(true);
|
||||
|
||||
expect(output).toEqual([
|
||||
'',
|
||||
expectLogsToMatch(output, [
|
||||
'Creating backend plugin backstage-plugin-test-backend',
|
||||
'Checking Prerequisites:',
|
||||
`availability plugins${sep}test-backend`,
|
||||
@@ -94,14 +92,14 @@ describe('backendPlugin factory', () => {
|
||||
]);
|
||||
|
||||
await expect(
|
||||
fs.readJson('/root/packages/backend/package.json'),
|
||||
fs.readJson(mockDir.resolve('packages/backend/package.json')),
|
||||
).resolves.toEqual({
|
||||
dependencies: {
|
||||
'backstage-plugin-test-backend': '^1.0.0',
|
||||
},
|
||||
});
|
||||
const standaloneServerFile = await fs.readFile(
|
||||
'/root/plugins/test-backend/src/service/standaloneServer.ts',
|
||||
mockDir.resolve('plugins/test-backend/src/service/standaloneServer.ts'),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
@@ -112,11 +110,11 @@ describe('backendPlugin factory', () => {
|
||||
|
||||
expect(Task.forCommand).toHaveBeenCalledTimes(2);
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn install', {
|
||||
cwd: resolvePath('/root/plugins/test-backend'),
|
||||
cwd: mockDir.resolve('plugins/test-backend'),
|
||||
optional: true,
|
||||
});
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', {
|
||||
cwd: resolvePath('/root/plugins/test-backend'),
|
||||
cwd: mockDir.resolve('plugins/test-backend'),
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,32 +15,37 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import { sep } from 'path';
|
||||
import { createMockOutputStream, mockPaths } from './testUtils';
|
||||
import {
|
||||
createMockOutputStream,
|
||||
expectLogsToMatch,
|
||||
mockPaths,
|
||||
} from './testUtils';
|
||||
import { CreateContext } from '../../types';
|
||||
import { executePluginPackageTemplate } from './tasks';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
mockPaths({
|
||||
ownDir: '/own',
|
||||
targetRoot: '/root',
|
||||
ownDir: mockDir.resolve('own'),
|
||||
targetRoot: mockDir.resolve('root'),
|
||||
});
|
||||
|
||||
describe('executePluginPackageTemplate', () => {
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should execute template', async () => {
|
||||
mockFs({
|
||||
'/root': {
|
||||
mockDir.setContent({
|
||||
root: {
|
||||
'yarn.lock': `
|
||||
some-package@^1.1.0:
|
||||
version "1.5.0"
|
||||
`,
|
||||
},
|
||||
'/own': {
|
||||
own: {
|
||||
templates: {
|
||||
'test-template': {
|
||||
'package.json.hbs': `
|
||||
@@ -78,7 +83,7 @@ some-package@^1.1.0:
|
||||
} as CreateContext,
|
||||
{
|
||||
templateName: 'test-template',
|
||||
targetDir: '/target',
|
||||
targetDir: mockDir.resolve('target'),
|
||||
values: {
|
||||
id: 'testing',
|
||||
makePrivate: true,
|
||||
@@ -87,7 +92,7 @@ some-package@^1.1.0:
|
||||
);
|
||||
|
||||
expect(modified).toBe(true);
|
||||
expect(output).toEqual([
|
||||
expectLogsToMatch(output, [
|
||||
'Checking Prerequisites:',
|
||||
`availability ..${sep}target`,
|
||||
'creating temp dir',
|
||||
@@ -98,7 +103,8 @@ some-package@^1.1.0:
|
||||
'Installing:',
|
||||
`moving ..${sep}target`,
|
||||
]);
|
||||
await expect(fs.readFile('/target/package.json', 'utf8')).resolves.toBe(`{
|
||||
await expect(fs.readFile(mockDir.resolve('target/package.json'), 'utf8'))
|
||||
.resolves.toBe(`{
|
||||
"name": "my-testing-plugin",
|
||||
"private": true,
|
||||
"description": "testing",
|
||||
@@ -109,10 +115,10 @@ some-package@^1.1.0:
|
||||
}
|
||||
`);
|
||||
await expect(
|
||||
fs.readFile('/target/subdir/templated.txt', 'utf8'),
|
||||
fs.readFile(mockDir.resolve('target/subdir/templated.txt'), 'utf8'),
|
||||
).resolves.toBe('Hello testing!');
|
||||
await expect(
|
||||
fs.readFile('/target/subdir/not-templated.txt', 'utf8'),
|
||||
fs.readFile(mockDir.resolve('target/subdir/not-templated.txt'), 'utf8'),
|
||||
).resolves.toBe('Hello {{id}}!');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -73,3 +73,11 @@ export function createMockOutputStream() {
|
||||
} as unknown as WriteStream & { fd: any },
|
||||
] as const;
|
||||
}
|
||||
|
||||
// Avoid flakes by comparing sorted log lines. File system access is async, which leads to the log line order being indeterministic
|
||||
export function expectLogsToMatch(
|
||||
recievedLogs: String[],
|
||||
expected: String[],
|
||||
): void {
|
||||
expect(recievedLogs.filter(Boolean).sort()).toEqual(expected.sort());
|
||||
}
|
||||
|
||||
@@ -15,13 +15,16 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import { sep, resolve as resolvePath } from 'path';
|
||||
import { paths } from '../../paths';
|
||||
import { sep } from 'path';
|
||||
import { Task } from '../../tasks';
|
||||
import { FactoryRegistry } from '../FactoryRegistry';
|
||||
import { createMockOutputStream, mockPaths } from './common/testUtils';
|
||||
import {
|
||||
createMockOutputStream,
|
||||
expectLogsToMatch,
|
||||
mockPaths,
|
||||
} from './common/testUtils';
|
||||
import { frontendPlugin } from './frontendPlugin';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
const appTsxContent = `
|
||||
import { createApp } from '@backstage/app-defaults';
|
||||
@@ -34,33 +37,29 @@ const router = (
|
||||
`;
|
||||
|
||||
describe('frontendPlugin factory', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
beforeEach(() => {
|
||||
mockPaths({
|
||||
targetRoot: '/root',
|
||||
targetRoot: mockDir.path,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should create a frontend plugin', async () => {
|
||||
mockFs({
|
||||
'/root': {
|
||||
packages: {
|
||||
app: {
|
||||
'package.json': JSON.stringify({}),
|
||||
src: {
|
||||
'App.tsx': appTsxContent,
|
||||
},
|
||||
mockDir.setContent({
|
||||
packages: {
|
||||
app: {
|
||||
'package.json': JSON.stringify({}),
|
||||
src: {
|
||||
'App.tsx': appTsxContent,
|
||||
},
|
||||
},
|
||||
plugins: mockFs.directory(),
|
||||
},
|
||||
[paths.resolveOwn('templates')]: mockFs.load(
|
||||
paths.resolveOwn('templates'),
|
||||
),
|
||||
plugins: {},
|
||||
});
|
||||
|
||||
const options = await FactoryRegistry.populateOptions(frontendPlugin, {
|
||||
@@ -85,8 +84,7 @@ describe('frontendPlugin factory', () => {
|
||||
|
||||
expect(modified).toBe(true);
|
||||
|
||||
expect(output).toEqual([
|
||||
'',
|
||||
expectLogsToMatch(output, [
|
||||
'Creating frontend plugin backstage-plugin-test',
|
||||
'Checking Prerequisites:',
|
||||
`availability plugins${sep}test`,
|
||||
@@ -114,15 +112,16 @@ describe('frontendPlugin factory', () => {
|
||||
]);
|
||||
|
||||
await expect(
|
||||
fs.readJson('/root/packages/app/package.json'),
|
||||
fs.readJson(mockDir.resolve('packages/app/package.json')),
|
||||
).resolves.toEqual({
|
||||
dependencies: {
|
||||
'backstage-plugin-test': '^1.0.0',
|
||||
},
|
||||
});
|
||||
|
||||
await expect(fs.readFile('/root/packages/app/src/App.tsx', 'utf8')).resolves
|
||||
.toBe(`
|
||||
await expect(
|
||||
fs.readFile(mockDir.resolve('packages/app/src/App.tsx'), 'utf8'),
|
||||
).resolves.toBe(`
|
||||
import { createApp } from '@backstage/app-defaults';
|
||||
import { TestPage } from 'backstage-plugin-test';
|
||||
|
||||
@@ -136,32 +135,27 @@ const router = (
|
||||
|
||||
expect(Task.forCommand).toHaveBeenCalledTimes(2);
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn install', {
|
||||
cwd: resolvePath('/root/plugins/test'),
|
||||
cwd: mockDir.resolve('plugins/test'),
|
||||
optional: true,
|
||||
});
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', {
|
||||
cwd: resolvePath('/root/plugins/test'),
|
||||
cwd: mockDir.resolve('plugins/test'),
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a frontend plugin with more options and codeowners', async () => {
|
||||
mockFs({
|
||||
'/root': {
|
||||
CODEOWNERS: '',
|
||||
packages: {
|
||||
app: {
|
||||
'package.json': JSON.stringify({}),
|
||||
src: {
|
||||
'App.tsx': appTsxContent,
|
||||
},
|
||||
mockDir.setContent({
|
||||
CODEOWNERS: '',
|
||||
packages: {
|
||||
app: {
|
||||
'package.json': JSON.stringify({}),
|
||||
src: {
|
||||
'App.tsx': appTsxContent,
|
||||
},
|
||||
},
|
||||
plugins: mockFs.directory(),
|
||||
},
|
||||
[paths.resolveOwn('templates')]: mockFs.load(
|
||||
paths.resolveOwn('templates'),
|
||||
),
|
||||
plugins: {},
|
||||
});
|
||||
|
||||
const options = await FactoryRegistry.populateOptions(frontendPlugin, {
|
||||
@@ -183,15 +177,16 @@ const router = (
|
||||
});
|
||||
|
||||
await expect(
|
||||
fs.readJson('/root/packages/app/package.json'),
|
||||
fs.readJson(mockDir.resolve('packages/app/package.json')),
|
||||
).resolves.toEqual({
|
||||
dependencies: {
|
||||
'@internal/plugin-test': '^1.0.0',
|
||||
},
|
||||
});
|
||||
|
||||
await expect(fs.readFile('/root/packages/app/src/App.tsx', 'utf8')).resolves
|
||||
.toBe(`
|
||||
await expect(
|
||||
fs.readFile(mockDir.resolve('packages/app/src/App.tsx'), 'utf8'),
|
||||
).resolves.toBe(`
|
||||
import { createApp } from '@backstage/app-defaults';
|
||||
import { TestPage } from '@internal/plugin-test';
|
||||
|
||||
@@ -205,11 +200,11 @@ const router = (
|
||||
|
||||
expect(Task.forCommand).toHaveBeenCalledTimes(2);
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn install', {
|
||||
cwd: resolvePath('/root/plugins/test'),
|
||||
cwd: mockDir.resolve('plugins/test'),
|
||||
optional: true,
|
||||
});
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', {
|
||||
cwd: resolvePath('/root/plugins/test'),
|
||||
cwd: mockDir.resolve('plugins/test'),
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,36 +15,35 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import { resolve as resolvePath, join as joinPath } from 'path';
|
||||
import { paths } from '../../paths';
|
||||
import { join as joinPath } from 'path';
|
||||
import { Task } from '../../tasks';
|
||||
import { FactoryRegistry } from '../FactoryRegistry';
|
||||
import { createMockOutputStream, mockPaths } from './common/testUtils';
|
||||
import {
|
||||
createMockOutputStream,
|
||||
expectLogsToMatch,
|
||||
mockPaths,
|
||||
} from './common/testUtils';
|
||||
import { nodeLibraryPackage } from './nodeLibraryPackage';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('nodeLibraryPackage factory', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
beforeEach(() => {
|
||||
mockPaths({
|
||||
targetRoot: '/root',
|
||||
targetRoot: mockDir.path,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should create a node library package', async () => {
|
||||
const expectedNodeLibraryPackageName = 'test';
|
||||
|
||||
mockFs({
|
||||
'/root': {
|
||||
packages: mockFs.directory(),
|
||||
},
|
||||
[paths.resolveOwn('templates')]: mockFs.load(
|
||||
paths.resolveOwn('templates'),
|
||||
),
|
||||
mockDir.setContent({
|
||||
packages: {},
|
||||
});
|
||||
|
||||
const options = await FactoryRegistry.populateOptions(nodeLibraryPackage, {
|
||||
@@ -69,8 +68,7 @@ describe('nodeLibraryPackage factory', () => {
|
||||
|
||||
expect(modified).toBe(true);
|
||||
|
||||
expect(output).toEqual([
|
||||
'',
|
||||
expectLogsToMatch(output, [
|
||||
`Creating node-library package ${expectedNodeLibraryPackageName}`,
|
||||
'Checking Prerequisites:',
|
||||
`availability ${joinPath('packages', expectedNodeLibraryPackageName)}`,
|
||||
@@ -87,7 +85,11 @@ describe('nodeLibraryPackage factory', () => {
|
||||
|
||||
await expect(
|
||||
fs.readJson(
|
||||
`/root/packages/${expectedNodeLibraryPackageName}/package.json`,
|
||||
mockDir.resolve(
|
||||
'packages',
|
||||
expectedNodeLibraryPackageName,
|
||||
'package.json',
|
||||
),
|
||||
),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -99,11 +101,11 @@ describe('nodeLibraryPackage factory', () => {
|
||||
|
||||
expect(Task.forCommand).toHaveBeenCalledTimes(2);
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn install', {
|
||||
cwd: resolvePath(`/root/packages/${expectedNodeLibraryPackageName}`),
|
||||
cwd: mockDir.resolve('packages', expectedNodeLibraryPackageName),
|
||||
optional: true,
|
||||
});
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', {
|
||||
cwd: resolvePath(`/root/packages/${expectedNodeLibraryPackageName}`),
|
||||
cwd: mockDir.resolve('packages', expectedNodeLibraryPackageName),
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
@@ -111,14 +113,9 @@ describe('nodeLibraryPackage factory', () => {
|
||||
it('should create a node library plugin with options and codeowners', async () => {
|
||||
const expectedNodeLibraryPackageName = 'test';
|
||||
|
||||
mockFs({
|
||||
'/root': {
|
||||
CODEOWNERS: '',
|
||||
packages: mockFs.directory(),
|
||||
},
|
||||
[paths.resolveOwn('templates')]: mockFs.load(
|
||||
paths.resolveOwn('templates'),
|
||||
),
|
||||
mockDir.setContent({
|
||||
CODEOWNERS: '',
|
||||
packages: {},
|
||||
});
|
||||
|
||||
const options = await FactoryRegistry.populateOptions(nodeLibraryPackage, {
|
||||
@@ -141,11 +138,11 @@ describe('nodeLibraryPackage factory', () => {
|
||||
|
||||
expect(Task.forCommand).toHaveBeenCalledTimes(2);
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn install', {
|
||||
cwd: resolvePath(`/root/${expectedNodeLibraryPackageName}`),
|
||||
cwd: mockDir.resolve(expectedNodeLibraryPackageName),
|
||||
optional: true,
|
||||
});
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', {
|
||||
cwd: resolvePath(`/root/${expectedNodeLibraryPackageName}`),
|
||||
cwd: mockDir.resolve(expectedNodeLibraryPackageName),
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,34 +15,33 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import { sep, resolve as resolvePath } from 'path';
|
||||
import { paths } from '../../paths';
|
||||
import { sep } from 'path';
|
||||
import { Task } from '../../tasks';
|
||||
import { FactoryRegistry } from '../FactoryRegistry';
|
||||
import { createMockOutputStream, mockPaths } from './common/testUtils';
|
||||
import {
|
||||
createMockOutputStream,
|
||||
expectLogsToMatch,
|
||||
mockPaths,
|
||||
} from './common/testUtils';
|
||||
import { pluginCommon } from './pluginCommon';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('pluginCommon factory', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
beforeEach(() => {
|
||||
mockPaths({
|
||||
targetRoot: '/root',
|
||||
targetRoot: mockDir.path,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should create a common plugin package', async () => {
|
||||
mockFs({
|
||||
'/root': {
|
||||
plugins: mockFs.directory(),
|
||||
},
|
||||
[paths.resolveOwn('templates')]: mockFs.load(
|
||||
paths.resolveOwn('templates'),
|
||||
),
|
||||
mockDir.setContent({
|
||||
plugins: {},
|
||||
});
|
||||
|
||||
const options = await FactoryRegistry.populateOptions(pluginCommon, {
|
||||
@@ -67,8 +66,7 @@ describe('pluginCommon factory', () => {
|
||||
|
||||
expect(modified).toBe(true);
|
||||
|
||||
expect(output).toEqual([
|
||||
'',
|
||||
expectLogsToMatch(output, [
|
||||
'Creating backend plugin backstage-plugin-test-common',
|
||||
'Checking Prerequisites:',
|
||||
`availability plugins${sep}test-common`,
|
||||
@@ -84,7 +82,7 @@ describe('pluginCommon factory', () => {
|
||||
]);
|
||||
|
||||
await expect(
|
||||
fs.readJson('/root/plugins/test-common/package.json'),
|
||||
fs.readJson(mockDir.resolve('plugins/test-common/package.json')),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
name: 'backstage-plugin-test-common',
|
||||
@@ -96,11 +94,11 @@ describe('pluginCommon factory', () => {
|
||||
|
||||
expect(Task.forCommand).toHaveBeenCalledTimes(2);
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn install', {
|
||||
cwd: resolvePath('/root/plugins/test-common'),
|
||||
cwd: mockDir.resolve('plugins/test-common'),
|
||||
optional: true,
|
||||
});
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', {
|
||||
cwd: resolvePath('/root/plugins/test-common'),
|
||||
cwd: mockDir.resolve('plugins/test-common'),
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,34 +15,33 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import { sep, resolve as resolvePath } from 'path';
|
||||
import { paths } from '../../paths';
|
||||
import { sep } from 'path';
|
||||
import { Task } from '../../tasks';
|
||||
import { FactoryRegistry } from '../FactoryRegistry';
|
||||
import { createMockOutputStream, mockPaths } from './common/testUtils';
|
||||
import {
|
||||
createMockOutputStream,
|
||||
expectLogsToMatch,
|
||||
mockPaths,
|
||||
} from './common/testUtils';
|
||||
import { pluginNode } from './pluginNode';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('pluginNode factory', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
beforeEach(() => {
|
||||
mockPaths({
|
||||
targetRoot: '/root',
|
||||
targetRoot: mockDir.path,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should create a node plugin package', async () => {
|
||||
mockFs({
|
||||
'/root': {
|
||||
plugins: mockFs.directory(),
|
||||
},
|
||||
[paths.resolveOwn('templates')]: mockFs.load(
|
||||
paths.resolveOwn('templates'),
|
||||
),
|
||||
mockDir.setContent({
|
||||
plugins: {},
|
||||
});
|
||||
|
||||
const options = await FactoryRegistry.populateOptions(pluginNode, {
|
||||
@@ -67,8 +66,7 @@ describe('pluginNode factory', () => {
|
||||
|
||||
expect(modified).toBe(true);
|
||||
|
||||
expect(output).toEqual([
|
||||
'',
|
||||
expectLogsToMatch(output, [
|
||||
'Creating Node.js plugin library backstage-plugin-test-node',
|
||||
'Checking Prerequisites:',
|
||||
`availability plugins${sep}test-node`,
|
||||
@@ -84,7 +82,7 @@ describe('pluginNode factory', () => {
|
||||
]);
|
||||
|
||||
await expect(
|
||||
fs.readJson('/root/plugins/test-node/package.json'),
|
||||
fs.readJson(mockDir.resolve('plugins/test-node/package.json')),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
name: 'backstage-plugin-test-node',
|
||||
@@ -96,11 +94,11 @@ describe('pluginNode factory', () => {
|
||||
|
||||
expect(Task.forCommand).toHaveBeenCalledTimes(2);
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn install', {
|
||||
cwd: resolvePath('/root/plugins/test-node'),
|
||||
cwd: mockDir.resolve('plugins/test-node'),
|
||||
optional: true,
|
||||
});
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', {
|
||||
cwd: resolvePath('/root/plugins/test-node'),
|
||||
cwd: mockDir.resolve('plugins/test-node'),
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,34 +15,33 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import { sep, resolve as resolvePath } from 'path';
|
||||
import { paths } from '../../paths';
|
||||
import { sep } from 'path';
|
||||
import { Task } from '../../tasks';
|
||||
import { FactoryRegistry } from '../FactoryRegistry';
|
||||
import { createMockOutputStream, mockPaths } from './common/testUtils';
|
||||
import {
|
||||
createMockOutputStream,
|
||||
expectLogsToMatch,
|
||||
mockPaths,
|
||||
} from './common/testUtils';
|
||||
import { pluginWeb } from './pluginWeb';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('pluginWeb factory', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
beforeEach(() => {
|
||||
mockPaths({
|
||||
targetRoot: '/root',
|
||||
targetRoot: mockDir.path,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should create a react plugin package', async () => {
|
||||
mockFs({
|
||||
'/root': {
|
||||
plugins: mockFs.directory(),
|
||||
},
|
||||
[paths.resolveOwn('templates')]: mockFs.load(
|
||||
paths.resolveOwn('templates'),
|
||||
),
|
||||
mockDir.setContent({
|
||||
plugins: {},
|
||||
});
|
||||
|
||||
const options = await FactoryRegistry.populateOptions(pluginWeb, {
|
||||
@@ -67,8 +66,7 @@ describe('pluginWeb factory', () => {
|
||||
|
||||
expect(modified).toBe(true);
|
||||
|
||||
expect(output).toEqual([
|
||||
'',
|
||||
expectLogsToMatch(output, [
|
||||
'Creating web plugin library backstage-plugin-test-react',
|
||||
'Checking Prerequisites:',
|
||||
`availability plugins${sep}test-react`,
|
||||
@@ -91,7 +89,7 @@ describe('pluginWeb factory', () => {
|
||||
]);
|
||||
|
||||
await expect(
|
||||
fs.readJson('/root/plugins/test-react/package.json'),
|
||||
fs.readJson(mockDir.resolve('plugins/test-react/package.json')),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
name: 'backstage-plugin-test-react',
|
||||
@@ -103,11 +101,11 @@ describe('pluginWeb factory', () => {
|
||||
|
||||
expect(Task.forCommand).toHaveBeenCalledTimes(2);
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn install', {
|
||||
cwd: resolvePath('/root/plugins/test-react'),
|
||||
cwd: mockDir.resolve('plugins/test-react'),
|
||||
optional: true,
|
||||
});
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', {
|
||||
cwd: resolvePath('/root/plugins/test-react'),
|
||||
cwd: mockDir.resolve('plugins/test-react'),
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,34 +15,33 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import { sep, resolve as resolvePath } from 'path';
|
||||
import { paths } from '../../paths';
|
||||
import { sep } from 'path';
|
||||
import { Task } from '../../tasks';
|
||||
import { FactoryRegistry } from '../FactoryRegistry';
|
||||
import { createMockOutputStream, mockPaths } from './common/testUtils';
|
||||
import {
|
||||
createMockOutputStream,
|
||||
expectLogsToMatch,
|
||||
mockPaths,
|
||||
} from './common/testUtils';
|
||||
import { scaffolderModule } from './scaffolderModule';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('scaffolderModule factory', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
beforeEach(() => {
|
||||
mockPaths({
|
||||
targetRoot: '/root',
|
||||
targetRoot: mockDir.path,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should create a scaffolder backend module package', async () => {
|
||||
mockFs({
|
||||
'/root': {
|
||||
plugins: mockFs.directory(),
|
||||
},
|
||||
[paths.resolveOwn('templates')]: mockFs.load(
|
||||
paths.resolveOwn('templates'),
|
||||
),
|
||||
mockDir.setContent({
|
||||
plugins: {},
|
||||
});
|
||||
|
||||
const options = await FactoryRegistry.populateOptions(scaffolderModule, {
|
||||
@@ -67,8 +66,7 @@ describe('scaffolderModule factory', () => {
|
||||
|
||||
expect(modified).toBe(true);
|
||||
|
||||
expect(output).toEqual([
|
||||
'',
|
||||
expectLogsToMatch(output, [
|
||||
'Creating module backstage-plugin-scaffolder-backend-module-test',
|
||||
'Checking Prerequisites:',
|
||||
`availability plugins${sep}scaffolder-backend-module-test`,
|
||||
@@ -87,7 +85,9 @@ describe('scaffolderModule factory', () => {
|
||||
]);
|
||||
|
||||
await expect(
|
||||
fs.readJson('/root/plugins/scaffolder-backend-module-test/package.json'),
|
||||
fs.readJson(
|
||||
mockDir.resolve('plugins/scaffolder-backend-module-test/package.json'),
|
||||
),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
name: 'backstage-plugin-scaffolder-backend-module-test',
|
||||
@@ -99,11 +99,11 @@ describe('scaffolderModule factory', () => {
|
||||
|
||||
expect(Task.forCommand).toHaveBeenCalledTimes(2);
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn install', {
|
||||
cwd: resolvePath('/root/plugins/scaffolder-backend-module-test'),
|
||||
cwd: mockDir.resolve('plugins/scaffolder-backend-module-test'),
|
||||
optional: true,
|
||||
});
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', {
|
||||
cwd: resolvePath('/root/plugins/scaffolder-backend-module-test'),
|
||||
cwd: mockDir.resolve('plugins/scaffolder-backend-module-test'),
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,36 +15,35 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import { resolve as resolvePath, join as joinPath } from 'path';
|
||||
import { paths } from '../../paths';
|
||||
import { join as joinPath } from 'path';
|
||||
import { Task } from '../../tasks';
|
||||
import { FactoryRegistry } from '../FactoryRegistry';
|
||||
import { createMockOutputStream, mockPaths } from './common/testUtils';
|
||||
import {
|
||||
createMockOutputStream,
|
||||
expectLogsToMatch,
|
||||
mockPaths,
|
||||
} from './common/testUtils';
|
||||
import { webLibraryPackage } from './webLibraryPackage';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('webLibraryPackage factory', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
beforeEach(() => {
|
||||
mockPaths({
|
||||
targetRoot: '/root',
|
||||
targetRoot: mockDir.path,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should create a web library package', async () => {
|
||||
const expectedwebLibraryPackageName = 'test';
|
||||
|
||||
mockFs({
|
||||
'/root': {
|
||||
packages: mockFs.directory(),
|
||||
},
|
||||
[paths.resolveOwn('templates')]: mockFs.load(
|
||||
paths.resolveOwn('templates'),
|
||||
),
|
||||
mockDir.setContent({
|
||||
packages: {},
|
||||
});
|
||||
|
||||
const options = await FactoryRegistry.populateOptions(webLibraryPackage, {
|
||||
@@ -69,8 +68,7 @@ describe('webLibraryPackage factory', () => {
|
||||
|
||||
expect(modified).toBe(true);
|
||||
|
||||
expect(output).toEqual([
|
||||
'',
|
||||
expectLogsToMatch(output, [
|
||||
`Creating web-library package ${expectedwebLibraryPackageName}`,
|
||||
'Checking Prerequisites:',
|
||||
`availability ${joinPath('packages', expectedwebLibraryPackageName)}`,
|
||||
@@ -87,7 +85,11 @@ describe('webLibraryPackage factory', () => {
|
||||
|
||||
await expect(
|
||||
fs.readJson(
|
||||
`/root/packages/${expectedwebLibraryPackageName}/package.json`,
|
||||
mockDir.resolve(
|
||||
'packages',
|
||||
expectedwebLibraryPackageName,
|
||||
'package.json',
|
||||
),
|
||||
),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -99,11 +101,11 @@ describe('webLibraryPackage factory', () => {
|
||||
|
||||
expect(Task.forCommand).toHaveBeenCalledTimes(2);
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn install', {
|
||||
cwd: resolvePath(`/root/packages/${expectedwebLibraryPackageName}`),
|
||||
cwd: mockDir.resolve('packages', expectedwebLibraryPackageName),
|
||||
optional: true,
|
||||
});
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', {
|
||||
cwd: resolvePath(`/root/packages/${expectedwebLibraryPackageName}`),
|
||||
cwd: mockDir.resolve('packages', expectedwebLibraryPackageName),
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
@@ -111,14 +113,9 @@ describe('webLibraryPackage factory', () => {
|
||||
it('should create a web library plugin with options and codeowners', async () => {
|
||||
const expectedwebLibraryPackageName = 'test';
|
||||
|
||||
mockFs({
|
||||
'/root': {
|
||||
CODEOWNERS: '',
|
||||
packages: mockFs.directory(),
|
||||
},
|
||||
[paths.resolveOwn('templates')]: mockFs.load(
|
||||
paths.resolveOwn('templates'),
|
||||
),
|
||||
mockDir.setContent({
|
||||
CODEOWNERS: '',
|
||||
packages: {},
|
||||
});
|
||||
|
||||
const options = await FactoryRegistry.populateOptions(webLibraryPackage, {
|
||||
@@ -141,11 +138,11 @@ describe('webLibraryPackage factory', () => {
|
||||
|
||||
expect(Task.forCommand).toHaveBeenCalledTimes(2);
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn install', {
|
||||
cwd: resolvePath(`/root/${expectedwebLibraryPackageName}`),
|
||||
cwd: mockDir.resolve(expectedwebLibraryPackageName),
|
||||
optional: true,
|
||||
});
|
||||
expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', {
|
||||
cwd: resolvePath(`/root/${expectedwebLibraryPackageName}`),
|
||||
cwd: mockDir.resolve(expectedwebLibraryPackageName),
|
||||
optional: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,10 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import mockFs from 'mock-fs';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
import { Command } from 'commander';
|
||||
import { findRoleFromCommand } from './role';
|
||||
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
jest.mock('./paths', () => ({
|
||||
paths: {
|
||||
resolveTarget(filename: string) {
|
||||
return mockDir.resolve(filename);
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
describe('findRoleFromCommand', () => {
|
||||
function mkCommand(args: string) {
|
||||
const parsed = new Command()
|
||||
@@ -27,7 +37,7 @@ describe('findRoleFromCommand', () => {
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
'package.json': JSON.stringify({
|
||||
name: 'test',
|
||||
backstage: {
|
||||
@@ -37,10 +47,6 @@ describe('findRoleFromCommand', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('provides role info by role', async () => {
|
||||
await expect(findRoleFromCommand(mkCommand(''))).resolves.toEqual(
|
||||
'web-library',
|
||||
|
||||
@@ -15,14 +15,11 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { templatingTask } from './tasks';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('templatingTask', () => {
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
it('should template a directory with mix of regular files and templates', async () => {
|
||||
// Testing template directory
|
||||
@@ -36,7 +33,7 @@ describe('templatingTask', () => {
|
||||
const testVersionFileContent =
|
||||
"version: {{pluginVersion}} {{versionQuery 'mock-pkg'}}";
|
||||
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
[tmplDir]: {
|
||||
sub: {
|
||||
'version.txt.hbs': testVersionFileContent,
|
||||
@@ -47,8 +44,8 @@ describe('templatingTask', () => {
|
||||
});
|
||||
|
||||
await templatingTask(
|
||||
tmplDir,
|
||||
destDir,
|
||||
mockDir.resolve(tmplDir),
|
||||
mockDir.resolve(destDir),
|
||||
{
|
||||
pluginVersion: '0.0.0',
|
||||
},
|
||||
@@ -57,10 +54,10 @@ describe('templatingTask', () => {
|
||||
);
|
||||
|
||||
await expect(
|
||||
fs.readFile(resolvePath(destDir, 'test.txt'), 'utf8'),
|
||||
fs.readFile(mockDir.resolve(destDir, 'test.txt'), 'utf8'),
|
||||
).resolves.toBe(testFileContent);
|
||||
await expect(
|
||||
fs.readFile(resolvePath(destDir, 'sub/version.txt'), 'utf8'),
|
||||
fs.readFile(mockDir.resolve(destDir, 'sub/version.txt'), 'utf8'),
|
||||
).resolves.toBe('version: 0.0.0 ^0.1.2');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,15 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import mockFs from 'mock-fs';
|
||||
import { packageVersions, createPackageVersionProvider } from './version';
|
||||
import { Lockfile } from './versioning';
|
||||
import corePluginApiPkg from '@backstage/core-plugin-api/package.json';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('createPackageVersionProvider', () => {
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
const HEADER = `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
@@ -30,7 +28,7 @@ describe('createPackageVersionProvider', () => {
|
||||
`;
|
||||
|
||||
it('should provide package versions', async () => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
'yarn.lock': `${HEADER}
|
||||
"a@^0.1.0":
|
||||
version "0.1.5"
|
||||
@@ -55,7 +53,8 @@ describe('createPackageVersionProvider', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const lockfile = await Lockfile.load('yarn.lock');
|
||||
const lockfilePath = mockDir.resolve('yarn.lock');
|
||||
const lockfile = await Lockfile.load(lockfilePath);
|
||||
const provider = createPackageVersionProvider(lockfile);
|
||||
|
||||
expect(provider('a', '0.1.5')).toBe('^0.1.0');
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import { BackstagePackage } from '@backstage/cli-node';
|
||||
import { Lockfile } from './Lockfile';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
const LEGACY_HEADER = `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
@@ -76,16 +76,14 @@ const mockBDedup = `${LEGACY_HEADER}
|
||||
`;
|
||||
|
||||
describe('Lockfile', () => {
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
it('should load and serialize mockA', async () => {
|
||||
mockFs({
|
||||
'/yarn.lock': mockA,
|
||||
mockDir.setContent({
|
||||
'yarn.lock': mockA,
|
||||
});
|
||||
|
||||
const lockfile = await Lockfile.load('/yarn.lock');
|
||||
const lockfile = await Lockfile.load(mockDir.resolve('yarn.lock'));
|
||||
expect(lockfile.get('a')).toEqual([
|
||||
{ range: '^1', version: '1.0.1', dataKey: 'a@^1' },
|
||||
]);
|
||||
@@ -97,11 +95,12 @@ describe('Lockfile', () => {
|
||||
});
|
||||
|
||||
it('should deduplicate and save mockA', async () => {
|
||||
mockFs({
|
||||
'/yarn.lock': mockA,
|
||||
mockDir.setContent({
|
||||
'yarn.lock': mockA,
|
||||
});
|
||||
|
||||
const lockfile = await Lockfile.load('/yarn.lock');
|
||||
const lockfilePath = mockDir.resolve('yarn.lock');
|
||||
const lockfile = await Lockfile.load(lockfilePath);
|
||||
const result = lockfile.analyze({ localPackages: new Map() });
|
||||
expect(result).toEqual({
|
||||
invalidRanges: [],
|
||||
@@ -120,17 +119,17 @@ describe('Lockfile', () => {
|
||||
lockfile.replaceVersions(result.newVersions);
|
||||
expect(lockfile.toString()).toBe(mockADedup);
|
||||
|
||||
await expect(fs.readFile('/yarn.lock', 'utf8')).resolves.toBe(mockA);
|
||||
await expect(lockfile.save('/yarn.lock')).resolves.toBeUndefined();
|
||||
await expect(fs.readFile('/yarn.lock', 'utf8')).resolves.toBe(mockADedup);
|
||||
await expect(fs.readFile(lockfilePath, 'utf8')).resolves.toBe(mockA);
|
||||
await expect(lockfile.save(lockfilePath)).resolves.toBeUndefined();
|
||||
await expect(fs.readFile(lockfilePath, 'utf8')).resolves.toBe(mockADedup);
|
||||
});
|
||||
|
||||
it('should deduplicate mockB', async () => {
|
||||
mockFs({
|
||||
'/yarn.lock': mockB,
|
||||
mockDir.setContent({
|
||||
'yarn.lock': mockB,
|
||||
});
|
||||
|
||||
const lockfile = await Lockfile.load('/yarn.lock');
|
||||
const lockfile = await Lockfile.load(mockDir.resolve('yarn.lock'));
|
||||
const result = lockfile.analyze({ localPackages: new Map() });
|
||||
expect(result).toEqual({
|
||||
invalidRanges: [],
|
||||
@@ -226,16 +225,14 @@ b@^2:
|
||||
`;
|
||||
|
||||
describe('New Lockfile', () => {
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
it('should load and serialize mockANew', async () => {
|
||||
mockFs({
|
||||
'/yarn.lock': mockANew,
|
||||
mockDir.setContent({
|
||||
'yarn.lock': mockANew,
|
||||
});
|
||||
|
||||
const lockfile = await Lockfile.load('/yarn.lock');
|
||||
const lockfile = await Lockfile.load(mockDir.resolve('yarn.lock'));
|
||||
expect(lockfile.get('a')).toEqual([
|
||||
{ range: '^1', version: '1.0.1', dataKey: 'a@^1' },
|
||||
]);
|
||||
@@ -248,11 +245,12 @@ describe('New Lockfile', () => {
|
||||
});
|
||||
|
||||
it('should deduplicate and save mockANew', async () => {
|
||||
mockFs({
|
||||
'/yarn.lock': mockANew,
|
||||
mockDir.setContent({
|
||||
'yarn.lock': mockANew,
|
||||
});
|
||||
|
||||
const lockfile = await Lockfile.load('/yarn.lock');
|
||||
const lockfilePath = mockDir.resolve('yarn.lock');
|
||||
const lockfile = await Lockfile.load(lockfilePath);
|
||||
const result = lockfile.analyze({ localPackages: new Map() });
|
||||
expect(result).toEqual({
|
||||
invalidRanges: [],
|
||||
@@ -271,19 +269,20 @@ describe('New Lockfile', () => {
|
||||
lockfile.replaceVersions(result.newVersions);
|
||||
expect(lockfile.toString()).toBe(mockANewDedup);
|
||||
|
||||
await expect(fs.readFile('/yarn.lock', 'utf8')).resolves.toBe(mockANew);
|
||||
await expect(lockfile.save('/yarn.lock')).resolves.toBeUndefined();
|
||||
await expect(fs.readFile('/yarn.lock', 'utf8')).resolves.toBe(
|
||||
await expect(fs.readFile(lockfilePath, 'utf8')).resolves.toBe(mockANew);
|
||||
await expect(lockfile.save(lockfilePath)).resolves.toBeUndefined();
|
||||
await expect(fs.readFile(lockfilePath, 'utf8')).resolves.toBe(
|
||||
mockANewDedup,
|
||||
);
|
||||
});
|
||||
|
||||
it('should deduplicate and save mockANewLocal', async () => {
|
||||
mockFs({
|
||||
'/yarn.lock': mockANewLocal,
|
||||
mockDir.setContent({
|
||||
'yarn.lock': mockANewLocal,
|
||||
});
|
||||
|
||||
const lockfile = await Lockfile.load('/yarn.lock');
|
||||
const lockfilePath = mockDir.resolve('yarn.lock');
|
||||
const lockfile = await Lockfile.load(lockfilePath);
|
||||
const result = lockfile.analyze({
|
||||
localPackages: new Map([
|
||||
[
|
||||
@@ -311,11 +310,11 @@ describe('New Lockfile', () => {
|
||||
lockfile.replaceVersions(result.newVersions);
|
||||
expect(lockfile.toString()).toBe(mockANewLocalDedup);
|
||||
|
||||
await expect(fs.readFile('/yarn.lock', 'utf8')).resolves.toBe(
|
||||
await expect(fs.readFile(lockfilePath, 'utf8')).resolves.toBe(
|
||||
mockANewLocal,
|
||||
);
|
||||
await expect(lockfile.save('/yarn.lock')).resolves.toBeUndefined();
|
||||
await expect(fs.readFile('/yarn.lock', 'utf8')).resolves.toBe(
|
||||
await expect(lockfile.save(lockfilePath)).resolves.toBeUndefined();
|
||||
await expect(fs.readFile(lockfilePath, 'utf8')).resolves.toBe(
|
||||
mockANewLocalDedup,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -14,12 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import mockFs from 'mock-fs';
|
||||
import path from 'path';
|
||||
import * as runObj from '../run';
|
||||
import * as yarn from '../yarn';
|
||||
import { fetchPackageInfo, mapDependencies } from './packages';
|
||||
import { NotFoundError } from '../errors';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
jest.mock('../run', () => {
|
||||
return {
|
||||
@@ -96,34 +95,41 @@ describe('fetchPackageInfo', () => {
|
||||
});
|
||||
|
||||
describe('mapDependencies', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should read dependencies', async () => {
|
||||
mockFs({
|
||||
'/root/package.json': JSON.stringify({
|
||||
mockDir.setContent({
|
||||
'package.json': JSON.stringify({
|
||||
workspaces: {
|
||||
packages: ['pkgs/*'],
|
||||
},
|
||||
}),
|
||||
'/root/pkgs/a/package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '1 || 2',
|
||||
pkgs: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '1 || 2',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
'/root/pkgs/b/package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '3',
|
||||
'@backstage/cli': '^0',
|
||||
b: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '3',
|
||||
'@backstage/cli': '^0',
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
const dependencyMap = await mapDependencies('/root', '@backstage/*');
|
||||
const dependencyMap = await mapDependencies(mockDir.path, '@backstage/*');
|
||||
expect(Array.from(dependencyMap)).toEqual([
|
||||
[
|
||||
'@backstage/core',
|
||||
@@ -131,12 +137,12 @@ describe('mapDependencies', () => {
|
||||
{
|
||||
name: 'a',
|
||||
range: '1 || 2',
|
||||
location: path.resolve('/root/pkgs/a'),
|
||||
location: mockDir.resolve('pkgs/a'),
|
||||
},
|
||||
{
|
||||
name: 'b',
|
||||
range: '3',
|
||||
location: path.resolve('/root/pkgs/b'),
|
||||
location: mockDir.resolve('pkgs/b'),
|
||||
},
|
||||
],
|
||||
],
|
||||
@@ -146,7 +152,7 @@ describe('mapDependencies', () => {
|
||||
{
|
||||
name: 'b',
|
||||
range: '^0',
|
||||
location: path.resolve('/root/pkgs/b'),
|
||||
location: mockDir.resolve('pkgs/b'),
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
@@ -22,7 +22,7 @@ describe('acme:example', () => {
|
||||
logStream: new PassThrough(),
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory() {
|
||||
// Usage of mock-fs is recommended for testing of filesystem operations
|
||||
// Usage of createMockDirectory is recommended for testing of filesystem operations
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3784,7 +3784,6 @@ __metadata:
|
||||
"@types/inquirer": ^8.1.3
|
||||
"@types/jest": ^29.0.0
|
||||
"@types/minimatch": ^5.0.0
|
||||
"@types/mock-fs": ^4.13.0
|
||||
"@types/node": ^18.17.8
|
||||
"@types/npm-packlist": ^3.0.0
|
||||
"@types/recursive-readdir": ^2.2.0
|
||||
@@ -3839,7 +3838,6 @@ __metadata:
|
||||
lodash: ^4.17.21
|
||||
mini-css-extract-plugin: ^2.4.2
|
||||
minimatch: ^5.1.1
|
||||
mock-fs: ^5.2.0
|
||||
msw: ^1.0.0
|
||||
node-fetch: ^2.6.7
|
||||
node-libs-browser: ^2.2.1
|
||||
|
||||
Reference in New Issue
Block a user