Skip to content

Commit cae4ca8

Browse files
committed
feat(plugin-js-packages): add runner config integration test, restructure runner
1 parent 456793e commit cae4ca8

File tree

2 files changed

+96
-67
lines changed

2 files changed

+96
-67
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
import { writeFile } from 'node:fs/promises';
2-
import { dirname, join } from 'node:path';
3-
import type { AuditOutput, RunnerConfig } from '@code-pushup/models';
2+
import { dirname } from 'node:path';
3+
import type {
4+
AuditOutput,
5+
IssueSeverity,
6+
RunnerConfig,
7+
} from '@code-pushup/models';
48
import {
59
ensureDirectoryExists,
610
executeProcess,
711
readJsonFile,
812
} from '@code-pushup/utils';
913
import {
1014
FinalJSPackagesPluginConfig,
11-
PackageCommand,
15+
PackageAuditLevel,
1216
PackageDependency,
17+
PackageManager,
1318
packageDependencies,
1419
} from '../config';
1520
import { auditResultToAuditOutput } from './audit/transform';
@@ -18,55 +23,6 @@ import { PLUGIN_CONFIG_PATH, RUNNER_OUTPUT_PATH } from './constants';
1823
import { outdatedResultToAuditOutput } from './outdated/transform';
1924
import { NpmOutdatedResultJson } from './outdated/types';
2025

21-
export async function executeRunner(): Promise<void> {
22-
const outputPath = join(
23-
process.cwd(),
24-
'node_modules',
25-
'.code-pushup',
26-
'js-packages',
27-
);
28-
29-
const { packageManager, checks, auditLevelMapping } =
30-
await readJsonFile<FinalJSPackagesPluginConfig>(PLUGIN_CONFIG_PATH);
31-
32-
const results = await Promise.allSettled(
33-
checks.flatMap(check =>
34-
packageDependencies.map<Promise<AuditOutput>>(async dep => {
35-
const outputFilename = `${packageManager}-${check}-${dep}.json`;
36-
37-
await executeProcess({
38-
command: 'npm',
39-
args: [
40-
check,
41-
...getCommandArgs(check, dep, join(outputPath, outputFilename)),
42-
],
43-
alwaysResolve: true, // npm outdated returns exit code 1 when outdated dependencies are found
44-
});
45-
46-
if (check === 'audit') {
47-
const auditResult = await readJsonFile<NpmAuditResultJson>(
48-
join(outputPath, outputFilename),
49-
);
50-
return auditResultToAuditOutput(auditResult, dep, auditLevelMapping);
51-
} else {
52-
const outdatedResult = await readJsonFile<NpmOutdatedResultJson>(
53-
join(outputPath, outputFilename),
54-
);
55-
return outdatedResultToAuditOutput(outdatedResult, dep);
56-
}
57-
}),
58-
),
59-
);
60-
const auditOutputs = results
61-
.filter(
62-
(x): x is PromiseFulfilledResult<AuditOutput> => x.status === 'fulfilled',
63-
)
64-
.map(x => x.value);
65-
66-
await ensureDirectoryExists(dirname(RUNNER_OUTPUT_PATH));
67-
await writeFile(RUNNER_OUTPUT_PATH, JSON.stringify(auditOutputs));
68-
}
69-
7026
export async function createRunnerConfig(
7127
scriptPath: string,
7228
config: FinalJSPackagesPluginConfig,
@@ -81,27 +37,64 @@ export async function createRunnerConfig(
8137
};
8238
}
8339

