From c259b942d367ebeeab4949b31b57465a38ffce9e Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 13 Nov 2023 22:47:42 +0100 Subject: [PATCH 01/18] wip --- .../implementation/execute-esm-runner.spec.ts | 22 +++++++++++++++ .../lib/implementation/execute-esm-runner.ts | 27 ++++++++++++++++++ packages/models/src/index.ts | 8 +++++- .../src/lib/plugin-config-runner.spec.ts | 24 ++++++++++++++-- .../models/src/lib/plugin-config-runner.ts | 28 ++++++++++++++++++- .../test/fixtures/runner-config.mock.ts | 20 +++++++++++-- packages/models/test/index.ts | 2 +- 7 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 packages/core/src/lib/implementation/execute-esm-runner.spec.ts create mode 100644 packages/core/src/lib/implementation/execute-esm-runner.ts diff --git a/packages/core/src/lib/implementation/execute-esm-runner.spec.ts b/packages/core/src/lib/implementation/execute-esm-runner.spec.ts new file mode 100644 index 000000000..50341d7da --- /dev/null +++ b/packages/core/src/lib/implementation/execute-esm-runner.spec.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from 'vitest'; +import { + AuditOutputs, + //EsmObserver, + auditOutputsSchema, +} from '@code-pushup/models'; +import { executeEsmRunner } from './execute-esm-runner'; + +describe('executeEsmRunner', () => { + it('should execute valid plugin config', async () => { + const autidOutputs = await executeEsmRunner({ + observer: { next: console.log }, + runner: (/*observer?: EsmObserver*/) => + Promise.resolve([ + { slug: 'mock-audit-slug', score: 0, value: 0 }, + ] satisfies AuditOutputs), + }); + expect(autidOutputs.audits[0]?.slug).toBe('mock-audit-slug'); + expect(autidOutputs).toBe('mock-audit-slug'); + expect(() => auditOutputsSchema.parse(autidOutputs)).not.toThrow(); + }); +}); diff --git a/packages/core/src/lib/implementation/execute-esm-runner.ts b/packages/core/src/lib/implementation/execute-esm-runner.ts new file mode 100644 index 000000000..3ad13b81b --- /dev/null +++ b/packages/core/src/lib/implementation/execute-esm-runner.ts @@ -0,0 +1,27 @@ +import { + EsmObserver, + EsmRunnerConfig, + RunnerResult, + runnerResultSchema, +} from '@code-pushup/models'; +import { calcDuration } from '@code-pushup/utils'; + +export type EsmRunnerProcessConfig = { + runner: EsmRunnerConfig; + observer?: EsmObserver; +}; + +export function executeEsmRunner( + cfg: EsmRunnerProcessConfig, +): Promise { + const { observer, runner } = cfg; + const date = new Date().toISOString(); + const start = performance.now(); + + return runner(observer).then(result => { + const timings = { date, duration: calcDuration(start) }; + return runnerResultSchema.parse({ result, ...timings }); + }); +} + + diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index bea662fd6..c9a0ba02c 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -29,7 +29,7 @@ export { AuditGroup, auditGroupSchema, } from './lib/plugin-config-groups'; -export { AuditOutput, auditOutputsSchema } from './lib/plugin-process-output'; +export { AuditOutput, AuditOutputs, auditOutputsSchema } from './lib/plugin-process-output'; export { Issue, IssueSeverity } from './lib/plugin-process-output-audit-issue'; export { AuditReport, @@ -40,3 +40,9 @@ export { } from './lib/report'; export { UploadConfig, uploadConfigSchema } from './lib/upload-config'; export { materialIconSchema } from './lib/implementation/schemas'; +export { + EsmObserver, + EsmRunnerConfig, + RunnerResult, + runnerResultSchema, +} from './lib/plugin-config-runner'; diff --git a/packages/models/src/lib/plugin-config-runner.spec.ts b/packages/models/src/lib/plugin-config-runner.spec.ts index cd40432f6..9d5d2e060 100644 --- a/packages/models/src/lib/plugin-config-runner.spec.ts +++ b/packages/models/src/lib/plugin-config-runner.spec.ts @@ -1,6 +1,12 @@ import { describe, expect, it } from 'vitest'; -import { runnerConfig } from '../../test/fixtures/runner-config.mock'; -import { runnerConfigSchema } from './plugin-config-runner'; +import { + esmRunnerConfig, + runnerConfig, +} from '../../test/fixtures/runner-config.mock'; +import { + esmRunnerConfigSchema, + runnerConfigSchema, +} from './plugin-config-runner'; describe('runnerConfig', () => { it('should parse if configuration is valid', () => { @@ -23,3 +29,17 @@ describe('runnerConfig', () => { ); }); }); + +describe('esmRunnerConfig', () => { + it('should parse if configuration is valid', () => { + const runnerConfigMock = esmRunnerConfig(); + expect(() => esmRunnerConfigSchema.parse(runnerConfigMock)).not.toThrow(); + }); + + it('should throw if not a function', () => { + const runnerConfigMock = runnerConfig(); + expect(() => esmRunnerConfigSchema.parse(runnerConfigMock)).toThrow( + `Expected function,`, + ); + }); +}); diff --git a/packages/models/src/lib/plugin-config-runner.ts b/packages/models/src/lib/plugin-config-runner.ts index b094bd0ad..b5bc274d3 100644 --- a/packages/models/src/lib/plugin-config-runner.ts +++ b/packages/models/src/lib/plugin-config-runner.ts @@ -1,5 +1,6 @@ import { z } from 'zod'; -import { filePathSchema } from './implementation/schemas'; +import { executionMetaSchema, filePathSchema } from './implementation/schemas'; +import {auditOutputSchema, auditOutputsSchema} from './plugin-process-output'; export const runnerConfigSchema = z.object( { @@ -15,3 +16,28 @@ export const runnerConfigSchema = z.object( ); export type RunnerConfig = z.infer; + +export const runnerResultSchema = executionMetaSchema().merge( + z.object( + { + audits: auditOutputSchema, + }, + { + description: 'Shape for all versions of runner', + }, + ), +); +export type RunnerResult = z.infer; +/* +export const esmObserver = z.object({ + next: z.function().args(z.unknown()).returns(z.void()), +}); +export type EsmObserver = z.infer; + +export const esmRunnerConfigSchema = z + .function() + .args(esmObserver.optional()) + .returns(z.promise(z.array(z.record(z.string(), z.unknown())))); + +export type EsmRunnerConfig = z.infer; +*/ diff --git a/packages/models/test/fixtures/runner-config.mock.ts b/packages/models/test/fixtures/runner-config.mock.ts index d7965b283..0fea52a7d 100644 --- a/packages/models/test/fixtures/runner-config.mock.ts +++ b/packages/models/test/fixtures/runner-config.mock.ts @@ -1,7 +1,11 @@ import { platform } from 'os'; import { RunnerConfig } from '../../src'; -import { runnerConfigSchema } from '../../src/lib/plugin-config-runner'; -import { AuditOutput } from '../../src/lib/plugin-process-output'; +import { + EsmRunnerConfig, + esmRunnerConfigSchema, + runnerConfigSchema, +} from '../../src/lib/plugin-config-runner'; +import { AuditOutput, AuditOutputs } from '../../src/lib/plugin-process-output'; /** * Use this helper as a general purpose mock with working defaults @@ -37,3 +41,15 @@ export function echoRunnerConfig( outputFile, }; } + +/** + * Use this helper as a general purpose mock with working defaults + * @param options + */ +export function esmRunnerConfig( + options?: Partial, +): EsmRunnerConfig { + return esmRunnerConfigSchema.parse((cfg: unknown): AuditOutputs => { + return [{} as AuditOutput]; + }); +} diff --git a/packages/models/test/index.ts b/packages/models/test/index.ts index c3b9ab142..17005cf2e 100644 --- a/packages/models/test/index.ts +++ b/packages/models/test/index.ts @@ -5,7 +5,7 @@ export * from './memfs'; export { eslintPluginConfig } from './fixtures/eslint-plugin.mock'; export { lighthousePluginConfig } from './fixtures/lighthouse-plugin.mock'; -export { echoRunnerConfig } from './fixtures/runner-config.mock'; +export { echoRunnerConfig, esmRunnerConfig } from './fixtures/runner-config.mock'; export { persistConfig } from './fixtures/persist-config.mock'; export { auditConfig, From 1fed84c78526eb85bfe561331874a393208d9547 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 14 Nov 2023 11:13:50 +0100 Subject: [PATCH 02/18] refactor: wip --- packages/cli/src/lib/model.ts | 0 .../src/lib/implementation/execute-esm-runner.spec.ts | 3 +-- .../core/src/lib/implementation/execute-esm-runner.ts | 2 -- packages/models/src/index.ts | 6 +++++- packages/models/src/lib/global-options.ts | 1 + packages/models/src/lib/plugin-config-runner.ts | 9 ++++----- packages/models/src/lib/plugin-process-output.ts | 6 +++--- packages/models/test/index.ts | 5 ++++- 8 files changed, 18 insertions(+), 14 deletions(-) delete mode 100644 packages/cli/src/lib/model.ts diff --git a/packages/cli/src/lib/model.ts b/packages/cli/src/lib/model.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/core/src/lib/implementation/execute-esm-runner.spec.ts b/packages/core/src/lib/implementation/execute-esm-runner.spec.ts index 50341d7da..f24202882 100644 --- a/packages/core/src/lib/implementation/execute-esm-runner.spec.ts +++ b/packages/core/src/lib/implementation/execute-esm-runner.spec.ts @@ -1,7 +1,6 @@ import { describe, expect, it } from 'vitest'; import { - AuditOutputs, - //EsmObserver, + AuditOutputs, //EsmObserver, auditOutputsSchema, } from '@code-pushup/models'; import { executeEsmRunner } from './execute-esm-runner'; diff --git a/packages/core/src/lib/implementation/execute-esm-runner.ts b/packages/core/src/lib/implementation/execute-esm-runner.ts index 3ad13b81b..76cff91d3 100644 --- a/packages/core/src/lib/implementation/execute-esm-runner.ts +++ b/packages/core/src/lib/implementation/execute-esm-runner.ts @@ -23,5 +23,3 @@ export function executeEsmRunner( return runnerResultSchema.parse({ result, ...timings }); }); } - - diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index c9a0ba02c..d9dc26526 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -29,7 +29,11 @@ export { AuditGroup, auditGroupSchema, } from './lib/plugin-config-groups'; -export { AuditOutput, AuditOutputs, auditOutputsSchema } from './lib/plugin-process-output'; +export { + AuditOutput, + AuditOutputs, + auditOutputsSchema, +} from './lib/plugin-process-output'; export { Issue, IssueSeverity } from './lib/plugin-process-output-audit-issue'; export { AuditReport, diff --git a/packages/models/src/lib/global-options.ts b/packages/models/src/lib/global-options.ts index f3dc3bada..b33e4e8fe 100644 --- a/packages/models/src/lib/global-options.ts +++ b/packages/models/src/lib/global-options.ts @@ -12,6 +12,7 @@ export const globalOptionsSchema = z.object({ description: 'Outputs additional information for a run', }) .default(false), + // @TODO move to cli package as it is only used there config: filePathSchema( "Path to config file in format `ts` or `mjs`. defaults to 'code-pushup.config.js'", ).default('code-pushup.config.js'), diff --git a/packages/models/src/lib/plugin-config-runner.ts b/packages/models/src/lib/plugin-config-runner.ts index b5bc274d3..d290d15e0 100644 --- a/packages/models/src/lib/plugin-config-runner.ts +++ b/packages/models/src/lib/plugin-config-runner.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; import { executionMetaSchema, filePathSchema } from './implementation/schemas'; -import {auditOutputSchema, auditOutputsSchema} from './plugin-process-output'; +import {auditReportSchema, pluginReportSchema} from './report'; export const runnerConfigSchema = z.object( { @@ -20,7 +20,7 @@ export type RunnerConfig = z.infer; export const runnerResultSchema = executionMetaSchema().merge( z.object( { - audits: auditOutputSchema, + // audits: auditReportSchema, }, { description: 'Shape for all versions of runner', @@ -28,7 +28,7 @@ export const runnerResultSchema = executionMetaSchema().merge( ), ); export type RunnerResult = z.infer; -/* + export const esmObserver = z.object({ next: z.function().args(z.unknown()).returns(z.void()), }); @@ -37,7 +37,6 @@ export type EsmObserver = z.infer; export const esmRunnerConfigSchema = z .function() .args(esmObserver.optional()) - .returns(z.promise(z.array(z.record(z.string(), z.unknown())))); + // .returns(z.promise(pluginReportSchema)); export type EsmRunnerConfig = z.infer; -*/ diff --git a/packages/models/src/lib/plugin-process-output.ts b/packages/models/src/lib/plugin-process-output.ts index 86efaf630..003545ab6 100644 --- a/packages/models/src/lib/plugin-process-output.ts +++ b/packages/models/src/lib/plugin-process-output.ts @@ -47,8 +47,8 @@ export const auditOutputsSchema = z ); export type AuditOutputs = z.infer; -export const pluginOutputSchema = pluginMetaSchema - .merge(executionMetaSchema()) +/*export const pluginOutputSchema = pluginMetaSchema + // .merge(executionMetaSchema()) .merge( z.object( { @@ -62,7 +62,7 @@ export const pluginOutputSchema = pluginMetaSchema ); export type PluginOutput = z.infer; - +*/ // helper for validator: audit slugs are unique function duplicateSlugsInAuditsErrorMsg(audits: AuditOutput[]) { const duplicateRefs = getDuplicateSlugsInAudits(audits); diff --git a/packages/models/test/index.ts b/packages/models/test/index.ts index 17005cf2e..27d638c8e 100644 --- a/packages/models/test/index.ts +++ b/packages/models/test/index.ts @@ -5,7 +5,10 @@ export * from './memfs'; export { eslintPluginConfig } from './fixtures/eslint-plugin.mock'; export { lighthousePluginConfig } from './fixtures/lighthouse-plugin.mock'; -export { echoRunnerConfig, esmRunnerConfig } from './fixtures/runner-config.mock'; +export { + echoRunnerConfig, + esmRunnerConfig, +} from './fixtures/runner-config.mock'; export { persistConfig } from './fixtures/persist-config.mock'; export { auditConfig, From 062d55d4939f52ba4541ec1a402304e75fe01efd Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 14 Nov 2023 12:00:00 +0100 Subject: [PATCH 03/18] refactor: wip 2 --- .../core/src/lib/implementation/execute-esm-runner.ts | 3 ++- packages/models/src/lib/plugin-config-runner.ts | 11 +++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/core/src/lib/implementation/execute-esm-runner.ts b/packages/core/src/lib/implementation/execute-esm-runner.ts index 76cff91d3..d96561c58 100644 --- a/packages/core/src/lib/implementation/execute-esm-runner.ts +++ b/packages/core/src/lib/implementation/execute-esm-runner.ts @@ -1,4 +1,5 @@ import { + AuditOutputs, EsmObserver, EsmRunnerConfig, RunnerResult, @@ -18,7 +19,7 @@ export function executeEsmRunner( const date = new Date().toISOString(); const start = performance.now(); - return runner(observer).then(result => { + return runner(observer).then((result: AuditOutputs) => { const timings = { date, duration: calcDuration(start) }; return runnerResultSchema.parse({ result, ...timings }); }); diff --git a/packages/models/src/lib/plugin-config-runner.ts b/packages/models/src/lib/plugin-config-runner.ts index d290d15e0..0d9b7d583 100644 --- a/packages/models/src/lib/plugin-config-runner.ts +++ b/packages/models/src/lib/plugin-config-runner.ts @@ -1,6 +1,6 @@ -import { z } from 'zod'; -import { executionMetaSchema, filePathSchema } from './implementation/schemas'; -import {auditReportSchema, pluginReportSchema} from './report'; +import {z} from 'zod'; +import {executionMetaSchema, filePathSchema} from './implementation/schemas'; +import {auditOutputsSchema} from "./plugin-process-output"; export const runnerConfigSchema = z.object( { @@ -34,9 +34,8 @@ export const esmObserver = z.object({ }); export type EsmObserver = z.infer; -export const esmRunnerConfigSchema = z - .function() +export const esmRunnerConfigSchema = z.function() .args(esmObserver.optional()) - // .returns(z.promise(pluginReportSchema)); + .returns(z.promise(auditOutputsSchema)); export type EsmRunnerConfig = z.infer; From bce48bc4544e4b7c892582b19f0e648ea613f754 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 14 Nov 2023 15:03:23 +0100 Subject: [PATCH 04/18] refactor(core): add esm runner type --- .../lib/implementation/execute-plugin.spec.ts | 56 +++++++++++++++---- .../src/lib/implementation/execute-plugin.ts | 53 ++++++------------ .../src/lib/implementation/runner-esm.spec.ts | 33 ++++++++--- .../core/src/lib/implementation/runner-esm.ts | 19 ++----- .../lib/implementation/runner-process.spec.ts | 26 ++++++--- .../models/src/lib/plugin-config-runner.ts | 2 +- packages/models/src/lib/plugin-config.ts | 7 ++- packages/utils/src/index.ts | 1 + packages/utils/src/lib/execute-process.ts | 5 +- packages/utils/src/lib/observer.ts | 5 ++ 10 files changed, 123 insertions(+), 84 deletions(-) create mode 100644 packages/utils/src/lib/observer.ts diff --git a/packages/core/src/lib/implementation/execute-plugin.spec.ts b/packages/core/src/lib/implementation/execute-plugin.spec.ts index 4ba7dde9c..99b3e4b7a 100644 --- a/packages/core/src/lib/implementation/execute-plugin.spec.ts +++ b/packages/core/src/lib/implementation/execute-plugin.spec.ts @@ -1,11 +1,12 @@ import { describe, expect, it } from 'vitest'; import { - AuditOutput, AuditOutputs, PluginConfig, + RunnerConfig, auditOutputsSchema, } from '@code-pushup/models'; import { auditReport, pluginConfig } from '@code-pushup/models/testing'; +import { Observer } from '@code-pushup/utils'; import { DEFAULT_TESTING_CLI_OPTIONS } from '../../../test/constants'; import { PluginOutputMissingAuditError, @@ -42,16 +43,47 @@ describe('executePlugin', () => { ); }); - it('should throw if invalid runnerOutput is produced with transform', async () => { + it('should work with valid process runner config', async () => { + const runnerConfig = validPluginCfg.runner as RunnerConfig; const pluginCfg: PluginConfig = { ...validPluginCfg, runner: { - ...validPluginCfg.runner, - outputTransform: (d: unknown) => - Array.from(d as Record[]).map((d, idx) => ({ - ...d, - slug: '-invalid-slug-' + idx, - })) as unknown as AuditOutputs, + ...runnerConfig, + outputTransform: (audits: unknown) => + Promise.resolve(audits as AuditOutputs), + }, + }; + const pluginResult = await executePlugin(pluginCfg); + expect(pluginResult.audits[0]?.slug).toBe('mock-audit-slug'); + expect(() => auditOutputsSchema.parse(pluginResult.audits)).not.toThrow(); + }); + + it('should work with valid esm runner config', async () => { + const esmRunnerConfig = (observer?: Observer) => { + observer?.next?.('update'); + return Promise.resolve([ + { slug: 'mock-audit-slug', score: 0, value: 0 }, + ] satisfies AuditOutputs); + }; + + const pluginCfg: PluginConfig = { + ...validPluginCfg, + runner: esmRunnerConfig, + }; + const pluginResult = await executePlugin(pluginCfg); + expect(pluginResult.audits[0]?.slug).toBe('mock-audit-slug'); + expect(() => auditOutputsSchema.parse(pluginResult.audits)).not.toThrow(); + }); + + it('should throw if invalid runnerOutput is produced with transform', async () => { + const pluginCfg: PluginConfig = { + ...validPluginCfg, + runner: (observer?: Observer) => { + observer?.next?.('update'); + + return Promise.resolve([ + { slug: '-mock-audit-slug', score: 0, value: 0 }, + ] satisfies AuditOutputs); }, }; @@ -87,21 +119,21 @@ describe('executePlugins', () => { }); it('should use outputTransform if provided', async () => { + const processRunner = validPluginCfg.runner as RunnerConfig; const plugins: PluginConfig[] = [ { ...validPluginCfg, runner: { - ...validPluginCfg.runner, + ...processRunner, outputTransform: (outputs: unknown): Promise => { - const arr = Array.from(outputs as Record[]); return Promise.resolve( - arr.map(output => { + (outputs as AuditOutputs).map(output => { return { ...output, displayValue: 'transformed slug description - ' + (output as { slug: string }).slug, - } as unknown as AuditOutput; + }; }), ); }, diff --git a/packages/core/src/lib/implementation/execute-plugin.ts b/packages/core/src/lib/implementation/execute-plugin.ts index 95fb6ec84..5b09c4e2c 100644 --- a/packages/core/src/lib/implementation/execute-plugin.ts +++ b/packages/core/src/lib/implementation/execute-plugin.ts @@ -1,5 +1,4 @@ import chalk from 'chalk'; -import { join } from 'path'; import { Audit, AuditOutput, @@ -9,12 +8,9 @@ import { PluginReport, auditOutputsSchema, } from '@code-pushup/models'; -import { - ProcessObserver, - executeProcess, - getProgressBar, - readJsonFile, -} from '@code-pushup/utils'; +import { Observer, getProgressBar } from '@code-pushup/utils'; +import { executeEsmRunner } from './runner-esm'; +import { executeProcessRunner } from './runner-process'; /** * Error thrown when plugin output is invalid. @@ -30,7 +26,7 @@ export class PluginOutputMissingAuditError extends Error { * * @public * @param pluginConfig - {@link ProcessConfig} object with runner and meta - * @param observer - process {@link ProcessObserver} + * @param observer - process {@link Observer} * @returns {Promise} - audit outputs from plugin runner * @throws {PluginOutputMissingAuditError} - if plugin runner output is invalid * @@ -49,48 +45,30 @@ export class PluginOutputMissingAuditError extends Error { */ export async function executePlugin( pluginConfig: PluginConfig, - observer?: ProcessObserver, + observer?: Observer, ): Promise { const { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - runner: onlyUsedForRestingPluginMeta, + runner, audits: pluginConfigAudits, description, docsUrl, groups, ...pluginMeta } = pluginConfig; - const { args, command } = pluginConfig.runner; - - const { date, duration } = await executeProcess({ - command, - args, - observer, - }); - const executionMeta = { date, duration }; - - const processOutputPath = join(process.cwd(), pluginConfig.runner.outputFile); - - // read process output from file system and parse it - let unknownAuditOutputs = await readJsonFile[]>( - processOutputPath, - ); - - // parse transform unknownAuditOutputs to auditOutputs - if (pluginConfig.runner?.outputTransform) { - unknownAuditOutputs = await pluginConfig.runner.outputTransform( - unknownAuditOutputs, - ); - } - // validate audit outputs - const auditOutputs = auditOutputsSchema.parse(unknownAuditOutputs); + // execute plugin runner + const runnerResult = + typeof runner === 'object' + ? await executeProcessRunner(runner, observer) + : await executeEsmRunner(runner, observer); + const { audits: unvalidatedAuditOutputs, ...executionMeta } = runnerResult; // validate auditOutputs + const auditOutputs = auditOutputsSchema.parse(unvalidatedAuditOutputs); auditOutputsCorrelateWithPluginOutput(auditOutputs, pluginConfigAudits); // enrich `AuditOutputs` to `AuditReport` - const audits: AuditReport[] = auditOutputs.map( + const auditReports: AuditReport[] = auditOutputs.map( (auditOutput: AuditOutput) => ({ ...auditOutput, ...(pluginConfigAudits.find( @@ -99,10 +77,11 @@ export async function executePlugin( }), ); + // create plugin report return { ...pluginMeta, ...executionMeta, - audits, + audits: auditReports, ...(description && { description }), ...(docsUrl && { docsUrl }), ...(groups && { groups }), diff --git a/packages/core/src/lib/implementation/runner-esm.spec.ts b/packages/core/src/lib/implementation/runner-esm.spec.ts index d849dc7c9..45d33f371 100644 --- a/packages/core/src/lib/implementation/runner-esm.spec.ts +++ b/packages/core/src/lib/implementation/runner-esm.spec.ts @@ -1,16 +1,33 @@ import { describe, expect, it } from 'vitest'; import { AuditOutputs } from '@code-pushup/models'; -import { runnerEsm } from './runner-esm'; +import { Observer } from '@code-pushup/utils'; +import { RunnerResult } from './runner'; +import { executeEsmRunner } from './runner-esm'; describe('executeEsmRunner', () => { it('should execute valid plugin config', async () => { - const autidOutputs = await runnerEsm({ - observer: { next: console.log }, - runner: () => - Promise.resolve([ + const nextSpy = vi.fn(); + const runnerResult: RunnerResult = await executeEsmRunner( + (observer?: Observer) => { + observer?.next?.('update'); + + return Promise.resolve([ { slug: 'mock-audit-slug', score: 0, value: 0 }, - ] satisfies AuditOutputs), - }); - expect(autidOutputs.audits[0]?.slug).toBe('mock-audit-slug'); + ] satisfies AuditOutputs); + }, + { next: nextSpy }, + ); + expect(nextSpy).toHaveBeenCalledWith('update'); + expect(runnerResult.audits[0]?.slug).toBe('mock-audit-slug'); + }); + + it('should throw if plugin throws', async () => { + const nextSpy = vi.fn(); + await expect( + executeEsmRunner( + () => Promise.reject(new Error('plugin exex mock error')), + { next: nextSpy }, + ), + ).rejects.toThrow('plugin exex mock error'); }); }); diff --git a/packages/core/src/lib/implementation/runner-esm.ts b/packages/core/src/lib/implementation/runner-esm.ts index eb7ed5392..4f1bee6b1 100644 --- a/packages/core/src/lib/implementation/runner-esm.ts +++ b/packages/core/src/lib/implementation/runner-esm.ts @@ -1,18 +1,11 @@ -import { - AuditOutputs, - EsmObserver, - EsmRunnerConfig, -} from '@code-pushup/models'; -import { calcDuration } from '@code-pushup/utils'; +import { AuditOutputs, EsmRunnerConfig } from '@code-pushup/models'; +import { Observer, calcDuration } from '@code-pushup/utils'; import { RunnerResult } from './runner'; -export type EsmRunnerProcessConfig = { - runner: EsmRunnerConfig; - observer?: EsmObserver; -}; - -export function runnerEsm(cfg: EsmRunnerProcessConfig): Promise { - const { observer, runner } = cfg; +export function executeEsmRunner( + runner: EsmRunnerConfig, + observer?: Observer, +): Promise { const date = new Date().toISOString(); const start = performance.now(); diff --git a/packages/core/src/lib/implementation/runner-process.spec.ts b/packages/core/src/lib/implementation/runner-process.spec.ts index 35c963f71..f56f3e2be 100644 --- a/packages/core/src/lib/implementation/runner-process.spec.ts +++ b/packages/core/src/lib/implementation/runner-process.spec.ts @@ -15,20 +15,17 @@ describe('executeRunner', () => { expect(runnerResult.audits[0]?.slug).toBe('mock-audit-slug'); // schema validation - // expect(() => runnerResult.date).toBe(expect.any(String)); - // expect(() => runnerResult.duration).toBe(expect.any(Number)); expect(() => auditOutputsSchema.parse(runnerResult.audits)).not.toThrow(); }); it('should use transform if provided', async () => { - const outputTransform = (audits: unknown) => - (audits as AuditOutputs).map(a => ({ - ...a, - displayValue: `transformed - ${a.slug}`, - })); const runnerCfgWithTransform = { ...validRunnerCfg, - outputTransform, + outputTransform: (audits: unknown) => + (audits as AuditOutputs).map(a => ({ + ...a, + displayValue: `transformed - ${a.slug}`, + })), }; const runnerResult = await executeProcessRunner(runnerCfgWithTransform); @@ -37,4 +34,17 @@ describe('executeRunner', () => { 'transformed - mock-audit-slug', ); }); + + it('should throw if transform throws', async () => { + const runnerCfgWithErrorTransform = { + ...validRunnerCfg, + outputTransform: () => { + return Promise.reject(new Error('transform mock error')); + }, + }; + + await expect( + executeProcessRunner(runnerCfgWithErrorTransform), + ).rejects.toThrow('transform mock error'); + }); }); diff --git a/packages/models/src/lib/plugin-config-runner.ts b/packages/models/src/lib/plugin-config-runner.ts index b11872d14..78107e0be 100644 --- a/packages/models/src/lib/plugin-config-runner.ts +++ b/packages/models/src/lib/plugin-config-runner.ts @@ -26,7 +26,7 @@ export const runnerConfigSchema = z.object( export type RunnerConfig = z.infer; export const esmObserver = z.object({ - next: z.function().args(z.unknown()).returns(z.void()), + next: z.function().args(z.unknown()).returns(z.void()).optional(), }); export type EsmObserver = z.infer; diff --git a/packages/models/src/lib/plugin-config.ts b/packages/models/src/lib/plugin-config.ts index 25f1cbd2a..59bc2f33a 100644 --- a/packages/models/src/lib/plugin-config.ts +++ b/packages/models/src/lib/plugin-config.ts @@ -8,7 +8,10 @@ import { import { errorItems, hasMissingStrings } from './implementation/utils'; import { pluginAuditsSchema } from './plugin-config-audits'; import { auditGroupsSchema } from './plugin-config-groups'; -import { runnerConfigSchema } from './plugin-config-runner'; +import { + esmRunnerConfigSchema, + runnerConfigSchema, +} from './plugin-config-runner'; export const pluginMetaSchema = packageVersionSchema({ optional: true, @@ -29,7 +32,7 @@ export const pluginMetaSchema = packageVersionSchema({ ); export const pluginDataSchema = z.object({ - runner: runnerConfigSchema, + runner: z.union([runnerConfigSchema, esmRunnerConfigSchema]), audits: pluginAuditsSchema, groups: auditGroupsSchema, }); diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index ae501640a..b5809bb30 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -43,3 +43,4 @@ export { slugify, } from './lib/transformation'; export { NEW_LINE } from './lib/md'; +export { Observer } from './lib/observer'; diff --git a/packages/utils/src/lib/execute-process.ts b/packages/utils/src/lib/execute-process.ts index 80290046a..803bc9c16 100644 --- a/packages/utils/src/lib/execute-process.ts +++ b/packages/utils/src/lib/execute-process.ts @@ -1,4 +1,5 @@ import { spawn } from 'child_process'; +import { Observer } from './observer'; import { calcDuration } from './report'; /** @@ -97,10 +98,8 @@ export type ProcessConfig = { * next: (stdout) => console.log(stdout) * } */ -export type ProcessObserver = { - next?: (stdout: string) => void; +export type ProcessObserver = Omit & { error?: (error: ProcessError) => void; - complete?: () => void; }; /** diff --git a/packages/utils/src/lib/observer.ts b/packages/utils/src/lib/observer.ts new file mode 100644 index 000000000..b633656bf --- /dev/null +++ b/packages/utils/src/lib/observer.ts @@ -0,0 +1,5 @@ +export type Observer = { + next?: (value: unknown) => void; + error?: (error: unknown) => void; + complete?: () => void; +}; From 5875d81e8525cf488439112b92b2a1704093d1cd Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 14 Nov 2023 15:24:44 +0100 Subject: [PATCH 05/18] refactor(core): adjust logic --- .../core/src/lib/implementation/runner-esm.ts | 17 +++++++++++------ .../src/lib/implementation/runner-process.ts | 4 +++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/core/src/lib/implementation/runner-esm.ts b/packages/core/src/lib/implementation/runner-esm.ts index 4f1bee6b1..d50ebd85d 100644 --- a/packages/core/src/lib/implementation/runner-esm.ts +++ b/packages/core/src/lib/implementation/runner-esm.ts @@ -1,16 +1,21 @@ -import { AuditOutputs, EsmRunnerConfig } from '@code-pushup/models'; +import { EsmRunnerConfig } from '@code-pushup/models'; import { Observer, calcDuration } from '@code-pushup/utils'; import { RunnerResult } from './runner'; -export function executeEsmRunner( +export async function executeEsmRunner( runner: EsmRunnerConfig, observer?: Observer, ): Promise { const date = new Date().toISOString(); const start = performance.now(); - return runner(observer).then((audits: AuditOutputs) => { - const timings = { date, duration: calcDuration(start) }; - return { ...timings, audits }; - }); + // execute plugin runner + const audits = await runner(observer); + + // create runner result + return { + date, + duration: calcDuration(start), + audits, + } satisfies RunnerResult; } diff --git a/packages/core/src/lib/implementation/runner-process.ts b/packages/core/src/lib/implementation/runner-process.ts index e7b33eb23..754dfeeb3 100644 --- a/packages/core/src/lib/implementation/runner-process.ts +++ b/packages/core/src/lib/implementation/runner-process.ts @@ -13,6 +13,7 @@ export async function executeProcessRunner( ): Promise { const { args, command, outputFile, outputTransform } = cfg; + // execute process const { duration, date } = await executeProcess({ command, args, @@ -26,9 +27,10 @@ export async function executeProcessRunner( // transform unknownAuditOutputs to auditOutputs if (outputTransform) { - audits = (await outputTransform(audits)) as AuditOutputs; + audits = await outputTransform(audits); } + // create runner result return { duration, date, From 0c263efa6fb6ef2e596480500d968502d39d2a04 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 14 Nov 2023 15:40:31 +0100 Subject: [PATCH 06/18] refactor: merge files --- .../src/lib/implementation/execute-plugin.ts | 3 +- .../src/lib/implementation/runner-esm.spec.ts | 33 ---------- .../core/src/lib/implementation/runner-esm.ts | 21 ------- .../src/lib/implementation/runner-process.ts | 39 ------------ ...{runner-process.spec.ts => runner.spec.ts} | 31 ++++++++- .../core/src/lib/implementation/runner.ts | 63 ++++++++++++++++++- 6 files changed, 93 insertions(+), 97 deletions(-) delete mode 100644 packages/core/src/lib/implementation/runner-esm.spec.ts delete mode 100644 packages/core/src/lib/implementation/runner-esm.ts delete mode 100644 packages/core/src/lib/implementation/runner-process.ts rename packages/core/src/lib/implementation/{runner-process.spec.ts => runner.spec.ts} (62%) diff --git a/packages/core/src/lib/implementation/execute-plugin.ts b/packages/core/src/lib/implementation/execute-plugin.ts index 5b09c4e2c..6c645577b 100644 --- a/packages/core/src/lib/implementation/execute-plugin.ts +++ b/packages/core/src/lib/implementation/execute-plugin.ts @@ -9,8 +9,7 @@ import { auditOutputsSchema, } from '@code-pushup/models'; import { Observer, getProgressBar } from '@code-pushup/utils'; -import { executeEsmRunner } from './runner-esm'; -import { executeProcessRunner } from './runner-process'; +import { executeEsmRunner, executeProcessRunner } from './runner'; /** * Error thrown when plugin output is invalid. diff --git a/packages/core/src/lib/implementation/runner-esm.spec.ts b/packages/core/src/lib/implementation/runner-esm.spec.ts deleted file mode 100644 index 45d33f371..000000000 --- a/packages/core/src/lib/implementation/runner-esm.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { AuditOutputs } from '@code-pushup/models'; -import { Observer } from '@code-pushup/utils'; -import { RunnerResult } from './runner'; -import { executeEsmRunner } from './runner-esm'; - -describe('executeEsmRunner', () => { - it('should execute valid plugin config', async () => { - const nextSpy = vi.fn(); - const runnerResult: RunnerResult = await executeEsmRunner( - (observer?: Observer) => { - observer?.next?.('update'); - - return Promise.resolve([ - { slug: 'mock-audit-slug', score: 0, value: 0 }, - ] satisfies AuditOutputs); - }, - { next: nextSpy }, - ); - expect(nextSpy).toHaveBeenCalledWith('update'); - expect(runnerResult.audits[0]?.slug).toBe('mock-audit-slug'); - }); - - it('should throw if plugin throws', async () => { - const nextSpy = vi.fn(); - await expect( - executeEsmRunner( - () => Promise.reject(new Error('plugin exex mock error')), - { next: nextSpy }, - ), - ).rejects.toThrow('plugin exex mock error'); - }); -}); diff --git a/packages/core/src/lib/implementation/runner-esm.ts b/packages/core/src/lib/implementation/runner-esm.ts deleted file mode 100644 index d50ebd85d..000000000 --- a/packages/core/src/lib/implementation/runner-esm.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { EsmRunnerConfig } from '@code-pushup/models'; -import { Observer, calcDuration } from '@code-pushup/utils'; -import { RunnerResult } from './runner'; - -export async function executeEsmRunner( - runner: EsmRunnerConfig, - observer?: Observer, -): Promise { - const date = new Date().toISOString(); - const start = performance.now(); - - // execute plugin runner - const audits = await runner(observer); - - // create runner result - return { - date, - duration: calcDuration(start), - audits, - } satisfies RunnerResult; -} diff --git a/packages/core/src/lib/implementation/runner-process.ts b/packages/core/src/lib/implementation/runner-process.ts deleted file mode 100644 index 754dfeeb3..000000000 --- a/packages/core/src/lib/implementation/runner-process.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { join } from 'path'; -import { AuditOutputs, RunnerConfig } from '@code-pushup/models'; -import { - ProcessObserver, - executeProcess, - readJsonFile, -} from '@code-pushup/utils'; -import { RunnerResult } from './runner'; - -export async function executeProcessRunner( - cfg: RunnerConfig, - observer?: ProcessObserver, -): Promise { - const { args, command, outputFile, outputTransform } = cfg; - - // execute process - const { duration, date } = await executeProcess({ - command, - args, - observer, - }); - - // read process output from file system and parse it - let audits = await readJsonFile( - join(process.cwd(), outputFile), - ); - - // transform unknownAuditOutputs to auditOutputs - if (outputTransform) { - audits = await outputTransform(audits); - } - - // create runner result - return { - duration, - date, - audits, - }; -} diff --git a/packages/core/src/lib/implementation/runner-process.spec.ts b/packages/core/src/lib/implementation/runner.spec.ts similarity index 62% rename from packages/core/src/lib/implementation/runner-process.spec.ts rename to packages/core/src/lib/implementation/runner.spec.ts index f56f3e2be..27fdfdb2d 100644 --- a/packages/core/src/lib/implementation/runner-process.spec.ts +++ b/packages/core/src/lib/implementation/runner.spec.ts @@ -1,7 +1,8 @@ import { describe, expect, it } from 'vitest'; import { AuditOutputs, auditOutputsSchema } from '@code-pushup/models'; import { auditReport, echoRunnerConfig } from '@code-pushup/models/testing'; -import { executeProcessRunner } from './runner-process'; +import { Observer } from '@code-pushup/utils'; +import { RunnerResult, executeEsmRunner, executeProcessRunner } from './runner'; const validRunnerCfg = echoRunnerConfig([auditReport()], 'output.json'); @@ -48,3 +49,31 @@ describe('executeRunner', () => { ).rejects.toThrow('transform mock error'); }); }); + +describe('executeEsmRunner', () => { + it('should execute valid plugin config', async () => { + const nextSpy = vi.fn(); + const runnerResult: RunnerResult = await executeEsmRunner( + (observer?: Observer) => { + observer?.next?.('update'); + + return Promise.resolve([ + { slug: 'mock-audit-slug', score: 0, value: 0 }, + ] satisfies AuditOutputs); + }, + { next: nextSpy }, + ); + expect(nextSpy).toHaveBeenCalledWith('update'); + expect(runnerResult.audits[0]?.slug).toBe('mock-audit-slug'); + }); + + it('should throw if plugin throws', async () => { + const nextSpy = vi.fn(); + await expect( + executeEsmRunner( + () => Promise.reject(new Error('plugin exex mock error')), + { next: nextSpy }, + ), + ).rejects.toThrow('plugin exex mock error'); + }); +}); diff --git a/packages/core/src/lib/implementation/runner.ts b/packages/core/src/lib/implementation/runner.ts index 5d5aa2597..39e7b89fb 100644 --- a/packages/core/src/lib/implementation/runner.ts +++ b/packages/core/src/lib/implementation/runner.ts @@ -1,7 +1,68 @@ -import { AuditOutputs } from '@code-pushup/models'; +import { join } from 'path'; +import { + AuditOutputs, + EsmRunnerConfig, + RunnerConfig, +} from '@code-pushup/models'; +import { + Observer, + ProcessObserver, + calcDuration, + executeProcess, + readJsonFile, +} from '@code-pushup/utils'; export type RunnerResult = { date: string; duration: number; audits: AuditOutputs; }; + +export async function executeProcessRunner( + cfg: RunnerConfig, + observer?: ProcessObserver, +): Promise { + const { args, command, outputFile, outputTransform } = cfg; + + // execute process + const { duration, date } = await executeProcess({ + command, + args, + observer, + }); + + // read process output from file system and parse it + let audits = await readJsonFile( + join(process.cwd(), outputFile), + ); + + // transform unknownAuditOutputs to auditOutputs + if (outputTransform) { + audits = await outputTransform(audits); + } + + // create runner result + return { + duration, + date, + audits, + }; +} + +export async function executeEsmRunner( + runner: EsmRunnerConfig, + observer?: Observer, +): Promise { + const date = new Date().toISOString(); + const start = performance.now(); + + // execute plugin runner + const audits = await runner(observer); + + // create runner result + return { + date, + duration: calcDuration(start), + audits, + } satisfies RunnerResult; +} From d916c9ccbde7f59690824645676e6803ea42998a Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 14 Nov 2023 15:43:26 +0100 Subject: [PATCH 07/18] refactor: remove unused file --- packages/models/test/fixtures/runner.mock.ts | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 packages/models/test/fixtures/runner.mock.ts diff --git a/packages/models/test/fixtures/runner.mock.ts b/packages/models/test/fixtures/runner.mock.ts deleted file mode 100644 index c56edcaf0..000000000 --- a/packages/models/test/fixtures/runner.mock.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { join } from 'node:path'; -import { AuditReport, RunnerConfig } from '../../src'; - -export function runnerConfig( - audits: AuditReport[], - outputFile = join('tmp', `out.${Date.now()}.json`), -): RunnerConfig { - return { - command: 'echo', - args: [`${JSON.stringify(audits)} > ${outputFile}`], - outputFile, - }; -} From 5c2d6081c76d6357ecc1385455ed4a6fe72debbb Mon Sep 17 00:00:00 2001 From: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> Date: Tue, 14 Nov 2023 17:18:13 +0100 Subject: [PATCH 08/18] Update packages/models/src/lib/plugin-config-runner.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matěj Chalk <34691111+matejchalk@users.noreply.github.com> --- packages/models/src/lib/plugin-config-runner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models/src/lib/plugin-config-runner.ts b/packages/models/src/lib/plugin-config-runner.ts index 78107e0be..59042e91c 100644 --- a/packages/models/src/lib/plugin-config-runner.ts +++ b/packages/models/src/lib/plugin-config-runner.ts @@ -33,6 +33,6 @@ export type EsmObserver = z.infer; export const esmRunnerConfigSchema = z .function() .args(esmObserver.optional()) - .returns(z.promise(auditOutputsSchema)); + .returns(z.union([auditOutputsSchema, z.promise(auditOutputsSchema)])); export type EsmRunnerConfig = z.infer; From 230d6bfbc567e87dd4fe363a6b39804275f3684b Mon Sep 17 00:00:00 2001 From: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> Date: Tue, 14 Nov 2023 17:21:41 +0100 Subject: [PATCH 09/18] Update packages/core/src/lib/implementation/runner.spec.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matěj Chalk <34691111+matejchalk@users.noreply.github.com> --- packages/core/src/lib/implementation/runner.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/lib/implementation/runner.spec.ts b/packages/core/src/lib/implementation/runner.spec.ts index 27fdfdb2d..009a20ad4 100644 --- a/packages/core/src/lib/implementation/runner.spec.ts +++ b/packages/core/src/lib/implementation/runner.spec.ts @@ -71,9 +71,9 @@ describe('executeEsmRunner', () => { const nextSpy = vi.fn(); await expect( executeEsmRunner( - () => Promise.reject(new Error('plugin exex mock error')), + () => Promise.reject(new Error('plugin exec mock error')), { next: nextSpy }, ), - ).rejects.toThrow('plugin exex mock error'); + ).rejects.toThrow('plugin exec mock error'); }); }); From f7494036cfaf728c730a895d5ea82dbe5f5f75a9 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 14 Nov 2023 19:52:55 +0100 Subject: [PATCH 10/18] refactor(utils): refactor Observer --- .../src/lib/implementation/execute-plugin.ts | 11 ++++--- .../src/lib/implementation/runner.spec.ts | 32 ++++++++++++------- .../core/src/lib/implementation/runner.ts | 25 ++++++--------- packages/models/src/index.ts | 9 ++++-- .../src/lib/plugin-config-runner.spec.ts | 2 +- .../models/src/lib/plugin-config-runner.ts | 11 ++++--- packages/models/src/lib/plugin-config.ts | 2 +- .../test/fixtures/runner-config.mock.ts | 2 +- packages/utils/src/index.ts | 1 - .../utils/src/lib/execute-process.spec.ts | 6 ++-- packages/utils/src/lib/execute-process.ts | 17 +++++----- packages/utils/src/lib/observer.ts | 5 --- 12 files changed, 64 insertions(+), 59 deletions(-) delete mode 100644 packages/utils/src/lib/observer.ts diff --git a/packages/core/src/lib/implementation/execute-plugin.ts b/packages/core/src/lib/implementation/execute-plugin.ts index 6c645577b..57c95a163 100644 --- a/packages/core/src/lib/implementation/execute-plugin.ts +++ b/packages/core/src/lib/implementation/execute-plugin.ts @@ -4,12 +4,13 @@ import { AuditOutput, AuditOutputs, AuditReport, + OnProgress, PluginConfig, PluginReport, auditOutputsSchema, } from '@code-pushup/models'; -import { Observer, getProgressBar } from '@code-pushup/utils'; -import { executeEsmRunner, executeProcessRunner } from './runner'; +import { getProgressBar } from '@code-pushup/utils'; +import { executeRunnerConfig, executeRunnerFunction } from './runner'; /** * Error thrown when plugin output is invalid. @@ -44,7 +45,7 @@ export class PluginOutputMissingAuditError extends Error { */ export async function executePlugin( pluginConfig: PluginConfig, - observer?: Observer, + onProgress?: OnProgress, ): Promise { const { runner, @@ -58,8 +59,8 @@ export async function executePlugin( // execute plugin runner const runnerResult = typeof runner === 'object' - ? await executeProcessRunner(runner, observer) - : await executeEsmRunner(runner, observer); + ? await executeRunnerConfig(runner, onProgress) + : await executeRunnerFunction(runner, onProgress); const { audits: unvalidatedAuditOutputs, ...executionMeta } = runnerResult; // validate auditOutputs diff --git a/packages/core/src/lib/implementation/runner.spec.ts b/packages/core/src/lib/implementation/runner.spec.ts index 009a20ad4..1e4faa6c1 100644 --- a/packages/core/src/lib/implementation/runner.spec.ts +++ b/packages/core/src/lib/implementation/runner.spec.ts @@ -1,14 +1,21 @@ import { describe, expect, it } from 'vitest'; -import { AuditOutputs, auditOutputsSchema } from '@code-pushup/models'; +import { + AuditOutputs, + OnProgress, + auditOutputsSchema, +} from '@code-pushup/models'; import { auditReport, echoRunnerConfig } from '@code-pushup/models/testing'; -import { Observer } from '@code-pushup/utils'; -import { RunnerResult, executeEsmRunner, executeProcessRunner } from './runner'; +import { + RunnerResult, + executeRunnerConfig, + executeRunnerFunction, +} from './runner'; const validRunnerCfg = echoRunnerConfig([auditReport()], 'output.json'); describe('executeRunner', () => { it('should work with valid plugins', async () => { - const runnerResult = await executeProcessRunner(validRunnerCfg); + const runnerResult = await executeRunnerConfig(validRunnerCfg); // data sanity expect(runnerResult.date.endsWith('Z')).toBeTruthy(); @@ -29,7 +36,7 @@ describe('executeRunner', () => { })), }; - const runnerResult = await executeProcessRunner(runnerCfgWithTransform); + const runnerResult = await executeRunnerConfig(runnerCfgWithTransform); expect(runnerResult.audits[0]?.displayValue).toBe( 'transformed - mock-audit-slug', @@ -45,7 +52,7 @@ describe('executeRunner', () => { }; await expect( - executeProcessRunner(runnerCfgWithErrorTransform), + executeRunnerConfig(runnerCfgWithErrorTransform), ).rejects.toThrow('transform mock error'); }); }); @@ -53,15 +60,15 @@ describe('executeRunner', () => { describe('executeEsmRunner', () => { it('should execute valid plugin config', async () => { const nextSpy = vi.fn(); - const runnerResult: RunnerResult = await executeEsmRunner( - (observer?: Observer) => { - observer?.next?.('update'); + const runnerResult: RunnerResult = await executeRunnerFunction( + (observer?: OnProgress) => { + observer?.('update'); return Promise.resolve([ { slug: 'mock-audit-slug', score: 0, value: 0 }, ] satisfies AuditOutputs); }, - { next: nextSpy }, + nextSpy, ); expect(nextSpy).toHaveBeenCalledWith('update'); expect(runnerResult.audits[0]?.slug).toBe('mock-audit-slug'); @@ -70,10 +77,11 @@ describe('executeEsmRunner', () => { it('should throw if plugin throws', async () => { const nextSpy = vi.fn(); await expect( - executeEsmRunner( + executeRunnerFunction( () => Promise.reject(new Error('plugin exec mock error')), - { next: nextSpy }, + nextSpy, ), ).rejects.toThrow('plugin exec mock error'); + expect(nextSpy).not.toHaveBeenCalled(); }); }); diff --git a/packages/core/src/lib/implementation/runner.ts b/packages/core/src/lib/implementation/runner.ts index 15b2376f5..359d6dc70 100644 --- a/packages/core/src/lib/implementation/runner.ts +++ b/packages/core/src/lib/implementation/runner.ts @@ -1,16 +1,11 @@ import { join } from 'path'; import { AuditOutputs, - RunnerFunction, + OnProgress, RunnerConfig, + RunnerFunction, } from '@code-pushup/models'; -import { - Observer, - ProcessObserver, - calcDuration, - executeProcess, - readJsonFile, -} from '@code-pushup/utils'; +import { calcDuration, executeProcess, readJsonFile } from '@code-pushup/utils'; export type RunnerResult = { date: string; @@ -18,9 +13,9 @@ export type RunnerResult = { audits: AuditOutputs; }; -export async function executeProcessRunner( +export async function executeRunnerConfig( cfg: RunnerConfig, - observer?: ProcessObserver, + onProgress?: OnProgress, ): Promise { const { args, command, outputFile, outputTransform } = cfg; @@ -28,7 +23,7 @@ export async function executeProcessRunner( const { duration, date } = await executeProcess({ command, args, - observer, + observer: { onStdout: onProgress }, }); // read process output from file system and parse it @@ -49,20 +44,20 @@ export async function executeProcessRunner( }; } -export async function executeEsmRunner( +export async function executeRunnerFunction( runner: RunnerFunction, - observer?: Observer, + onProgress?: OnProgress, ): Promise { const date = new Date().toISOString(); const start = performance.now(); // execute plugin runner - const audits = await runner(observer); + const audits = await runner(onProgress); // create runner result return { date, duration: calcDuration(start), audits, - } satisfies RunnerResult; + }; } diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index 19f096da3..5175f3daf 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -18,7 +18,6 @@ export { persistConfigSchema, } from './lib/persist-config'; export { PluginConfig, pluginConfigSchema } from './lib/plugin-config'; -export { RunnerConfig } from './lib/plugin-config-runner'; export { auditSchema, Audit, @@ -45,4 +44,10 @@ export { } from './lib/report'; export { UploadConfig, uploadConfigSchema } from './lib/upload-config'; export { materialIconSchema } from './lib/implementation/schemas'; -export { EsmObserver, RunnerFunction } from './lib/plugin-config-runner'; +export { + onProgressSchema, + OnProgress, + RunnerFunction, + runnerConfigSchema, + RunnerConfig, +} from './lib/plugin-config-runner'; diff --git a/packages/models/src/lib/plugin-config-runner.spec.ts b/packages/models/src/lib/plugin-config-runner.spec.ts index 4885d74d3..22e4c428d 100644 --- a/packages/models/src/lib/plugin-config-runner.spec.ts +++ b/packages/models/src/lib/plugin-config-runner.spec.ts @@ -4,8 +4,8 @@ import { runnerConfig, } from '../../test/fixtures/runner-config.mock'; import { - runnerFunctionSchema, runnerConfigSchema, + runnerFunctionSchema, } from './plugin-config-runner'; describe('runnerConfig', () => { diff --git a/packages/models/src/lib/plugin-config-runner.ts b/packages/models/src/lib/plugin-config-runner.ts index 2355f9e1a..b4d986253 100644 --- a/packages/models/src/lib/plugin-config-runner.ts +++ b/packages/models/src/lib/plugin-config-runner.ts @@ -25,14 +25,15 @@ export const runnerConfigSchema = z.object( export type RunnerConfig = z.infer; -export const esmObserver = z.object({ - next: z.function().args(z.unknown()).returns(z.void()).optional(), -}); -export type EsmObserver = z.infer; +export const onProgressSchema = z + .function() + .args(z.unknown()) + .returns(z.void()); +export type OnProgress = z.infer; export const runnerFunctionSchema = z .function() - .args(esmObserver.optional()) + .args(onProgressSchema.optional()) .returns(z.union([auditOutputsSchema, z.promise(auditOutputsSchema)])); export type RunnerFunction = z.infer; diff --git a/packages/models/src/lib/plugin-config.ts b/packages/models/src/lib/plugin-config.ts index c8f2d3bfc..b64f1fbb7 100644 --- a/packages/models/src/lib/plugin-config.ts +++ b/packages/models/src/lib/plugin-config.ts @@ -9,8 +9,8 @@ import { errorItems, hasMissingStrings } from './implementation/utils'; import { pluginAuditsSchema } from './plugin-config-audits'; import { auditGroupsSchema } from './plugin-config-groups'; import { - runnerFunctionSchema, runnerConfigSchema, + runnerFunctionSchema, } from './plugin-config-runner'; export const pluginMetaSchema = packageVersionSchema({ diff --git a/packages/models/test/fixtures/runner-config.mock.ts b/packages/models/test/fixtures/runner-config.mock.ts index fba044f39..cb51fc357 100644 --- a/packages/models/test/fixtures/runner-config.mock.ts +++ b/packages/models/test/fixtures/runner-config.mock.ts @@ -2,8 +2,8 @@ import { platform } from 'os'; import { RunnerConfig } from '../../src'; import { RunnerFunction, - runnerFunctionSchema, runnerConfigSchema, + runnerFunctionSchema, } from '../../src/lib/plugin-config-runner'; import { AuditOutput, AuditOutputs } from '../../src/lib/plugin-process-output'; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index b5809bb30..ae501640a 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -43,4 +43,3 @@ export { slugify, } from './lib/transformation'; export { NEW_LINE } from './lib/md'; -export { Observer } from './lib/observer'; diff --git a/packages/utils/src/lib/execute-process.spec.ts b/packages/utils/src/lib/execute-process.spec.ts index d32dd7f8b..db4538640 100644 --- a/packages/utils/src/lib/execute-process.spec.ts +++ b/packages/utils/src/lib/execute-process.spec.ts @@ -18,7 +18,7 @@ describe('executeProcess', () => { const cfg = mockProcessConfig({ command: `npx`, args: ['--help'] }); const { observer } = cfg; const processResult = await executeProcess(cfg); - expect(observer?.next).toHaveBeenCalledTimes(1); + expect(observer?.onStdout).toHaveBeenCalledTimes(1); expect(observer?.complete).toHaveBeenCalledTimes(1); expect(processResult.stdout).toContain('Options'); }); @@ -32,7 +32,7 @@ describe('executeProcess', () => { const processResult = await executeProcess(cfg).catch(errorSpy); expect(errorSpy).toHaveBeenCalledTimes(0); expect(processResult.stdout).toContain('process:complete'); - expect(observer?.next).toHaveBeenCalledTimes(6); + expect(observer?.onStdout).toHaveBeenCalledTimes(6); expect(observer?.error).toHaveBeenCalledTimes(0); expect(observer?.complete).toHaveBeenCalledTimes(1); }); @@ -47,7 +47,7 @@ describe('executeProcess', () => { expect(errorSpy).toHaveBeenCalledTimes(1); expect(processResult).toBeUndefined(); expect(observer?.complete).toHaveBeenCalledTimes(0); - expect(observer?.next).toHaveBeenCalledTimes(2); + expect(observer?.onStdout).toHaveBeenCalledTimes(2); expect(observer?.error).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/utils/src/lib/execute-process.ts b/packages/utils/src/lib/execute-process.ts index 803bc9c16..e8a816d29 100644 --- a/packages/utils/src/lib/execute-process.ts +++ b/packages/utils/src/lib/execute-process.ts @@ -1,5 +1,4 @@ import { spawn } from 'child_process'; -import { Observer } from './observer'; import { calcDuration } from './report'; /** @@ -86,20 +85,22 @@ export type ProcessConfig = { }; /** - * Process observer object. Contains the next, error and complete function. + * Process observer object. Contains the onStdout, error and complete function. * @category Types * @public - * @property {function} next - The next function of the observer (optional). + * @property {function} onStdout - The onStdout function of the observer (optional). * @property {function} error - The error function of the observer (optional). * @property {function} complete - The complete function of the observer (optional). * * @example * const observer = { - * next: (stdout) => console.log(stdout) + * onStdout: (stdout) => console.log(stdout) * } */ -export type ProcessObserver = Omit & { +export type ProcessObserver = { + onStdout?: (stdout: string) => void; error?: (error: ProcessError) => void; + complete?: () => void; }; /** @@ -120,7 +121,7 @@ export type ProcessObserver = Omit & { * command: 'node', * args: ['download-data.js'], * observer: { - * next: updateProgress, + * onStdout: updateProgress, * error: handleError, * complete: cleanLogs, * } @@ -132,7 +133,7 @@ export type ProcessObserver = Omit & { */ export function executeProcess(cfg: ProcessConfig): Promise { const { observer, cwd } = cfg; - const { next, error, complete } = observer || {}; + const { onStdout, error, complete } = observer || {}; const date = new Date().toISOString(); const start = performance.now(); @@ -143,7 +144,7 @@ export function executeProcess(cfg: ProcessConfig): Promise { process.stdout.on('data', data => { stdout += data.toString(); - next?.(data); + onStdout?.(data); }); process.stderr.on('data', data => { diff --git a/packages/utils/src/lib/observer.ts b/packages/utils/src/lib/observer.ts deleted file mode 100644 index b633656bf..000000000 --- a/packages/utils/src/lib/observer.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type Observer = { - next?: (value: unknown) => void; - error?: (error: unknown) => void; - complete?: () => void; -}; From e8ce9b14c31fc69ed591d57ced57249d7fe4ad85 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 14 Nov 2023 20:01:40 +0100 Subject: [PATCH 11/18] refactor(utils): fix tests --- packages/models/src/lib/plugin-config-runner.spec.ts | 8 +++----- packages/models/test/fixtures/runner-config.mock.ts | 12 ------------ packages/models/test/index.ts | 5 +---- packages/utils/test/process-helper.mock.ts | 4 ++-- 4 files changed, 6 insertions(+), 23 deletions(-) diff --git a/packages/models/src/lib/plugin-config-runner.spec.ts b/packages/models/src/lib/plugin-config-runner.spec.ts index 22e4c428d..12b88e01f 100644 --- a/packages/models/src/lib/plugin-config-runner.spec.ts +++ b/packages/models/src/lib/plugin-config-runner.spec.ts @@ -1,8 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { - esmRunnerConfig, - runnerConfig, -} from '../../test/fixtures/runner-config.mock'; +import { auditReport } from '../../test/fixtures/plugin-config.mock'; +import { runnerConfig } from '../../test/fixtures/runner-config.mock'; import { runnerConfigSchema, runnerFunctionSchema, @@ -32,7 +30,7 @@ describe('runnerConfig', () => { describe('esmRunnerConfig', () => { it('should parse if configuration is valid', () => { - const runnerConfigMock = esmRunnerConfig(); + const runnerConfigMock = () => Promise.resolve([auditReport()]); expect(() => runnerFunctionSchema.parse(runnerConfigMock)).not.toThrow(); }); diff --git a/packages/models/test/fixtures/runner-config.mock.ts b/packages/models/test/fixtures/runner-config.mock.ts index cb51fc357..5e1b8a063 100644 --- a/packages/models/test/fixtures/runner-config.mock.ts +++ b/packages/models/test/fixtures/runner-config.mock.ts @@ -41,15 +41,3 @@ export function echoRunnerConfig( outputFile, }; } - -/** - * Use this helper as a general purpose mock with working defaults - * @param options - */ -export function esmRunnerConfig( - options?: Partial, -): RunnerFunction { - return runnerFunctionSchema.parse((cfg: unknown): AuditOutputs => { - return [{} as AuditOutput]; - }); -} diff --git a/packages/models/test/index.ts b/packages/models/test/index.ts index 4ad551ff5..d5e825d46 100644 --- a/packages/models/test/index.ts +++ b/packages/models/test/index.ts @@ -5,10 +5,7 @@ export * from './memfs'; export { eslintPluginConfig } from './fixtures/eslint-plugin.mock'; export { lighthousePluginConfig } from './fixtures/lighthouse-plugin.mock'; -export { - echoRunnerConfig, - esmRunnerConfig, -} from './fixtures/runner-config.mock'; +export { echoRunnerConfig } from './fixtures/runner-config.mock'; export { persistConfig } from './fixtures/persist-config.mock'; export { auditConfig, diff --git a/packages/utils/test/process-helper.mock.ts b/packages/utils/test/process-helper.mock.ts index 17468b661..2b83b9d4b 100644 --- a/packages/utils/test/process-helper.mock.ts +++ b/packages/utils/test/process-helper.mock.ts @@ -46,11 +46,11 @@ export function mockProcessConfig( * Helps to set up spy observers for testing. */ export function spyObserver() { - const nextSpy = vi.fn(); + const onStdoutSpy = vi.fn(); const errorSpy = vi.fn(); const completeSpy = vi.fn(); return { - next: nextSpy, + onStdout: onStdoutSpy, error: errorSpy, complete: completeSpy, }; From 059ba90a2e8036f6c10575f78578dfae3cd9b8cb Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 14 Nov 2023 20:10:53 +0100 Subject: [PATCH 12/18] refactor(utils): adopt tests --- .../lib/implementation/execute-plugin.spec.ts | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/core/src/lib/implementation/execute-plugin.spec.ts b/packages/core/src/lib/implementation/execute-plugin.spec.ts index 99b3e4b7a..cdcb32d90 100644 --- a/packages/core/src/lib/implementation/execute-plugin.spec.ts +++ b/packages/core/src/lib/implementation/execute-plugin.spec.ts @@ -1,12 +1,13 @@ import { describe, expect, it } from 'vitest'; import { AuditOutputs, + OnProgress, PluginConfig, RunnerConfig, + RunnerFunction, auditOutputsSchema, } from '@code-pushup/models'; import { auditReport, pluginConfig } from '@code-pushup/models/testing'; -import { Observer } from '@code-pushup/utils'; import { DEFAULT_TESTING_CLI_OPTIONS } from '../../../test/constants'; import { PluginOutputMissingAuditError, @@ -43,7 +44,7 @@ describe('executePlugin', () => { ); }); - it('should work with valid process runner config', async () => { + it('should work with valid runner config', async () => { const runnerConfig = validPluginCfg.runner as RunnerConfig; const pluginCfg: PluginConfig = { ...validPluginCfg, @@ -58,9 +59,9 @@ describe('executePlugin', () => { expect(() => auditOutputsSchema.parse(pluginResult.audits)).not.toThrow(); }); - it('should work with valid esm runner config', async () => { - const esmRunnerConfig = (observer?: Observer) => { - observer?.next?.('update'); + it('should work with valid runner function', async () => { + const runnerFunction = (onProgress?: OnProgress) => { + onProgress?.('update'); return Promise.resolve([ { slug: 'mock-audit-slug', score: 0, value: 0 }, ] satisfies AuditOutputs); @@ -68,18 +69,28 @@ describe('executePlugin', () => { const pluginCfg: PluginConfig = { ...validPluginCfg, - runner: esmRunnerConfig, + runner: runnerFunction, }; const pluginResult = await executePlugin(pluginCfg); expect(pluginResult.audits[0]?.slug).toBe('mock-audit-slug'); expect(() => auditOutputsSchema.parse(pluginResult.audits)).not.toThrow(); }); - it('should throw if invalid runnerOutput is produced with transform', async () => { + it('should throw with invalid runner config', async () => { const pluginCfg: PluginConfig = { ...validPluginCfg, - runner: (observer?: Observer) => { - observer?.next?.('update'); + runner: '' as unknown as RunnerFunction, + }; + const pluginResult = await executePlugin(pluginCfg); + expect(pluginResult.audits[0]?.slug).toBe('mock-audit-slug'); + expect(() => auditOutputsSchema.parse(pluginResult.audits)).not.toThrow(); + }); + + it('should throw if invalid runnerOutput', async () => { + const pluginCfg: PluginConfig = { + ...validPluginCfg, + runner: (onProgress?: OnProgress) => { + onProgress?.('update'); return Promise.resolve([ { slug: '-mock-audit-slug', score: 0, value: 0 }, From 5f38f029b742fca97bac89274b64e8344ff97fb5 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 14 Nov 2023 20:14:05 +0100 Subject: [PATCH 13/18] refactor(utils): adopt tests --- packages/core/src/lib/implementation/execute-plugin.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/lib/implementation/execute-plugin.spec.ts b/packages/core/src/lib/implementation/execute-plugin.spec.ts index cdcb32d90..cda58837c 100644 --- a/packages/core/src/lib/implementation/execute-plugin.spec.ts +++ b/packages/core/src/lib/implementation/execute-plugin.spec.ts @@ -81,9 +81,9 @@ describe('executePlugin', () => { ...validPluginCfg, runner: '' as unknown as RunnerFunction, }; - const pluginResult = await executePlugin(pluginCfg); - expect(pluginResult.audits[0]?.slug).toBe('mock-audit-slug'); - expect(() => auditOutputsSchema.parse(pluginResult.audits)).not.toThrow(); + await expect(executePlugin(pluginCfg)).rejects.toThrow( + 'runner is not a function', + ); }); it('should throw if invalid runnerOutput', async () => { From 38a804f9251878ee4d58da4822284eb7885fd122 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 14 Nov 2023 20:16:01 +0100 Subject: [PATCH 14/18] refactor(utils): adopt tests --- packages/core/src/lib/implementation/runner.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/lib/implementation/runner.spec.ts b/packages/core/src/lib/implementation/runner.spec.ts index 1e4faa6c1..e88f87f9e 100644 --- a/packages/core/src/lib/implementation/runner.spec.ts +++ b/packages/core/src/lib/implementation/runner.spec.ts @@ -13,7 +13,7 @@ import { const validRunnerCfg = echoRunnerConfig([auditReport()], 'output.json'); -describe('executeRunner', () => { +describe('executeRunnerConfig', () => { it('should work with valid plugins', async () => { const runnerResult = await executeRunnerConfig(validRunnerCfg); @@ -57,7 +57,7 @@ describe('executeRunner', () => { }); }); -describe('executeEsmRunner', () => { +describe('executeRunnerFunction', () => { it('should execute valid plugin config', async () => { const nextSpy = vi.fn(); const runnerResult: RunnerResult = await executeRunnerFunction( From a4c2237384066b39025218c91ccad34b1646620f Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 14 Nov 2023 20:18:16 +0100 Subject: [PATCH 15/18] refactor(utils): adopt tests --- packages/models/src/lib/plugin-config-runner.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models/src/lib/plugin-config-runner.spec.ts b/packages/models/src/lib/plugin-config-runner.spec.ts index 12b88e01f..a8f82f15b 100644 --- a/packages/models/src/lib/plugin-config-runner.spec.ts +++ b/packages/models/src/lib/plugin-config-runner.spec.ts @@ -28,7 +28,7 @@ describe('runnerConfig', () => { }); }); -describe('esmRunnerConfig', () => { +describe('runnerFunction', () => { it('should parse if configuration is valid', () => { const runnerConfigMock = () => Promise.resolve([auditReport()]); expect(() => runnerFunctionSchema.parse(runnerConfigMock)).not.toThrow(); From b08434c075b46e7994f5f104e173976043ad11ca Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 14 Nov 2023 20:24:26 +0100 Subject: [PATCH 16/18] refactor(utils): adopt tests --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b7b838766..344a0ed59 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ .env +.nx + # compiled output dist tmp @@ -42,4 +44,4 @@ testem.log Thumbs.db # generated Code PushUp reports -/.code-pushup \ No newline at end of file +/.code-pushup From 8e59953293d4b28e6add3b2a76fa5d80dd3d79a0 Mon Sep 17 00:00:00 2001 From: Michael Hladky <10064416+BioPhoton@users.noreply.github.com> Date: Wed, 15 Nov 2023 14:16:12 +0100 Subject: [PATCH 17/18] Update packages/core/src/lib/implementation/execute-plugin.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matěj Chalk <34691111+matejchalk@users.noreply.github.com> --- packages/core/src/lib/implementation/execute-plugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/lib/implementation/execute-plugin.ts b/packages/core/src/lib/implementation/execute-plugin.ts index 57c95a163..c0ec49336 100644 --- a/packages/core/src/lib/implementation/execute-plugin.ts +++ b/packages/core/src/lib/implementation/execute-plugin.ts @@ -26,7 +26,7 @@ export class PluginOutputMissingAuditError extends Error { * * @public * @param pluginConfig - {@link ProcessConfig} object with runner and meta - * @param observer - process {@link Observer} + * @param onProgress - progress handler {@link OnProgress} * @returns {Promise} - audit outputs from plugin runner * @throws {PluginOutputMissingAuditError} - if plugin runner output is invalid * From 8ea1fc19c3fd7d91177e38ac2796117f5188817a Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 15 Nov 2023 14:24:32 +0100 Subject: [PATCH 18/18] refactor: implement review --- packages/utils/src/lib/execute-process.spec.ts | 10 +++++----- packages/utils/src/lib/execute-process.ts | 14 +++++++------- packages/utils/test/process-helper.mock.ts | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/utils/src/lib/execute-process.spec.ts b/packages/utils/src/lib/execute-process.spec.ts index db4538640..2fee3965a 100644 --- a/packages/utils/src/lib/execute-process.spec.ts +++ b/packages/utils/src/lib/execute-process.spec.ts @@ -19,7 +19,7 @@ describe('executeProcess', () => { const { observer } = cfg; const processResult = await executeProcess(cfg); expect(observer?.onStdout).toHaveBeenCalledTimes(1); - expect(observer?.complete).toHaveBeenCalledTimes(1); + expect(observer?.onComplete).toHaveBeenCalledTimes(1); expect(processResult.stdout).toContain('Options'); }); @@ -33,8 +33,8 @@ describe('executeProcess', () => { expect(errorSpy).toHaveBeenCalledTimes(0); expect(processResult.stdout).toContain('process:complete'); expect(observer?.onStdout).toHaveBeenCalledTimes(6); - expect(observer?.error).toHaveBeenCalledTimes(0); - expect(observer?.complete).toHaveBeenCalledTimes(1); + expect(observer?.onError).toHaveBeenCalledTimes(0); + expect(observer?.onComplete).toHaveBeenCalledTimes(1); }); it('should work with async script `node custom-script.js --arg` that throws an error', async () => { @@ -46,9 +46,9 @@ describe('executeProcess', () => { const processResult = await executeProcess(cfg).catch(errorSpy); expect(errorSpy).toHaveBeenCalledTimes(1); expect(processResult).toBeUndefined(); - expect(observer?.complete).toHaveBeenCalledTimes(0); + expect(observer?.onComplete).toHaveBeenCalledTimes(0); expect(observer?.onStdout).toHaveBeenCalledTimes(2); - expect(observer?.error).toHaveBeenCalledTimes(1); + expect(observer?.onError).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/utils/src/lib/execute-process.ts b/packages/utils/src/lib/execute-process.ts index e8a816d29..a49c12e81 100644 --- a/packages/utils/src/lib/execute-process.ts +++ b/packages/utils/src/lib/execute-process.ts @@ -89,8 +89,8 @@ export type ProcessConfig = { * @category Types * @public * @property {function} onStdout - The onStdout function of the observer (optional). - * @property {function} error - The error function of the observer (optional). - * @property {function} complete - The complete function of the observer (optional). + * @property {function} onError - The error function of the observer (optional). + * @property {function} onComplete - The complete function of the observer (optional). * * @example * const observer = { @@ -99,8 +99,8 @@ export type ProcessConfig = { */ export type ProcessObserver = { onStdout?: (stdout: string) => void; - error?: (error: ProcessError) => void; - complete?: () => void; + onError?: (error: ProcessError) => void; + onComplete?: () => void; }; /** @@ -133,7 +133,7 @@ export type ProcessObserver = { */ export function executeProcess(cfg: ProcessConfig): Promise { const { observer, cwd } = cfg; - const { onStdout, error, complete } = observer || {}; + const { onStdout, onError, onComplete } = observer || {}; const date = new Date().toISOString(); const start = performance.now(); @@ -158,11 +158,11 @@ export function executeProcess(cfg: ProcessConfig): Promise { process.on('close', code => { const timings = { date, duration: calcDuration(start) }; if (code === 0) { - complete?.(); + onComplete?.(); resolve({ code, stdout, stderr, ...timings }); } else { const errorMsg = new ProcessError({ code, stdout, stderr, ...timings }); - error?.(errorMsg); + onError?.(errorMsg); reject(errorMsg); } }); diff --git a/packages/utils/test/process-helper.mock.ts b/packages/utils/test/process-helper.mock.ts index 2b83b9d4b..0c0ac1ad4 100644 --- a/packages/utils/test/process-helper.mock.ts +++ b/packages/utils/test/process-helper.mock.ts @@ -1,6 +1,6 @@ import { join } from 'path'; import { vi } from 'vitest'; -import { ProcessConfig } from '../src'; +import { ProcessConfig, ProcessObserver } from '../src'; const asyncProcessPath = join(__dirname, './fixtures/execute-process.mock.mjs'); @@ -45,13 +45,13 @@ export function mockProcessConfig( /** * Helps to set up spy observers for testing. */ -export function spyObserver() { +export function spyObserver(): ProcessObserver { const onStdoutSpy = vi.fn(); const errorSpy = vi.fn(); const completeSpy = vi.fn(); return { onStdout: onStdoutSpy, - error: errorSpy, - complete: completeSpy, + onError: errorSpy, + onComplete: completeSpy, }; }