Merge pull request #20399 from backstage/rugvip/more-20-fixes
More replacements of mock-fs
This commit is contained in:
@@ -87,7 +87,6 @@
|
||||
"@types/node-forge": "^1.3.0",
|
||||
"@types/stoppable": "^1.1.0",
|
||||
"http-errors": "^2.0.0",
|
||||
"mock-fs": "^5.2.0",
|
||||
"supertest": "^6.1.3"
|
||||
},
|
||||
"configSchema": "config.d.ts",
|
||||
|
||||
@@ -14,28 +14,31 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import mockFs from 'mock-fs';
|
||||
import { resolve as resolvePath, dirname } from 'path';
|
||||
import { startTestBackend, mockServices } from '@backstage/backend-test-utils';
|
||||
import {
|
||||
startTestBackend,
|
||||
mockServices,
|
||||
createMockDirectory,
|
||||
} from '@backstage/backend-test-utils';
|
||||
import { featureDiscoveryServiceFactory } from './featureDiscoveryServiceFactory';
|
||||
|
||||
const rootDir = dirname(process.argv[1]);
|
||||
const mockDir = createMockDirectory();
|
||||
process.argv[1] = mockDir.path;
|
||||
|
||||
const pluginApiPath = require.resolve('@backstage/backend-plugin-api');
|
||||
|
||||
describe('featureDiscoveryServiceFactory', () => {
|
||||
beforeEach(() => {
|
||||
mockFs({
|
||||
[rootDir]: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'example-app',
|
||||
dependencies: {
|
||||
'detected-plugin': '0.0.0',
|
||||
'detected-module': '0.0.0',
|
||||
'detected-plugin-with-alpha': '0.0.0',
|
||||
'detected-library': '0.0.0',
|
||||
},
|
||||
}),
|
||||
},
|
||||
[resolvePath(rootDir, 'node_modules/detected-plugin')]: {
|
||||
mockDir.setContent({
|
||||
'package.json': JSON.stringify({
|
||||
name: 'example-app',
|
||||
dependencies: {
|
||||
'detected-plugin': '0.0.0',
|
||||
'detected-module': '0.0.0',
|
||||
'detected-plugin-with-alpha': '0.0.0',
|
||||
'detected-library': '0.0.0',
|
||||
},
|
||||
}),
|
||||
'node_modules/detected-plugin': {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'detected-plugin',
|
||||
main: 'index.js',
|
||||
@@ -44,7 +47,7 @@ describe('featureDiscoveryServiceFactory', () => {
|
||||
},
|
||||
}),
|
||||
'index.js': `
|
||||
const { createBackendPlugin, coreServices } = require('@backstage/backend-plugin-api');
|
||||
const { createBackendPlugin, coreServices } = require('${pluginApiPath}');
|
||||
exports.default = createBackendPlugin({
|
||||
pluginId: 'detected',
|
||||
register(env) {
|
||||
@@ -58,7 +61,7 @@ describe('featureDiscoveryServiceFactory', () => {
|
||||
});
|
||||
`,
|
||||
},
|
||||
[resolvePath(rootDir, 'node_modules/detected-module')]: {
|
||||
'node_modules/detected-module': {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'detected-module',
|
||||
main: 'index.js',
|
||||
@@ -67,7 +70,7 @@ describe('featureDiscoveryServiceFactory', () => {
|
||||
},
|
||||
}),
|
||||
'index.js': `
|
||||
const { createBackendModule, coreServices } = require('@backstage/backend-plugin-api');
|
||||
const { createBackendModule, coreServices } = require('${pluginApiPath}');
|
||||
exports.default = createBackendModule({
|
||||
pluginId: 'detected',
|
||||
moduleId: 'derp',
|
||||
@@ -82,7 +85,7 @@ describe('featureDiscoveryServiceFactory', () => {
|
||||
});
|
||||
`,
|
||||
},
|
||||
[resolvePath(rootDir, 'node_modules/detected-plugin-with-alpha')]: {
|
||||
'node_modules/detected-plugin-with-alpha': {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'detected-plugin-with-alpha',
|
||||
main: 'index.js',
|
||||
@@ -101,7 +104,7 @@ describe('featureDiscoveryServiceFactory', () => {
|
||||
}),
|
||||
'index.js': `exports.default = undefined;`,
|
||||
'alpha.js': `
|
||||
const { createBackendPlugin, coreServices } = require('@backstage/backend-plugin-api');
|
||||
const { createBackendPlugin, coreServices } = require('${pluginApiPath}');
|
||||
exports.default = createBackendPlugin({
|
||||
pluginId: 'detected-alpha',
|
||||
register(env) {
|
||||
@@ -115,7 +118,7 @@ describe('featureDiscoveryServiceFactory', () => {
|
||||
});
|
||||
`,
|
||||
},
|
||||
[resolvePath(rootDir, 'node_modules/detected-library')]: {
|
||||
'node_modules/detected-library': {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'detected-library',
|
||||
main: 'index.js',
|
||||
@@ -124,7 +127,7 @@ describe('featureDiscoveryServiceFactory', () => {
|
||||
},
|
||||
}),
|
||||
'index.js': `
|
||||
const { createServiceFactory, createServiceRef, coreServices } = require('@backstage/backend-plugin-api');
|
||||
const { createServiceFactory, createServiceRef, coreServices } = require('${pluginApiPath}');
|
||||
exports.default = createServiceFactory({
|
||||
service: createServiceRef({ id: 'test', scope: 'root' }),
|
||||
deps: { logger: coreServices.rootLogger },
|
||||
@@ -138,10 +141,6 @@ describe('featureDiscoveryServiceFactory', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should detect plugin and module packages when "all" is specified', async () => {
|
||||
const mock = mockServices.rootLogger.mock({ child: () => mock });
|
||||
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
"@types/json-schema-merge-allof": "^0.6.0",
|
||||
"@types/mock-fs": "^4.10.0",
|
||||
"@types/yup": "^0.29.13",
|
||||
"mock-fs": "^5.2.0",
|
||||
"msw": "^1.0.0",
|
||||
"zen-observable": "^0.10.0"
|
||||
},
|
||||
|
||||
@@ -16,15 +16,62 @@
|
||||
|
||||
import { AppConfig } from '@backstage/config';
|
||||
import { loadConfig } from './loader';
|
||||
import mockFs from 'mock-fs';
|
||||
import fs from 'fs-extra';
|
||||
import { rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { resolve as resolvePath, sep } from 'path';
|
||||
|
||||
const root = resolvePath('/');
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('loadConfig', () => {
|
||||
const mockDir = createMockDirectory({
|
||||
content: {
|
||||
'app-config.yaml': `
|
||||
app:
|
||||
title: Example App
|
||||
sessionKey:
|
||||
$file: secrets/session-key.txt
|
||||
escaped: \$\${Escaped}
|
||||
`,
|
||||
'app-config2.yaml': `
|
||||
app:
|
||||
title: Example App 2
|
||||
sessionKey:
|
||||
$file: secrets/session-key.txt
|
||||
escaped: \$\${Escaped}
|
||||
`,
|
||||
'app-config.development.yaml': `
|
||||
app:
|
||||
sessionKey: development-key
|
||||
backend:
|
||||
$include: ./included.yaml
|
||||
other:
|
||||
$include: secrets/included.yaml
|
||||
`,
|
||||
'secrets/session-key.txt': 'abc123',
|
||||
'secrets/included.yaml': `
|
||||
secret:
|
||||
$file: session-key.txt
|
||||
`,
|
||||
'included.yaml': `
|
||||
foo:
|
||||
bar: token \${MY_SECRET}
|
||||
`,
|
||||
'app-config.substitute.yaml': `
|
||||
app:
|
||||
someConfig:
|
||||
$include: \${SUBSTITUTE_ME}.yaml
|
||||
noSubstitute:
|
||||
$file: \$\${ESCAPE_ME}.txt
|
||||
`,
|
||||
'substituted.yaml': `
|
||||
secret:
|
||||
$file: secrets/\${SUBSTITUTE_ME}.txt
|
||||
`,
|
||||
'secrets/substituted.txt': '123abc',
|
||||
'${ESCAPE_ME}.txt': 'notSubstituted',
|
||||
'empty.yaml': '# just a comment',
|
||||
},
|
||||
});
|
||||
|
||||
const server = setupServer();
|
||||
const initialLoaderHandler = rest.get(
|
||||
`https://some.domain.io/app-config.yaml`,
|
||||
@@ -61,58 +108,9 @@ describe('loadConfig', () => {
|
||||
beforeEach(() => {
|
||||
process.env.MY_SECRET = 'is-secret';
|
||||
process.env.SUBSTITUTE_ME = 'substituted';
|
||||
|
||||
mockFs({
|
||||
'/root/app-config.yaml': `
|
||||
app:
|
||||
title: Example App
|
||||
sessionKey:
|
||||
$file: secrets/session-key.txt
|
||||
escaped: \$\${Escaped}
|
||||
`,
|
||||
'/root/app-config2.yaml': `
|
||||
app:
|
||||
title: Example App 2
|
||||
sessionKey:
|
||||
$file: secrets/session-key.txt
|
||||
escaped: \$\${Escaped}
|
||||
`,
|
||||
'/root/app-config.development.yaml': `
|
||||
app:
|
||||
sessionKey: development-key
|
||||
backend:
|
||||
$include: ./included.yaml
|
||||
other:
|
||||
$include: secrets/included.yaml
|
||||
`,
|
||||
'/root/secrets/session-key.txt': 'abc123',
|
||||
'/root/secrets/included.yaml': `
|
||||
secret:
|
||||
$file: session-key.txt
|
||||
`,
|
||||
'/root/included.yaml': `
|
||||
foo:
|
||||
bar: token \${MY_SECRET}
|
||||
`,
|
||||
'/root/app-config.substitute.yaml': `
|
||||
app:
|
||||
someConfig:
|
||||
$include: \${SUBSTITUTE_ME}.yaml
|
||||
noSubstitute:
|
||||
$file: \$\${ESCAPE_ME}.txt
|
||||
`,
|
||||
'/root/substituted.yaml': `
|
||||
secret:
|
||||
$file: secrets/\${SUBSTITUTE_ME}.txt
|
||||
`,
|
||||
'/root/secrets/substituted.txt': '123abc',
|
||||
'/root/${ESCAPE_ME}.txt': 'notSubstituted',
|
||||
'/root/empty.yaml': '# just a comment',
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
server.resetHandlers();
|
||||
});
|
||||
|
||||
@@ -121,7 +119,7 @@ describe('loadConfig', () => {
|
||||
it('load config from default path', async () => {
|
||||
await expect(
|
||||
loadConfig({
|
||||
configRoot: '/root',
|
||||
configRoot: mockDir.path,
|
||||
configTargets: [],
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
@@ -135,7 +133,7 @@ describe('loadConfig', () => {
|
||||
escaped: '${Escaped}',
|
||||
},
|
||||
},
|
||||
path: `${root}root${sep}app-config.yaml`,
|
||||
path: mockDir.resolve('app-config.yaml'),
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -148,7 +146,7 @@ describe('loadConfig', () => {
|
||||
|
||||
await expect(
|
||||
loadConfig({
|
||||
configRoot: '/root',
|
||||
configRoot: mockDir.path,
|
||||
configTargets: [{ url: configUrl }],
|
||||
remote: {
|
||||
reloadIntervalSeconds: 30,
|
||||
@@ -173,10 +171,10 @@ describe('loadConfig', () => {
|
||||
it('loads config with secrets from two different files', async () => {
|
||||
await expect(
|
||||
loadConfig({
|
||||
configRoot: '/root',
|
||||
configRoot: mockDir.path,
|
||||
configTargets: [
|
||||
{ path: '/root/app-config.yaml' },
|
||||
{ path: '/root/app-config2.yaml' },
|
||||
{ path: mockDir.resolve('app-config.yaml') },
|
||||
{ path: mockDir.resolve('app-config2.yaml') },
|
||||
],
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
@@ -190,7 +188,7 @@ describe('loadConfig', () => {
|
||||
escaped: '${Escaped}',
|
||||
},
|
||||
},
|
||||
path: '/root/app-config.yaml',
|
||||
path: mockDir.resolve('app-config.yaml'),
|
||||
},
|
||||
{
|
||||
context: 'app-config2.yaml',
|
||||
@@ -201,7 +199,7 @@ describe('loadConfig', () => {
|
||||
escaped: '${Escaped}',
|
||||
},
|
||||
},
|
||||
path: '/root/app-config2.yaml',
|
||||
path: mockDir.resolve('app-config2.yaml'),
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -210,8 +208,8 @@ describe('loadConfig', () => {
|
||||
it('loads config with secrets from single file', async () => {
|
||||
await expect(
|
||||
loadConfig({
|
||||
configRoot: '/root',
|
||||
configTargets: [{ path: '/root/app-config.yaml' }],
|
||||
configRoot: mockDir.path,
|
||||
configTargets: [{ path: mockDir.resolve('app-config.yaml') }],
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
appConfigs: [
|
||||
@@ -224,7 +222,7 @@ describe('loadConfig', () => {
|
||||
escaped: '${Escaped}',
|
||||
},
|
||||
},
|
||||
path: '/root/app-config.yaml',
|
||||
path: mockDir.resolve('app-config.yaml'),
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -233,10 +231,10 @@ describe('loadConfig', () => {
|
||||
it('loads development config with secrets', async () => {
|
||||
await expect(
|
||||
loadConfig({
|
||||
configRoot: '/root',
|
||||
configRoot: mockDir.path,
|
||||
configTargets: [
|
||||
{ path: '/root/app-config.yaml' },
|
||||
{ path: '/root/app-config.development.yaml' },
|
||||
{ path: mockDir.resolve('app-config.yaml') },
|
||||
{ path: mockDir.resolve('app-config.development.yaml') },
|
||||
],
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
@@ -250,7 +248,7 @@ describe('loadConfig', () => {
|
||||
escaped: '${Escaped}',
|
||||
},
|
||||
},
|
||||
path: '/root/app-config.yaml',
|
||||
path: mockDir.resolve('app-config.yaml'),
|
||||
},
|
||||
{
|
||||
context: 'app-config.development.yaml',
|
||||
@@ -267,7 +265,7 @@ describe('loadConfig', () => {
|
||||
secret: 'abc123',
|
||||
},
|
||||
},
|
||||
path: '/root/app-config.development.yaml',
|
||||
path: mockDir.resolve('app-config.development.yaml'),
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -276,8 +274,10 @@ describe('loadConfig', () => {
|
||||
it('loads deep substituted config', async () => {
|
||||
await expect(
|
||||
loadConfig({
|
||||
configRoot: '/root',
|
||||
configTargets: [{ path: '/root/app-config.substitute.yaml' }],
|
||||
configRoot: mockDir.path,
|
||||
configTargets: [
|
||||
{ path: mockDir.resolve('app-config.substitute.yaml') },
|
||||
],
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
appConfigs: [
|
||||
@@ -291,7 +291,7 @@ describe('loadConfig', () => {
|
||||
noSubstitute: 'notSubstituted',
|
||||
},
|
||||
},
|
||||
path: '/root/app-config.substitute.yaml',
|
||||
path: mockDir.resolve('app-config.substitute.yaml'),
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -303,7 +303,7 @@ describe('loadConfig', () => {
|
||||
|
||||
await expect(
|
||||
loadConfig({
|
||||
configRoot: '/root',
|
||||
configRoot: mockDir.path,
|
||||
configTargets: [],
|
||||
watch: {
|
||||
onChange: onChange.resolve,
|
||||
@@ -321,12 +321,12 @@ describe('loadConfig', () => {
|
||||
escaped: '${Escaped}',
|
||||
},
|
||||
},
|
||||
path: `${root}root${sep}app-config.yaml`,
|
||||
path: mockDir.resolve('app-config.yaml'),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await fs.writeJson('/root/app-config.yaml', {
|
||||
await fs.writeJson(mockDir.resolve('app-config.yaml'), {
|
||||
app: {
|
||||
title: 'New Title',
|
||||
},
|
||||
@@ -339,7 +339,7 @@ describe('loadConfig', () => {
|
||||
title: 'New Title',
|
||||
},
|
||||
},
|
||||
path: `${root}root${sep}app-config.yaml`,
|
||||
path: mockDir.resolve('app-config.yaml'),
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -352,8 +352,10 @@ describe('loadConfig', () => {
|
||||
|
||||
await expect(
|
||||
loadConfig({
|
||||
configRoot: '/root',
|
||||
configTargets: [{ path: '/root/app-config.development.yaml' }],
|
||||
configRoot: mockDir.path,
|
||||
configTargets: [
|
||||
{ path: mockDir.resolve('app-config.development.yaml') },
|
||||
],
|
||||
watch: {
|
||||
onChange: onChange.resolve,
|
||||
stopSignal: stopSignal.promise,
|
||||
@@ -376,14 +378,14 @@ describe('loadConfig', () => {
|
||||
secret: 'abc123',
|
||||
},
|
||||
},
|
||||
path: '/root/app-config.development.yaml',
|
||||
path: mockDir.resolve('app-config.development.yaml'),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// session-key is indirectly included in app-config.development.yaml
|
||||
// via included.yaml
|
||||
await fs.writeFile('/root/secrets/session-key.txt', 'abc234');
|
||||
await fs.writeFile(mockDir.resolve('secrets/session-key.txt'), 'abc234');
|
||||
|
||||
await expect(onChange.promise).resolves.toEqual([
|
||||
{
|
||||
@@ -401,7 +403,7 @@ describe('loadConfig', () => {
|
||||
secret: 'abc234',
|
||||
},
|
||||
},
|
||||
path: '/root/app-config.development.yaml',
|
||||
path: mockDir.resolve('app-config.development.yaml'),
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -417,7 +419,7 @@ describe('loadConfig', () => {
|
||||
const configUrl = 'https://some.domain.io/app-config.yaml';
|
||||
await expect(
|
||||
loadConfig({
|
||||
configRoot: '/root',
|
||||
configRoot: mockDir.path,
|
||||
configTargets: [{ url: configUrl }],
|
||||
watch: {
|
||||
onChange: onChange.resolve,
|
||||
@@ -464,7 +466,7 @@ describe('loadConfig', () => {
|
||||
const stopSignal = defer<void>();
|
||||
|
||||
await loadConfig({
|
||||
configRoot: '/root',
|
||||
configRoot: mockDir.path,
|
||||
configTargets: [],
|
||||
watch: {
|
||||
onChange: () => {
|
||||
@@ -476,7 +478,7 @@ describe('loadConfig', () => {
|
||||
|
||||
stopSignal.resolve();
|
||||
|
||||
await fs.writeJson('/root/app-config.yaml', {
|
||||
await fs.writeJson(mockDir.resolve('app-config.yaml'), {
|
||||
app: {
|
||||
title: 'New Title',
|
||||
},
|
||||
@@ -487,8 +489,8 @@ describe('loadConfig', () => {
|
||||
it('handles empty files gracefully', async () => {
|
||||
await expect(
|
||||
loadConfig({
|
||||
configRoot: '/root',
|
||||
configTargets: [{ path: '/root/empty.yaml' }],
|
||||
configRoot: mockDir.path,
|
||||
configTargets: [{ path: mockDir.resolve('empty.yaml') }],
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
appConfigs: [],
|
||||
|
||||
@@ -14,10 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import mockFs from 'mock-fs';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
import { collectConfigSchemas } from './collect';
|
||||
import path from 'path';
|
||||
|
||||
// cwd must be restored
|
||||
const origDir = process.cwd();
|
||||
afterAll(() => {
|
||||
process.chdir(origDir);
|
||||
});
|
||||
|
||||
const mockSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -28,25 +34,15 @@ const mockSchema = {
|
||||
},
|
||||
};
|
||||
|
||||
// Gotta make sure this is in the compiler cache before we start mocking the filesystem
|
||||
require('typescript-json-schema');
|
||||
|
||||
// We need to load in actual TS libraries when using mock-fs.
|
||||
// This lookup is to allow the `typescript` dependency to exist either
|
||||
// at top level or inside node_modules of typescript-json-schema
|
||||
const typescriptModuleDir = path.dirname(
|
||||
require.resolve('typescript/package.json', {
|
||||
paths: [require.resolve('typescript-json-schema')],
|
||||
}),
|
||||
);
|
||||
|
||||
describe('collectConfigSchemas', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
mockDir.clear();
|
||||
});
|
||||
|
||||
it('should not find any schemas without packages', async () => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
'lerna.json': JSON.stringify({
|
||||
packages: ['packages/*'],
|
||||
}),
|
||||
@@ -56,7 +52,7 @@ describe('collectConfigSchemas', () => {
|
||||
});
|
||||
|
||||
it('should find schema in a local package', async () => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
node_modules: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
@@ -66,6 +62,7 @@ describe('collectConfigSchemas', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
process.chdir(mockDir.path);
|
||||
|
||||
await expect(collectConfigSchemas(['a'], [])).resolves.toEqual([
|
||||
{
|
||||
@@ -76,7 +73,7 @@ describe('collectConfigSchemas', () => {
|
||||
});
|
||||
|
||||
it('should find schema at explicit package path', async () => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
root: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'root',
|
||||
@@ -84,6 +81,7 @@ describe('collectConfigSchemas', () => {
|
||||
}),
|
||||
},
|
||||
});
|
||||
process.chdir(mockDir.path);
|
||||
|
||||
await expect(
|
||||
collectConfigSchemas([], [path.join('root', 'package.json')]),
|
||||
@@ -96,7 +94,7 @@ describe('collectConfigSchemas', () => {
|
||||
});
|
||||
|
||||
it('should find schema in transitive dependencies and explicit path', async () => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
root: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'root',
|
||||
@@ -152,6 +150,7 @@ describe('collectConfigSchemas', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
process.chdir(mockDir.path);
|
||||
|
||||
await expect(
|
||||
collectConfigSchemas(['a'], [path.join('root', 'package.json')]),
|
||||
@@ -178,7 +177,7 @@ describe('collectConfigSchemas', () => {
|
||||
});
|
||||
|
||||
it('should schema of different types', async () => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
node_modules: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
@@ -198,15 +197,16 @@ describe('collectConfigSchemas', () => {
|
||||
name: 'c',
|
||||
configSchema: 'schema.d.ts',
|
||||
}),
|
||||
'schema.d.ts': `export interface Config {
|
||||
'schema.d.ts': `
|
||||
export interface Config {
|
||||
/** @visibility secret */
|
||||
tsKey: string
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
},
|
||||
},
|
||||
// TypeScript compilation needs to load some real files inside the typescript dir
|
||||
[typescriptModuleDir]: (mockFs as any).load(typescriptModuleDir),
|
||||
});
|
||||
process.chdir(mockDir.path);
|
||||
|
||||
await expect(collectConfigSchemas(['a', 'b', 'c'], [])).resolves.toEqual([
|
||||
{
|
||||
@@ -235,7 +235,7 @@ describe('collectConfigSchemas', () => {
|
||||
});
|
||||
|
||||
it('should load schema from different package versions', async () => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
node_modules: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
@@ -275,6 +275,7 @@ describe('collectConfigSchemas', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
process.chdir(mockDir.path);
|
||||
|
||||
await expect(collectConfigSchemas(['a'], [])).resolves.toEqual([
|
||||
{
|
||||
@@ -303,7 +304,7 @@ describe('collectConfigSchemas', () => {
|
||||
});
|
||||
|
||||
it('should not allow unknown schema file types', async () => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
node_modules: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
@@ -314,6 +315,7 @@ describe('collectConfigSchemas', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
process.chdir(mockDir.path);
|
||||
|
||||
await expect(collectConfigSchemas(['a'], [])).rejects.toThrow(
|
||||
'Config schema files must be .json or .d.ts, got schema.yaml',
|
||||
@@ -321,7 +323,7 @@ describe('collectConfigSchemas', () => {
|
||||
});
|
||||
|
||||
it('should reject typescript config declaration without a Config type', async () => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
node_modules: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
@@ -331,9 +333,8 @@ describe('collectConfigSchemas', () => {
|
||||
'schema.d.ts': `export interface NotConfig {}`,
|
||||
},
|
||||
},
|
||||
// TypeScript compilation needs to load some real files inside the typescript dir
|
||||
[typescriptModuleDir]: (mockFs as any).load(typescriptModuleDir),
|
||||
});
|
||||
process.chdir(mockDir.path);
|
||||
|
||||
await expect(collectConfigSchemas(['a'], [])).rejects.toThrow(
|
||||
`Invalid schema in ${path.join(
|
||||
|
||||
@@ -14,16 +14,24 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import mockFs from 'mock-fs';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
import { loadConfigSchema } from './load';
|
||||
|
||||
// cwd must be restored
|
||||
const origDir = process.cwd();
|
||||
afterAll(() => {
|
||||
process.chdir(origDir);
|
||||
});
|
||||
|
||||
describe('loadConfigSchema', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
mockDir.clear();
|
||||
});
|
||||
|
||||
it('should load schema from packages or data', async () => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
node_modules: {
|
||||
a: {
|
||||
'package.json': JSON.stringify({
|
||||
@@ -53,6 +61,7 @@ describe('loadConfigSchema', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
process.chdir(mockDir.path);
|
||||
|
||||
const schema = await loadConfigSchema({
|
||||
dependencies: ['a'],
|
||||
@@ -119,7 +128,7 @@ describe('loadConfigSchema', () => {
|
||||
|
||||
describe('should consider schema', () => {
|
||||
it('when filtering simple config', async () => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
'package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
configSchema: {
|
||||
@@ -131,6 +140,7 @@ describe('loadConfigSchema', () => {
|
||||
},
|
||||
}),
|
||||
});
|
||||
process.chdir(mockDir.path);
|
||||
|
||||
const schema = await loadConfigSchema({
|
||||
packagePaths: ['package.json'],
|
||||
@@ -156,7 +166,7 @@ describe('loadConfigSchema', () => {
|
||||
});
|
||||
|
||||
it('when filtering nested config', async () => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
'package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
configSchema: {
|
||||
@@ -185,6 +195,7 @@ describe('loadConfigSchema', () => {
|
||||
},
|
||||
}),
|
||||
});
|
||||
process.chdir(mockDir.path);
|
||||
|
||||
const schema = await loadConfigSchema({
|
||||
packagePaths: ['package.json'],
|
||||
@@ -244,7 +255,7 @@ describe('loadConfigSchema', () => {
|
||||
});
|
||||
|
||||
it('when filtering config with required values', async () => {
|
||||
mockFs({
|
||||
mockDir.setContent({
|
||||
'package.json': JSON.stringify({
|
||||
name: 'a',
|
||||
configSchema: {
|
||||
@@ -261,6 +272,7 @@ describe('loadConfigSchema', () => {
|
||||
},
|
||||
}),
|
||||
});
|
||||
process.chdir(mockDir.path);
|
||||
|
||||
const schema = await loadConfigSchema({
|
||||
packagePaths: ['package.json'],
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"recursive-readdir": "^2.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-test-utils": "workspace:^",
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@types/command-exists": "^1.2.0",
|
||||
"@types/fs-extra": "^9.0.1",
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
import mockFs from 'mock-fs';
|
||||
import path from 'path';
|
||||
import { Command } from 'commander';
|
||||
import * as tasks from './lib/tasks';
|
||||
import createApp from './createApp';
|
||||
import { findPaths } from '@backstage/cli-common';
|
||||
import { tmpdir } from 'os';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
jest.mock('./lib/tasks');
|
||||
|
||||
@@ -40,16 +40,7 @@ const moveAppMock = jest.spyOn(tasks, 'moveAppTask');
|
||||
const buildAppMock = jest.spyOn(tasks, 'buildAppTask');
|
||||
|
||||
describe('command entrypoint', () => {
|
||||
beforeEach(() => {
|
||||
mockFs({
|
||||
[`${__dirname}/package.json`]: '', // required by `findPaths(__dirname)`
|
||||
'templates/': mockFs.load(path.resolve(__dirname, '../templates/')),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
const mockDir = createMockDirectory({ mockOsTmpDir: true });
|
||||
|
||||
beforeEach(() => {
|
||||
promptMock.mockResolvedValueOnce({
|
||||
@@ -62,6 +53,7 @@ describe('command entrypoint', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockDir.clear();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
@@ -75,7 +67,6 @@ describe('command entrypoint', () => {
|
||||
findPaths(__dirname).resolveTarget(
|
||||
'packages',
|
||||
'create-app',
|
||||
'src',
|
||||
'templates',
|
||||
'default-app',
|
||||
),
|
||||
@@ -97,7 +88,6 @@ describe('command entrypoint', () => {
|
||||
findPaths(__dirname).resolveTarget(
|
||||
'packages',
|
||||
'create-app',
|
||||
'src',
|
||||
'templates',
|
||||
'default-app',
|
||||
),
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import child_process from 'child_process';
|
||||
import path, { resolve as resolvePath } from 'path';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import os from 'os';
|
||||
import {
|
||||
Task,
|
||||
@@ -29,6 +28,7 @@ import {
|
||||
tryInitGitRepository,
|
||||
readGitConfig,
|
||||
} from './tasks';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
jest.spyOn(Task, 'log').mockReturnValue(undefined);
|
||||
jest.spyOn(Task, 'error').mockReturnValue(undefined);
|
||||
@@ -101,21 +101,33 @@ describe('tasks', () => {
|
||||
) => void
|
||||
>;
|
||||
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
const realChdir = process.chdir;
|
||||
// If anyone calls chdir then make it resolve within the tmpdir
|
||||
const mockChdir = jest.spyOn(process, 'chdir');
|
||||
|
||||
beforeEach(() => {
|
||||
mockFs({
|
||||
'projects/my-module.ts': '',
|
||||
'projects/dir/my-file.txt': '',
|
||||
'tmp/mockApp/.gitignore': '',
|
||||
'tmp/mockApp/package.json': '',
|
||||
'tmp/mockApp/packages/app/package.json': '',
|
||||
// load templates into mock filesystem
|
||||
'templates/': mockFs.load(path.resolve(__dirname, '../../templates/')),
|
||||
mockDir.setContent({
|
||||
projects: {
|
||||
'my-module.ts': '',
|
||||
'dir/my-file.txt': '',
|
||||
},
|
||||
'tmp/mockApp': {
|
||||
'.gitignore': '',
|
||||
'package.json': '',
|
||||
'packages/app/package.json': '',
|
||||
},
|
||||
});
|
||||
realChdir(mockDir.path);
|
||||
mockChdir.mockImplementation((dir: string) =>
|
||||
realChdir(mockDir.resolve(dir)),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockExec.mockRestore();
|
||||
mockFs.restore();
|
||||
mockChdir.mockReset();
|
||||
});
|
||||
|
||||
describe('checkAppExistsTask', () => {
|
||||
@@ -164,8 +176,6 @@ describe('tasks', () => {
|
||||
|
||||
describe('buildAppTask', () => {
|
||||
it('should change to `appDir` and run `yarn install` and `yarn tsc`', async () => {
|
||||
const mockChdir = jest.spyOn(process, 'chdir');
|
||||
|
||||
// requires callback implementation to support `promisify` wrapper
|
||||
// https://stackoverflow.com/a/60579617/10044859
|
||||
mockExec.mockImplementation((_command, callback) => {
|
||||
@@ -199,8 +209,6 @@ describe('tasks', () => {
|
||||
});
|
||||
|
||||
it('should error out on incorrect yarn version', async () => {
|
||||
const mockChdir = jest.spyOn(process, 'chdir');
|
||||
|
||||
// requires callback implementation to support `promisify` wrapper
|
||||
// https://stackoverflow.com/a/60579617/10044859
|
||||
mockExec.mockImplementation((_command, callback) => {
|
||||
@@ -265,7 +273,7 @@ describe('tasks', () => {
|
||||
|
||||
describe('templatingTask', () => {
|
||||
it('should generate a project populating context parameters', async () => {
|
||||
const templateDir = 'templates/default-app';
|
||||
const templateDir = resolvePath(__dirname, '../../templates/default-app');
|
||||
const destinationDir = 'templatedApp';
|
||||
const context = {
|
||||
name: 'SuperCoolBackstageInstance',
|
||||
|
||||
@@ -3415,7 +3415,6 @@ __metadata:
|
||||
logform: ^2.3.2
|
||||
minimatch: ^5.0.0
|
||||
minimist: ^1.2.5
|
||||
mock-fs: ^5.2.0
|
||||
morgan: ^1.10.0
|
||||
node-forge: ^1.3.1
|
||||
selfsigned: ^2.0.0
|
||||
@@ -3913,7 +3912,6 @@ __metadata:
|
||||
json-schema-traverse: ^1.0.0
|
||||
lodash: ^4.17.21
|
||||
minimist: ^1.2.5
|
||||
mock-fs: ^5.2.0
|
||||
msw: ^1.0.0
|
||||
node-fetch: ^2.6.7
|
||||
typescript-json-schema: ^0.55.0
|
||||
@@ -4207,6 +4205,7 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/create-app@workspace:packages/create-app"
|
||||
dependencies:
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/cli-common": "workspace:^"
|
||||
"@types/command-exists": ^1.2.0
|
||||
|
||||
Reference in New Issue
Block a user