84-
function getCommandArgs(
85-
check: PackageCommand,
86-
dep: PackageDependency,
87-
outputPath: string,
40+
export async function executeRunner(): Promise<void> {
41+
const { packageManager, checks, auditLevelMapping } =
42+
await readJsonFile<FinalJSPackagesPluginConfig>(PLUGIN_CONFIG_PATH);
43+
44+
const auditResults = checks.includes('audit')
45+
? await processAudit(packageManager, auditLevelMapping)
46+
: [];
47+
48+
const outdatedResults = checks.includes('outdated')
49+
? await processOutdated(packageManager)
50+
: [];
51+
const checkResults = [...auditResults, ...outdatedResults];
52+
53+
await ensureDirectoryExists(dirname(RUNNER_OUTPUT_PATH));
54+
await writeFile(RUNNER_OUTPUT_PATH, JSON.stringify(checkResults));
55+
}
56+
57+
async function processOutdated(packageManager: PackageManager) {
58+
const { stdout } = await executeProcess({
59+
command: packageManager,
60+
args: ['outdated', '--json', '--long'],
61+
alwaysResolve: true, // npm outdated returns exit code 1 when outdated dependencies are found
62+
});
63+
64+
const outdatedResult = JSON.parse(stdout) as NpmOutdatedResultJson;
65+
return packageDependencies.map(dep =>
66+
outdatedResultToAuditOutput(outdatedResult, dep),
67+
);
68+
}
69+
70+
async function processAudit(
71+
packageManager: PackageManager,
72+
auditLevelMapping: Record<PackageAuditLevel, IssueSeverity>,
8873
) {
89-
return check === 'audit'
90-
? [
91-
...createAuditFlags(dep),
92-
'--json',
93-
'--audit-level=none',
94-
'>',
95-
outputPath,
96-
]
97-
: ['--json', '--long', '>', outputPath];
74+
const auditResults = await Promise.allSettled(
75+
packageDependencies.map<Promise<AuditOutput>>(async dep => {
76+
const { stdout } = await executeProcess({
77+
command: packageManager,
78+
args: ['audit', ...getNpmAuditOptions(dep)],
79+
});
80+
81+
const auditResult = JSON.parse(stdout) as NpmAuditResultJson;
82+
return auditResultToAuditOutput(auditResult, dep, auditLevelMapping);
83+
}),
84+
);
85+
return auditResults
86+
.filter(
87+
(x): x is PromiseFulfilledResult<AuditOutput> => x.status === 'fulfilled',
88+
)
89+
.map(x => x.value);
9890
}
9991

100-
function createAuditFlags(currentDep: PackageDependency) {
101-
return [
92+
function getNpmAuditOptions(currentDep: PackageDependency) {
93+
const flags = [
10294
`--include=${currentDep}`,
10395
...packageDependencies
10496
.filter(dep => dep !== currentDep)
10597
.map(dep => `--omit=${dep}`),
10698
];
99+
return [...flags, '--json', '--audit-level=none'];
107100
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { RunnerConfig } from '@code-pushup/models';
3+
import { readJsonFile, removeDirectoryIfExists } from '@code-pushup/utils';
4+
import { createRunnerConfig } from '.';
5+
import { FinalJSPackagesPluginConfig } from '../config';
6+
import { defaultAuditLevelMapping } from '../constants';
7+
import { PLUGIN_CONFIG_PATH, WORKDIR } from './constants';
8+
9+
describe('createRunnerConfig', () => {
10+
it('should create a valid runner config', async () => {
11+
const runnerConfig = await createRunnerConfig('executeRunner.ts', {
12+
packageManager: 'npm',
13+
checks: ['audit'],
14+
auditLevelMapping: defaultAuditLevelMapping,
15+
});
16+
expect(runnerConfig).toStrictEqual<RunnerConfig>({
17+
command: 'node',
18+
args: ['executeRunner.ts'],
19+
outputFile: expect.stringContaining('runner-output.json'),
20+
});
21+
});
22+
23+
it('should provide plugin config to runner in JSON file', async () => {
24+
await removeDirectoryIfExists(WORKDIR);
25+
const pluginConfig: FinalJSPackagesPluginConfig = {
26+
packageManager: 'yarn-classic',
27+
checks: ['outdated'],
28+
auditLevelMapping: { ...defaultAuditLevelMapping, moderate: 'error' },
29+
};
30+
await createRunnerConfig('executeRunner.ts', pluginConfig);
31+
const config = await readJsonFile<FinalJSPackagesPluginConfig>(
32+
PLUGIN_CONFIG_PATH,
33+
);
34+
expect(config).toStrictEqual(pluginConfig);
35+
});
36+
});

0 commit comments

Comments
 (0)