Merge pull request #3016 from ayshiff/feat/cli-test-mock-fs

[CLI - refactor]: Temporary files use inside tests
This commit is contained in:
Fredrik Adelöw
2020-10-22 14:38:35 +02:00
committed by GitHub
5 changed files with 196 additions and 147 deletions
+1
View File
@@ -114,6 +114,7 @@
"@types/http-proxy": "^1.17.4",
"@types/inquirer": "^7.3.1",
"@types/mini-css-extract-plugin": "^0.9.1",
"@types/mock-fs": "^4.13.0",
"@types/node": "^13.7.2",
"@types/ora": "^3.2.0",
"@types/react-dev-utils": "^9.0.4",
@@ -16,14 +16,20 @@
import fs from 'fs-extra';
import path from 'path';
import mockFs from 'mock-fs';
import os from 'os';
import del from 'del';
import { createTemporaryPluginFolder, movePlugin } from './createPlugin';
const id = 'testPluginMock';
describe('createPlugin', () => {
afterAll(() => {
mockFs.restore();
});
describe('createPluginFolder', () => {
it('should create a temporary plugin directory in the correct place', async () => {
const id = 'testPlugin';
const tempDir = path.join(os.tmpdir(), id);
try {
await createTemporaryPluginFolder(tempDir);
@@ -35,35 +41,27 @@ describe('createPlugin', () => {
});
it('should not create a temporary plugin directory if it already exists', async () => {
const id = 'testPlugin';
const tempDir = path.join(os.tmpdir(), id);
try {
await createTemporaryPluginFolder(tempDir);
await expect(fs.pathExists(tempDir)).resolves.toBe(true);
await expect(createTemporaryPluginFolder(tempDir)).rejects.toThrow(
/Failed to create temporary plugin directory/,
);
} finally {
await del(tempDir, { force: true });
}
mockFs({
[id]: {},
});
await expect(createTemporaryPluginFolder(id)).rejects.toThrow(
/Failed to create temporary plugin directory/,
);
});
});
describe('movePlugin', () => {
it('should move the temporary plugin directory to its final place', async () => {
const id = 'testPlugin';
const tempDir = path.join(os.tmpdir(), id);
const rootDir = await fs.mkdtemp(path.join(os.tmpdir(), 'test-'));
const pluginDir = path.join(rootDir, 'plugins', id);
try {
await createTemporaryPluginFolder(tempDir);
await movePlugin(tempDir, pluginDir, id);
await expect(fs.pathExists(pluginDir)).resolves.toBe(true);
expect(pluginDir).toMatch(path.join('', 'plugins', id));
} finally {
await del(tempDir, { force: true });
await del(rootDir, { force: true });
}
mockFs({
[id]: {},
});
const tempDir = id;
const pluginDir = `/test-temp/plugins/${id}`;
await movePlugin(tempDir, pluginDir, id);
await expect(fs.pathExists(pluginDir)).resolves.toBe(true);
expect(pluginDir).toMatch(path.join('', 'plugins', id));
});
});
});
@@ -16,13 +16,9 @@
import fse from 'fs-extra';
import path from 'path';
import os from 'os';
import mockFs from 'mock-fs';
import { paths } from '../../lib/paths';
import {
addExportStatement,
capitalize,
createTemporaryPluginFolder,
} from '../create-plugin/createPlugin';
import { addExportStatement, capitalize } from '../create-plugin/createPlugin';
import { addCodeownersEntry } from '../../lib/codeowners';
import {
removeReferencesFromAppPackage,
@@ -35,7 +31,7 @@ import {
const BACKSTAGE = `@backstage`;
const testPluginName = 'yarn-test-package';
const testPluginPackage = `${BACKSTAGE}/plugin-${testPluginName}`;
const tempDir = path.join(os.tmpdir(), 'remove-plugin-test');
const tempDir = '/remove-plugin-test';
const removeEmptyLines = (file: string): string =>
file.split(/\r?\n/).filter(Boolean).join('\n');
@@ -47,13 +43,24 @@ const createTestPackageFile = async (
// Copy contents of package file for test
const packageFileContent = JSON.parse(fse.readFileSync(packageFile, 'utf8'));
packageFileContent.dependencies[testPluginPackage] = '0.1.0';
fse.createFileSync(testFilePath);
fse.writeFileSync(
testFilePath,
`${JSON.stringify(packageFileContent, null, 2)}\n`,
'utf8',
);
const testFileContent = {
...packageFileContent,
dependencies: {
...packageFileContent.dependencies,
[testPluginPackage]: '0.1.0',
},
};
mockFs({
'/packages': {
app: {
'package.json': `${JSON.stringify(packageFileContent, null, 2)}\n`,
},
},
[tempDir]: {
[testFilePath]: `${JSON.stringify(testFileContent, null, 2)}\n`,
},
});
return;
};
@@ -62,93 +69,126 @@ const createTestPluginFile = async (
pluginsFilePath: string,
) => {
// Copy contents of package file for test
fse.copyFileSync(pluginsFilePath, testFilePath);
const pluginsFileContent = fse.readFileSync(pluginsFilePath);
mockFs({
[tempDir]: {
[testFilePath]: `${pluginsFileContent}\n`,
[pluginsFilePath]: `${pluginsFileContent}\n`,
},
'/packages': {
app: {
src: {
'plugin.ts': `${pluginsFileContent}\n`,
},
},
},
});
const pluginNameCapitalized = testPluginName
.split('-')
.map(name => capitalize(name))
.join('');
const exportStatement = `export { plugin as ${pluginNameCapitalized}} from @backstage/plugin-${testPluginName}`;
await addExportStatement(testFilePath, exportStatement);
const exportStatement = `export { default as ${pluginNameCapitalized}} from @backstage/plugin-${testPluginName}`;
await addExportStatement(path.join(tempDir, testFilePath), exportStatement);
};
const mkTestPluginDir = (testDirPath: string) => {
fse.mkdirSync(testDirPath);
for (let i = 0; i < 50; i++)
fse.createFileSync(path.join(testDirPath, `testFile${i}.ts`));
const dirPath = `/${testDirPath}`;
const pluginFiles: { [index: number]: string } = {};
for (let i = 0; i < 50; i++) {
pluginFiles[i] = '';
}
mockFs({
[dirPath]: pluginFiles,
});
};
describe('removePlugin', () => {
beforeAll(() => {
beforeEach(() => {
// Create temporary directory for all tests
createTemporaryPluginFolder(tempDir);
const appPath = paths.resolveTargetRoot('packages', 'app');
mockFs({
[tempDir]: {
'package.json': mockFs.load(path.join(appPath, 'package.json')),
src: {
'plugin.ts': mockFs.load(path.join(appPath, 'src', 'plugins.ts')),
},
},
});
});
afterAll(() => {
// Remove temporary directory
fse.removeSync(tempDir);
mockFs.restore();
});
describe('Remove Plugin Dependencies', () => {
const appPath = paths.resolveTargetRoot('packages', 'app');
const githubDir = paths.resolveTargetRoot('.github');
it('removes plugin references from /packages/app/package.json', async () => {
// Set up test
const packageFilePath = path.join(appPath, 'package.json');
const testFilePath = path.join(tempDir, 'test.json');
const packageFilePath = path.join(tempDir, 'package.json');
const testFilePath = 'test.json';
createTestPackageFile(testFilePath, packageFilePath);
try {
await removeReferencesFromAppPackage(testFilePath, testPluginName);
const testFileContent = removeEmptyLines(
fse.readFileSync(testFilePath, 'utf8'),
);
const packageFileContent = removeEmptyLines(
fse.readFileSync(packageFilePath, 'utf8'),
);
expect(testFileContent).toBe(packageFileContent);
} finally {
fse.removeSync(testFilePath);
}
});
await removeReferencesFromAppPackage(
path.join(tempDir, testFilePath),
testPluginName,
);
const testFileContent = removeEmptyLines(
fse.readFileSync(path.join(tempDir, testFilePath), 'utf8'),
);
it('removes plugin exports from /packages/app/src/package.json', async () => {
const testFilePath = path.join(tempDir, 'test.ts');
const pluginsFilePaths = path.join(appPath, 'src', 'plugins.ts');
await createTestPluginFile(testFilePath, pluginsFilePaths);
try {
await removeReferencesFromPluginsFile(testFilePath, testPluginName);
const testFileContent = removeEmptyLines(
fse.readFileSync(testFilePath, 'utf8'),
);
const pluginsFileContent = removeEmptyLines(
fse.readFileSync(pluginsFilePaths, 'utf8'),
);
expect(testFileContent).toBe(pluginsFileContent);
} finally {
fse.removeSync(testFilePath);
}
const packageFileContent = removeEmptyLines(
fse.readFileSync('/packages/app/package.json', 'utf8'),
);
expect(testFileContent).toBe(packageFileContent);
});
it('removes plugin exports from /packages/app/src/packacge.json', async () => {
const testFilePath = 'test.ts';
const pluginsFilePaths = path.join(tempDir, 'src/plugin.ts');
createTestPluginFile(testFilePath, pluginsFilePaths);
await removeReferencesFromPluginsFile(
path.join(tempDir, testFilePath),
testPluginName,
);
const testFileContent = removeEmptyLines(
fse.readFileSync(path.join(tempDir, testFilePath), 'utf8'),
);
const pluginsFileContent = removeEmptyLines(
fse.readFileSync('/packages/app/src/plugin.ts', 'utf8'),
);
expect(testFileContent).toBe(pluginsFileContent);
});
it('removes codeOwners references', async () => {
const testFilePath = path.join(tempDir, 'test');
const testFileName = 'test';
const testFilePath = path.join(tempDir, testFileName);
const codeownersPath = path.join(githubDir, 'CODEOWNERS');
try {
fse.copySync(codeownersPath, testFilePath);
const testFileContent = removeEmptyLines(
fse.readFileSync(testFilePath, 'utf8'),
);
const codeOwnersFileContent = removeEmptyLines(
fse.readFileSync(codeownersPath, 'utf8'),
);
await addCodeownersEntry(testFilePath!, `/plugins/${testPluginName}`, [
'@thisIsAtestTeam',
'test@gmail.com',
]);
await removePluginFromCodeOwners(testFilePath, testPluginName);
expect(testFileContent).toBe(codeOwnersFileContent);
} finally {
if (fse.existsSync(testFilePath)) fse.removeSync(testFilePath);
}
const mockedCodeownersPath = '/.github/CODEOWNERS';
mockFs({
[tempDir]: {
[testFileName]: '',
},
'/.github': {
CODEOWNERS: mockFs.load(codeownersPath),
},
});
fse.copySync(mockedCodeownersPath, testFilePath);
const testFileContent = removeEmptyLines(
fse.readFileSync(testFilePath, 'utf8'),
);
const codeOwnersFileContent = removeEmptyLines(
fse.readFileSync(mockedCodeownersPath, 'utf8'),
);
await addCodeownersEntry(testFilePath!, `/plugins/${testPluginName}`, [
'@thisIsAtestTeam',
'test@gmail.com',
]);
await removePluginFromCodeOwners(testFilePath, testPluginName);
expect(testFileContent).toBe(codeOwnersFileContent);
});
});
@@ -161,34 +201,35 @@ describe('removePlugin', () => {
describe('Removes Plugin Directory', () => {
it('removes plugin directory from /plugins', async () => {
try {
mkTestPluginDir(testDirPath);
expect(fse.existsSync(testDirPath)).toBeTruthy();
await removePluginDirectory(testDirPath);
expect(fse.existsSync(testDirPath)).toBeFalsy();
} finally {
if (fse.existsSync(testDirPath)) fse.removeSync(testDirPath);
}
mkTestPluginDir(testDirPath);
expect(fse.existsSync(testDirPath)).toBeTruthy();
await removePluginDirectory(testDirPath);
expect(fse.existsSync(testDirPath)).toBeFalsy();
});
});
describe('Removes System Link', () => {
it('removes system link from @backstage', async () => {
const scopedDir = paths.resolveTargetRoot('node_modules', '@backstage');
const testSymLinkPath = path.join(
scopedDir,
`plugin-${testPluginName}`,
);
try {
mkTestPluginDir(testDirPath);
fse.ensureSymlinkSync(testSymLinkPath, testDirPath);
const symLink = `plugin-${testPluginName}`;
const testSymLinkPath = `/node_modules/@backstage/${symLink}`;
const mockedTestDirPath = path.join('/plugins', testPluginName);
await removeSymLink(testSymLinkPath);
expect(fse.existsSync(testSymLinkPath)).toBeFalsy();
} finally {
if (fse.existsSync(testDirPath)) fse.removeSync(testDirPath);
if (fse.existsSync(testSymLinkPath)) fse.removeSync(testSymLinkPath);
}
mockFs({
'/plugins': {
[testPluginName]: {},
},
'/node_modules': {
'@backstage': {
[symLink]: mockFs.symlink({
path: mockedTestDirPath,
}),
},
},
});
expect(fse.existsSync(testSymLinkPath)).toBeTruthy();
await removeSymLink(testSymLinkPath);
expect(fse.existsSync(testSymLinkPath)).toBeFalsy();
});
});
});
+28 -26
View File
@@ -15,39 +15,41 @@
*/
import fs from 'fs-extra';
import mockFs from 'mock-fs';
import { resolve as resolvePath } from 'path';
import os from 'os';
import del from 'del';
import { templatingTask } from './tasks';
describe('templatingTask', () => {
afterEach(() => {
mockFs.restore();
});
it('should template a directory with mix of regular files and templates', async () => {
// Set up a testing template directory
const tmplDir = await fs.mkdtemp(resolvePath(os.tmpdir(), 'test-'));
await fs.ensureDir(resolvePath(tmplDir, 'sub'));
await fs.writeFile(resolvePath(tmplDir, 'test.txt'), 'testing');
await fs.writeFile(
resolvePath(tmplDir, 'sub/version.txt.hbs'),
'version: {{version}}',
);
// Testing template directory
const tmplDir = 'test-tmpl';
// Set up a temporary dest dir to write the template to
const destDir = await fs.mkdtemp(resolvePath(os.tmpdir(), 'test-'));
// Temporary dest dir to write the template to
const destDir = 'test-dest';
try {
await templatingTask(tmplDir, destDir, {
version: '0.0.0',
});
mockFs({
[tmplDir]: {
sub: {
'version.txt.hbs': 'version: {{version}}',
},
'test.txt': 'testing',
},
[destDir]: {},
});
await expect(
fs.readFile(resolvePath(destDir, 'test.txt'), 'utf8'),
).resolves.toBe('testing');
await expect(
fs.readFile(resolvePath(destDir, 'sub/version.txt'), 'utf8'),
).resolves.toBe('version: 0.0.0');
} finally {
await del(tmplDir, { force: true });
await del(destDir, { force: true });
}
await templatingTask(tmplDir, destDir, {
version: '0.0.0',
});
await expect(
fs.readFile(resolvePath(destDir, 'test.txt'), 'utf8'),
).resolves.toBe('testing');
await expect(
fs.readFile(resolvePath(destDir, 'sub/version.txt'), 'utf8'),
).resolves.toBe('version: 0.0.0');
});
});
+7
View File
@@ -5365,6 +5365,13 @@
dependencies:
"@types/node" "*"
"@types/mock-fs@^4.13.0":
version "4.13.0"
resolved "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.0.tgz#b8b01cd2db588668b2532ecd21b1babd3fffb2c0"
integrity sha512-FUqxhURwqFtFBCuUj3uQMp7rPSQs//b3O9XecAVxhqS9y4/W8SIJEZFq2mmpnFVZBXwR/2OyPLE97CpyYiB8Mw==
dependencies:
"@types/node" "*"
"@types/morgan@^1.9.0":
version "1.9.1"
resolved "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.1.tgz#6457872df95647c1dbc6b3741e8146b71ece74bf"