adding tests to each option

Signed-off-by: Juan Pablo Garcia Ripa <juanpablog@spotify.com>
This commit is contained in:
Juan Pablo Garcia Ripa
2022-12-06 11:12:07 +01:00
parent b35c1770cf
commit e5dc85a821
4 changed files with 571 additions and 35 deletions
@@ -0,0 +1,505 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import mockFs from 'mock-fs';
import { resolve as resolvePath } from 'path';
import * as pathsLib from '../../lib/paths';
import {
buildDocs,
runCliExtraction,
runApiExtraction,
categorizePackageDirs,
} from './api-extractor';
import { buildApiReports } from './api-reports';
import { generateTSC } from './generateTSC';
jest.mock('./generateTSC');
// create mocks for the dependencies of the `buildApiReports` function
jest.mock('./api-extractor', () => ({
createTemporaryTsConfig: jest.fn(),
categorizePackageDirs: jest.fn().mockImplementation(async (p: string[]) => {
console.log('categorizePackageDirs', p);
return {
tsPackageDirs: p,
cliPackageDirs: p,
};
}),
runApiExtraction: jest.fn(),
runCliExtraction: jest.fn(),
buildDocs: jest.fn(),
}));
const paths = pathsLib.paths;
jest.spyOn(paths, 'targetRoot', 'get').mockReturnValue('/root');
jest.spyOn(paths, 'resolveTargetRoot').mockImplementation((...path) => {
return resolvePath('/root', ...path);
});
describe('buildApiReports', () => {
beforeEach(() => {
mockFs({
[paths.targetRoot]: {
'package.json': JSON.stringify({
workspaces: { packages: ['packages/*', 'plugins/*'] },
}),
packages: {
'package-a': {
'package.json': '{}',
},
'package-b': {
'package.json': '{}',
},
'package-c': {},
'README.md': 'Hello World',
},
plugins: {
'plugin-a': {
'package.json': '{}',
},
'plugin-b': {
'package.json': '{}',
},
'plugin-c': {
'package.json': '{}',
},
},
},
});
});
afterEach(() => {
mockFs.restore();
jest.clearAllMocks();
});
it('should run whitout any options', async () => {
const opts = {};
await buildApiReports(opts);
expect(categorizePackageDirs).toHaveBeenCalledWith([
'packages/package-a',
'packages/package-b',
'plugins/plugin-a',
'plugins/plugin-b',
'plugins/plugin-c',
]);
expect(generateTSC).not.toHaveBeenCalled();
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: [
'packages/package-a',
'packages/package-b',
'plugins/plugin-a',
'plugins/plugin-b',
'plugins/plugin-c',
],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: undefined,
omitMessages: [],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
expect(runCliExtraction).toHaveBeenCalledWith({
packageDirs: [
'packages/package-a',
'packages/package-b',
'plugins/plugin-a',
'plugins/plugin-b',
'plugins/plugin-c',
],
isLocalBuild: true,
});
expect(buildDocs).not.toHaveBeenCalled();
});
describe('paths', () => {
it('should generate API reports for one specific package', async () => {
const opts = {
paths: ['packages/package-a'],
};
await buildApiReports(opts);
expect(categorizePackageDirs).toHaveBeenCalledWith([
'packages/package-a',
]);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a'],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: undefined,
omitMessages: [],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
expect(runCliExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a'],
isLocalBuild: true,
});
expect(buildDocs).not.toHaveBeenCalled();
});
it('should generate API reports for multiple specific packages', async () => {
const opts = {
paths: ['packages/package-a', 'packages/package-b'],
};
await buildApiReports(opts);
expect(categorizePackageDirs).toHaveBeenCalledWith([
'packages/package-a',
'packages/package-b',
]);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: undefined,
omitMessages: [],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
expect(runCliExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
isLocalBuild: true,
});
expect(buildDocs).not.toHaveBeenCalled();
});
it('should generate API reports for all packages matching the glob pattern', async () => {
const opts = {
paths: ['packages/*'],
};
await buildApiReports(opts);
expect(categorizePackageDirs).toHaveBeenCalledWith([
'packages/package-a',
'packages/package-b',
]);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: undefined,
omitMessages: [],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
expect(runCliExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
isLocalBuild: true,
});
expect(buildDocs).not.toHaveBeenCalled();
});
it('should generate API reports for all packages matching multiple glob patterns', async () => {
const opts = {
paths: ['packages/*', 'plugins/*a'],
};
await buildApiReports(opts);
expect(categorizePackageDirs).toHaveBeenCalledWith([
'packages/package-a',
'packages/package-b',
'plugins/plugin-a',
]);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: [
'packages/package-a',
'packages/package-b',
'plugins/plugin-a',
],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: undefined,
omitMessages: [],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
expect(runCliExtraction).toHaveBeenCalledWith({
packageDirs: [
'packages/package-a',
'packages/package-b',
'plugins/plugin-a',
],
isLocalBuild: true,
});
expect(buildDocs).not.toHaveBeenCalled();
});
it('should generate API reports for specific packages and glob pattern', async () => {
const opts = {
paths: ['packages/package-a', 'plugins/*'],
};
await buildApiReports(opts);
expect(categorizePackageDirs).toHaveBeenCalledWith([
'packages/package-a',
'plugins/plugin-a',
'plugins/plugin-b',
'plugins/plugin-c',
]);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: [
'packages/package-a',
'plugins/plugin-a',
'plugins/plugin-b',
'plugins/plugin-c',
],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: undefined,
omitMessages: [],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
expect(runCliExtraction).toHaveBeenCalledWith({
packageDirs: [
'packages/package-a',
'plugins/plugin-a',
'plugins/plugin-b',
'plugins/plugin-c',
],
isLocalBuild: true,
});
expect(buildDocs).not.toHaveBeenCalled();
});
});
describe('allowWarnings', () => {
it('should accept boolean values', async () => {
const opts = {
paths: ['packages/*'],
allowWarnings: true,
};
await buildApiReports(opts);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: true,
omitMessages: [],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
});
it('should accept single path value', async () => {
const opts = {
paths: ['packages/*'],
allowWarnings: ['packages/package-a'],
};
await buildApiReports(opts);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: ['packages/package-a'],
omitMessages: [],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
});
it('should accept multiple path values as array', async () => {
const opts = {
paths: ['packages/*'],
allowWarnings: ['packages/package-a', 'packages/package-b'],
};
await buildApiReports(opts);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: ['packages/package-a', 'packages/package-b'],
omitMessages: [],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
});
it('should accept multiple path values as comma separated string', async () => {
const opts = {
paths: ['packages/*'],
allowWarnings: ['packages/package-a,packages/package-b'],
};
await buildApiReports(opts);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: ['packages/package-a', 'packages/package-b'],
omitMessages: [],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
});
it('should accept multiple path values as comma separated string with spaces', async () => {
const opts = {
paths: ['packages/*'],
allowWarnings: ['packages/package-a, packages/package-b'],
};
await buildApiReports(opts);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: ['packages/package-a', 'packages/package-b'],
omitMessages: [],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
});
});
describe('omitMessages', () => {
it('should accept single message value', async () => {
const opts = {
paths: ['packages/*'],
omitMessages: ['ae-missing-release-tag'],
};
await buildApiReports(opts);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: undefined,
omitMessages: ['ae-missing-release-tag'],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
});
it('should accept multiple message values as array', async () => {
const opts = {
paths: ['packages/*'],
omitMessages: ['ae-missing-release-tag', 'ae-missing-annotations'],
};
await buildApiReports(opts);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: undefined,
omitMessages: ['ae-missing-release-tag', 'ae-missing-annotations'],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
});
it('should accept multiple message values as comma separated string', async () => {
const opts = {
paths: ['packages/*'],
omitMessages: ['ae-missing-release-tag,ae-missing-annotations'],
};
await buildApiReports(opts);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: undefined,
omitMessages: ['ae-missing-release-tag', 'ae-missing-annotations'],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
});
it('should accept multiple message values as comma separated string with spaces', async () => {
const opts = {
paths: ['packages/*'],
omitMessages: ['ae-missing-release-tag, ae-missing-annotations'],
};
await buildApiReports(opts);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: undefined,
omitMessages: ['ae-missing-release-tag', 'ae-missing-annotations'],
isLocalBuild: true,
outputDir: '/root/node_modules/.cache/api-extractor',
});
});
});
describe('isCI', () => {
it('should set localBuild to false if CI option is passed', async () => {
const opts = {
paths: ['packages/*'],
ci: true,
};
await buildApiReports(opts);
expect(runApiExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
tsconfigFilePath: '/root/tsconfig.json',
allowWarnings: undefined,
omitMessages: [],
isLocalBuild: false,
outputDir: '/root/node_modules/.cache/api-extractor',
});
expect(runCliExtraction).toHaveBeenCalledWith({
packageDirs: ['packages/package-a', 'packages/package-b'],
isLocalBuild: false,
});
});
});
describe('docs', () => {
it('should run typedoc if docs option is passed', async () => {
const opts = {
paths: ['packages/*'],
docs: true,
};
await buildApiReports(opts);
expect(buildDocs).toHaveBeenCalledWith({
inputDir: '/root/node_modules/.cache/api-extractor',
outputDir: '/root/docs/reference',
});
});
});
describe('tsc', () => {
it('should run tsc if tsc option is passed', async () => {
const opts = {
paths: ['packages/*'],
tsc: true,
};
await buildApiReports(opts);
expect(generateTSC).toHaveBeenCalled();
});
});
});
@@ -16,7 +16,6 @@
import { OptionValues } from 'commander';
import fs from 'fs-extra';
import { spawnSync } from 'child_process';
import {
createTemporaryTsConfig,
categorizePackageDirs,
@@ -25,8 +24,18 @@ import {
buildDocs,
} from './api-extractor';
import { findPackageDirs, paths as cliPaths } from '../../lib/paths';
import { generateTSC } from './generateTSC';
export default async (opts: OptionValues) => {
type Options = {
ci?: boolean;
docs?: boolean;
tsc?: boolean;
paths?: string[];
allowWarnings?: string[] | boolean;
omitMessages?: string[];
} & OptionValues;
export const buildApiReports = async (opts: Options) => {
const tmpDir = cliPaths.resolveTargetRoot(
'./node_modules/.cache/api-extractor',
);
@@ -90,6 +99,7 @@ export default async (opts: OptionValues) => {
});
}
console.log(isDocsBuild);
if (isDocsBuild) {
console.log('# Generating package documentation');
await buildDocs({
@@ -99,37 +109,6 @@ export default async (opts: OptionValues) => {
}
};
/**
* Generates the TypeScript declaration files for the specified project, using the provided `tsconfig.json` file.
*
* Any existing declaration files in the `dist-types` directory will be deleted before generating the new ones.
*
* If the `tsc` command exits with a non-zero exit code, the process will be terminated with the same exit code.
*
* @param tsconfigFilePath {string} The path to the `tsconfig.json` file to use for generating the declaration files.
* @returns {Promise<void>} A promise that resolves when the declaration files have been generated.
*/
export async function generateTSC(tsconfigFilePath: string) {
await fs.remove(cliPaths.resolveTargetRoot('dist-types'));
const { status } = spawnSync(
'yarn',
[
'tsc',
['--project', tsconfigFilePath],
['--skipLibCheck', 'false'],
['--incremental', 'false'],
].flat(),
{
stdio: 'inherit',
shell: true,
cwd: cliPaths.targetRoot,
},
);
if (status !== 0) {
process.exit(status || undefined);
}
}
/**
* Retrieves the list of package names in the "workspaces" field of the `package.json` file in the current workspace root.
*
@@ -173,6 +152,6 @@ function parseArrayOption(value: string[] | boolean | undefined) {
return value;
}
return value?.flatMap((str: string) =>
str.includes(',') ? str.split(',') : str,
str.includes(',') ? str.split(',').map(s => s.trim()) : str,
);
}
@@ -0,0 +1,50 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fs from 'fs-extra';
import { spawnSync } from 'child_process';
import { paths as cliPaths } from '../../lib/paths';
/**
* Generates the TypeScript declaration files for the specified project, using the provided `tsconfig.json` file.
*
* Any existing declaration files in the `dist-types` directory will be deleted before generating the new ones.
*
* If the `tsc` command exits with a non-zero exit code, the process will be terminated with the same exit code.
*
* @param tsconfigFilePath {string} The path to the `tsconfig.json` file to use for generating the declaration files.
* @returns {Promise<void>} A promise that resolves when the declaration files have been generated.
*/
export async function generateTSC(tsconfigFilePath: string) {
await fs.remove(cliPaths.resolveTargetRoot('dist-types'));
const { status } = spawnSync(
'yarn',
[
'tsc',
['--project', tsconfigFilePath],
['--skipLibCheck', 'false'],
['--incremental', 'false'],
].flat(),
{
stdio: 'inherit',
shell: true,
cwd: cliPaths.targetRoot,
},
);
if (status !== 0) {
process.exit(status || undefined);
}
}
+3 -1
View File
@@ -39,7 +39,9 @@ export function registerCommands(program: Command) {
)
.description('Generate an API report for selected packages')
.action(
lazy(() => import('./api-reports/api-reports').then(m => m.default)),
lazy(() =>
import('./api-reports/api-reports').then(m => m.buildApiReports),
),
);
program