diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 3662fedca..0a7e93505 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -5,7 +5,7 @@ export { } from './lib/implementation/persist'; export { executePlugins, - PluginOutputError, + PluginOutputMissingAuditError, } from './lib/implementation/execute-plugin'; export { collect, CollectOptions } from './lib/implementation/collect'; export { upload, UploadOptions } from './lib/upload'; 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..2e15f2dc2 --- /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..76cff91d3 --- /dev/null +++ b/packages/core/src/lib/implementation/execute-esm-runner.ts @@ -0,0 +1,25 @@ +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/core/src/lib/implementation/execute-plugin.spec.ts b/packages/core/src/lib/implementation/execute-plugin.spec.ts index bd8df9760..96a5c9675 100644 --- a/packages/core/src/lib/implementation/execute-plugin.spec.ts +++ b/packages/core/src/lib/implementation/execute-plugin.spec.ts @@ -1,5 +1,6 @@ import { join } from 'path'; import { describe, expect, it } from 'vitest'; +import { PluginConfig, pluginReportSchema } from '@code-pushup/models'; import { AuditReport, PluginConfig, @@ -9,6 +10,10 @@ import { auditReport, echoRunnerConfig, pluginConfig, +} from '@code-pushup/models/testing'; + auditReport, + outputFileToAuditOutputs, + pluginConfig, } from '@code-pushup/models/testing'; import { DEFAULT_TESTING_CLI_OPTIONS } from '../../../test/constants'; import { executePlugin, executePlugins } from './execute-plugin'; @@ -32,7 +37,7 @@ describe('executePlugin', () => { it('should execute valid plugin config', async () => { const pluginResult = await executePlugin(validPluginCfg); expect(pluginResult.audits[0]?.slug).toBe('mock-audit-slug'); - expect(() => auditOutputsSchema.parse(pluginResult.audits)).not.toThrow(); + expect(() => pluginReportSchema.parse(pluginResult.audits)).not.toThrow(); }); it('should throws with invalid plugin audits slug', async () => { @@ -42,15 +47,15 @@ describe('executePlugin', () => { ); }); - it('should throw if invalid runnerOutput is produced', async () => { - const invalidAuditOutputs: AuditReport[] = [ - { p: 42 } as unknown as AuditReport, - ]; - const pluginCfg = pluginConfig([auditReport()]); - pluginCfg.runner = echoRunnerConfig( - invalidAuditOutputs, - join('tmp', 'out.json'), - ); + it('should throw if invalid runnerOutput is produced with transform', async () => { + const pluginCfg: PluginConfig = { + ...validPluginCfg, + runner: { + ...validPluginCfg.runner, + outputFileToAuditResults: outputFileToAuditOutputs(), + }, + }; + await expect(() => executePlugin(pluginCfg)).rejects.toThrow( /Plugin output of plugin .* is invalid./, ); @@ -60,19 +65,10 @@ describe('executePlugin', () => { describe('executePlugins', () => { it('should work with valid plugins', async () => { const plugins = [validPluginCfg, validPluginCfg2]; - const pluginResult = await executePlugins(plugins, DEFAULT_OPTIONS); - expect(pluginResult[0]?.date.endsWith('Z')).toBeTruthy(); - expect(pluginResult[0]?.duration).toBeTruthy(); + const pluginResult = await executePlugins(plugins, DEFAULT_OPTIONS); - expect(pluginResult[0]?.audits[0]?.slug).toBe('mock-audit-slug'); - expect(pluginResult[1]?.audits[0]?.slug).toBe('mock-audit-slug'); - expect(() => - auditOutputsSchema.parse(pluginResult[0]?.audits), - ).not.toThrow(); - expect(() => - auditOutputsSchema.parse(pluginResult[1]?.audits), - ).not.toThrow(); + expect(() => pluginReportSchema.parse(pluginResult)).not.toThrow(); }); it('should throws with invalid plugins', async () => { diff --git a/packages/core/src/lib/implementation/execute-plugin.ts b/packages/core/src/lib/implementation/execute-plugin.ts index b6f4e00cd..385c4f152 100644 --- a/packages/core/src/lib/implementation/execute-plugin.ts +++ b/packages/core/src/lib/implementation/execute-plugin.ts @@ -1,29 +1,23 @@ import chalk from 'chalk'; -import { readFile } from 'fs/promises'; -import { join } from 'path'; import { + AuditOutputs, + AuditReport, PluginConfig, PluginReport, - auditOutputsSchema, + RunnerResult, + auditReportSchema, } from '@code-pushup/models'; -import { - ProcessObserver, - executeProcess, - getProgressBar, -} from '@code-pushup/utils'; +import { ProcessObserver, getProgressBar } from '@code-pushup/utils'; +import { executeRunner } from './execute-runner'; /** * Error thrown when plugin output is invalid. */ -export class PluginOutputError extends Error { - constructor(pluginSlug: string, error?: Error) { +export class PluginOutputMissingAuditError extends Error { + constructor(auditSlug: string, pluginSlug: string) { super( - `Plugin output of plugin with slug ${pluginSlug} is invalid. \n Error: ${error?.message}`, + `Audit metadata not found for slug ${auditSlug} from plugin ${pluginSlug}`, ); - if (error) { - this.name = error.name; - this.stack = error.stack; - } } } @@ -34,7 +28,7 @@ export class PluginOutputError extends Error { * @param pluginConfig - {@link ProcessConfig} object with runner and meta * @param observer - process {@link ProcessObserver} * @returns {Promise} - audit outputs from plugin runner - * @throws {PluginOutputError} - if plugin runner output is invalid + * @throws {PluginOutputMissingAuditError} - if plugin runner output is invalid * * @example * // plugin execution @@ -54,70 +48,41 @@ export async function executePlugin( observer?: ProcessObserver, ): Promise { const { - slug, - title, - icon, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + audits: _, description, docsUrl, - version, - packageName, groups, + ...pluginMeta } = pluginConfig; - const { args, command } = pluginConfig.runner; - - const { duration, date } = await executeProcess({ - command, - args, + const runnerResult: RunnerResult = await executeRunner( + pluginConfig.runner, observer, - }); + ); + const { audits: runnerAuditOutputs, ...executionMeta } = runnerResult; - try { - const processOutputPath = join( - process.cwd(), - pluginConfig.runner.outputFile, - ); - - // read process output from file system and parse it - const auditOutputs = auditOutputsSchema.parse( - JSON.parse((await readFile(processOutputPath)).toString()), - ); + // read process output from file system and parse it + /*auditOutputsCorrelateWithPluginOutput( + runnerAuditOutputs, + pluginConfig.audits, + );*/ - const audits = auditOutputs.map(auditOutput => { - const auditMetadata = pluginConfig.audits.find( - audit => audit.slug === auditOutput.slug, - ); - if (!auditMetadata) { - throw new PluginOutputError( - slug, - new Error( - `Audit metadata not found for slug ${auditOutput.slug} from runner output`, - ), - ); - } - return { - ...auditOutput, - ...auditMetadata, - }; + // enrich `AuditOutputs` to `AuditReport` + const audits: AuditReport[] = runnerAuditOutputs.map(auditOutput => { + return auditReportSchema.parse({ + ...auditOutput, + ...pluginConfig.audits.find(audit => audit.slug === auditOutput.slug), }); + }); - // @TODO consider just resting/spreading the values - return { - version, - packageName, - slug, - title, - icon, - date, - duration, - audits, - ...(description && { description }), - ...(docsUrl && { docsUrl }), - ...(groups && { groups }), - } satisfies PluginReport; - } catch (error) { - const e = error as Error; - throw new PluginOutputError(slug, e); - } + return { + ...pluginMeta, + ...executionMeta, + audits, + ...(description && { description }), + ...(docsUrl && { docsUrl }), + ...(groups && { groups }), + } satisfies PluginReport; } /** @@ -165,3 +130,17 @@ export async function executePlugins( return pluginsResult; } + +function auditOutputsCorrelateWithPluginOutput( + auditOutputs: AuditOutputs, + pluginConfigAudits: PluginConfig['audits'], +) { + auditOutputs.forEach(auditOutput => { + const auditMetadata = pluginConfigAudits.find( + audit => audit.slug === auditOutput.slug, + ); + if (!auditMetadata) { + throw new Error(`Missing audit ${auditOutput.slug}.`); + } + }); +} diff --git a/packages/core/src/lib/implementation/execute-runner.spec.ts b/packages/core/src/lib/implementation/execute-runner.spec.ts new file mode 100644 index 000000000..d3fb61a5d --- /dev/null +++ b/packages/core/src/lib/implementation/execute-runner.spec.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from 'vitest'; +import { runnerResultSchema } from '@code-pushup/models'; +import { + auditReport, + echoRunnerConfig, + outputFileToAuditOutputs, +} from '@code-pushup/models/testing'; +import { executeRunner } from './execute-runner'; + +const validRunnerCfg = echoRunnerConfig([auditReport()], 'output.json'); + +describe('executeRunner', () => { + it('should work with valid plugins', async () => { + const runnerResult = await executeRunner(validRunnerCfg); + + // data sanity + expect(runnerResult.date.endsWith('Z')).toBeTruthy(); + expect(runnerResult.duration).toBeTruthy(); + expect(runnerResult.audits[0]?.slug).toBe('mock-audit-slug'); + + // schema validation + expect(() => runnerResultSchema.parse(runnerResult.audits)).not.toThrow(); + }); + + it('should use transform if provided', async () => { + const runnerCfgWithTransform = { + ...validRunnerCfg, + outputFileToAuditResults: outputFileToAuditOutputs(), + }; + + const runnerResult = await executeRunner(runnerCfgWithTransform); + + expect(runnerResult.audits[0]?.displayValue).toBe( + 'transformed - mock-audit-slug', + ); + }); +}); diff --git a/packages/core/src/lib/implementation/execute-runner.ts b/packages/core/src/lib/implementation/execute-runner.ts new file mode 100644 index 000000000..92292975d --- /dev/null +++ b/packages/core/src/lib/implementation/execute-runner.ts @@ -0,0 +1,41 @@ +import { join } from 'path'; +import { + AuditOutputs, + RunnerConfig, + RunnerResult, + runnerResultSchema, +} from '@code-pushup/models'; +import { + ProcessObserver, + executeProcess, + readJsonFile, +} from '@code-pushup/utils'; + +export async function executeRunner( + cfg: RunnerConfig, + observer?: ProcessObserver, +): Promise { + const { args, command, outputFile, outputFileToAuditResults } = cfg; + + 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 (outputFileToAuditResults) { + audits = outputFileToAuditResults(audits) as RunnerResult['audits']; + } + + return runnerResultSchema.parse({ + duration, + date, + audits, + }); +} diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index bea662fd6..68c6ac0a6 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -18,18 +18,31 @@ export { persistConfigSchema, } from './lib/persist-config'; export { PluginConfig, pluginConfigSchema } from './lib/plugin-config'; -export { RunnerConfig } from './lib/plugin-config-runner'; +export { + RunnerConfig, + RunnerResult, + runnerResultSchema, + OutputFileToAuditOutputs, + EsmRunnerConfig, + EsmObserver, +} from './lib/plugin-config-runner'; export { auditSchema, Audit, pluginAuditsSchema, + PluginAudits, } from './lib/plugin-config-audits'; export { AuditGroupRef, AuditGroup, auditGroupSchema, } from './lib/plugin-config-groups'; -export { AuditOutput, auditOutputsSchema } from './lib/plugin-process-output'; +export { + AuditOutput, + AuditOutputs, + auditOutputsSchema, + PluginOutput, +} from './lib/plugin-process-output'; export { Issue, IssueSeverity } from './lib/plugin-process-output-audit-issue'; export { AuditReport, @@ -37,6 +50,10 @@ export { Report, pluginReportSchema, reportSchema, + auditReportSchema, } from './lib/report'; export { UploadConfig, uploadConfigSchema } from './lib/upload-config'; -export { materialIconSchema } from './lib/implementation/schemas'; +export { + materialIconSchema, + ExecutionMeta, +} from './lib/implementation/schemas'; diff --git a/packages/models/src/lib/implementation/schemas.ts b/packages/models/src/lib/implementation/schemas.ts index 351c79da6..01b49f967 100644 --- a/packages/models/src/lib/implementation/schemas.ts +++ b/packages/models/src/lib/implementation/schemas.ts @@ -19,6 +19,7 @@ export function executionMetaSchema( duration: z.number({ description: options.descriptionDuration }), }); } +export type ExecutionMeta = z.infer>; /** * Schema for a slug of a categories, plugins or audits. diff --git a/packages/models/src/lib/plugin-config-audits.ts b/packages/models/src/lib/plugin-config-audits.ts index 5ed08bbaf..b4b0a5051 100644 --- a/packages/models/src/lib/plugin-config-audits.ts +++ b/packages/models/src/lib/plugin-config-audits.ts @@ -27,7 +27,7 @@ export const pluginAuditsSchema = z message: duplicateSlugsInAuditsErrorMsg(auditMetadata), }), ); - +export type PluginAudits = z.infer; // ======================= // helper for validator: audit slugs are unique 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..590275d86 100644 --- a/packages/models/src/lib/plugin-config-runner.ts +++ b/packages/models/src/lib/plugin-config-runner.ts @@ -1,5 +1,12 @@ import { z } from 'zod'; -import { filePathSchema } from './implementation/schemas'; +import { executionMetaSchema, filePathSchema } from './implementation/schemas'; +import { AuditOutputs, auditOutputsSchema } from './plugin-process-output'; + +// @TODO add returns definition as AuditOutputs - this fails ATM when compiling +export const outputFileToAuditOutputsSchema = z.function().args(z.unknown()); + +// @TODO us z.infer; +export type OutputFileToAuditOutputs = (d: unknown) => AuditOutputs; export const runnerConfigSchema = z.object( { @@ -8,6 +15,7 @@ export const runnerConfigSchema = z.object( }), args: z.array(z.string({ description: 'Command arguments' })).optional(), outputFile: filePathSchema('Output path'), + outputFileToAuditResults: outputFileToAuditOutputsSchema.optional(), }, { description: 'How to execute runner', @@ -15,3 +23,27 @@ export const runnerConfigSchema = z.object( ); export type RunnerConfig = z.infer; + +export const runnerResultSchema = executionMetaSchema().merge( + z.object( + { + audits: auditOutputsSchema, + }, + { + 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/src/lib/plugin-config.spec.ts b/packages/models/src/lib/plugin-config.spec.ts index 88e2a50a0..5a9c76217 100644 --- a/packages/models/src/lib/plugin-config.spec.ts +++ b/packages/models/src/lib/plugin-config.spec.ts @@ -1,29 +1,37 @@ import { describe, expect, it } from 'vitest'; -import { config } from '../../test'; +import { + auditReport, + config, + echoRunnerConfig, + lighthousePluginConfig, + outputFileToAuditOutputs, + pluginConfig, + report, +} from '../../test'; import { pluginConfigSchema } from './plugin-config'; describe('pluginConfigSchema', () => { it('should parse if plugin configuration is valid', () => { - const pluginConfig = config().plugins[0]; - expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); + const pluginCfg = pluginConfig([auditReport()]); + expect(() => pluginConfigSchema.parse(pluginCfg)).not.toThrow(); }); it('should throw if plugin slug has a invalid pattern', () => { const invalidPluginSlug = '-invalid-plugin-slug'; - const pluginConfig = config().plugins[0]; - pluginConfig.slug = invalidPluginSlug; + const pluginCfg = pluginConfig([auditReport()]); + pluginCfg.slug = invalidPluginSlug; - expect(() => pluginConfigSchema.parse(pluginConfig)).toThrow( + expect(() => pluginConfigSchema.parse(pluginCfg)).toThrow( `slug has to follow the pattern`, ); }); it('should throw if plugin audits contain invalid slugs', () => { const invalidAuditRef = '-invalid-audit-slug'; - const pluginConfig = config().plugins[0]; - pluginConfig.audits[0].slug = invalidAuditRef; + const pluginCfg = pluginConfig([auditReport()]); + pluginCfg.audits[0].slug = invalidAuditRef; - expect(() => pluginConfigSchema.parse(pluginConfig)).toThrow( + expect(() => pluginConfigSchema.parse(pluginCfg)).toThrow( `slug has to follow the patter`, ); }); @@ -39,7 +47,7 @@ describe('pluginConfigSchema', () => { it('should throw if plugin groups contain invalid slugs', () => { const invalidGroupSlug = '-invalid-group-slug'; - const pluginConfig = config().plugins[1]; + const pluginConfig = lighthousePluginConfig(); const groups = pluginConfig.groups; groups[0].slug = invalidGroupSlug; pluginConfig.groups = groups; @@ -50,7 +58,7 @@ describe('pluginConfigSchema', () => { }); it('should throw if plugin groups have duplicate slugs', () => { - const pluginConfig = config().plugins[1]; + const pluginConfig = lighthousePluginConfig(); const groups = pluginConfig.groups; pluginConfig.groups = [...groups, groups[0]]; expect(() => pluginConfigSchema.parse(pluginConfig)).toThrow( @@ -60,7 +68,7 @@ describe('pluginConfigSchema', () => { it('should throw if plugin groups refs contain invalid slugs', () => { const invalidAuditRef = '-invalid-audit-ref'; - const pluginConfig = config().plugins[1]; + const pluginConfig = lighthousePluginConfig(); const groups = pluginConfig.groups; groups[0].refs[0].slug = invalidAuditRef; @@ -70,4 +78,18 @@ describe('pluginConfigSchema', () => { `slug has to follow the pattern`, ); }); + + it('take a transform function', () => { + const pluginCfg = pluginConfig([], { + runner: echoRunnerConfig([auditReport()], 'out.json', { + outputFileToAuditResults: outputFileToAuditOutputs(), + }), + }); + + const plgCfg = pluginConfigSchema.parse(pluginCfg); + expect(plgCfg.runner.outputFileToAuditResults).toBeDefined(); + expect(plgCfg.runner.outputFileToAuditResults([report()])).toEqual([ + { slug: 'mock-audit-slug', displayValue: 'transformed' }, + ]); + }); }); diff --git a/packages/models/src/lib/plugin-config.ts b/packages/models/src/lib/plugin-config.ts index 25f1cbd2a..d87c098d1 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, @@ -30,6 +33,7 @@ export const pluginMetaSchema = packageVersionSchema({ export const pluginDataSchema = z.object({ runner: runnerConfigSchema, + es5Runner: esmRunnerConfigSchema, audits: pluginAuditsSchema, groups: auditGroupsSchema, }); diff --git a/packages/models/test/fixtures/runner-config.mock.ts b/packages/models/test/fixtures/runner-config.mock.ts index d7965b283..a98cb7c42 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 { + AuditOutputs, + OutputFileToAuditOutputs, + RunnerConfig, +} from '../../src'; +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 @@ -17,6 +21,18 @@ export function runnerConfig(options?: Partial): RunnerConfig { }); } +/** + * 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 + ]; + }); +} + /** * Use this helper to mock the output data of a plugin synchronously * @@ -26,6 +42,7 @@ export function runnerConfig(options?: Partial): RunnerConfig { export function echoRunnerConfig( output: AuditOutput[], outputFile: string, + options?: Partial, ): RunnerConfig { const auditOutput = platform() === 'win32' @@ -35,5 +52,19 @@ export function echoRunnerConfig( command: 'echo', args: [auditOutput, '>', outputFile], outputFile, + ...options, + }; +} + +export function outputFileToAuditOutputs( + transform: (outputs: any) => AuditOutput = (a: AuditOutput) => ({ + ...a, + displayValue: 'transformed', + }), +): OutputFileToAuditOutputs { + return (outputs: unknown): AuditOutputs => { + return (outputs as AuditOutputs).map(output => { + return transform(output); + }); }; } 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, - }; -} diff --git a/packages/models/test/index.ts b/packages/models/test/index.ts index c3b9ab142..36c58aa19 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 } from './fixtures/runner-config.mock'; +export { + echoRunnerConfig, + outputFileToAuditOutputs, +} from './fixtures/runner-config.mock'; export { persistConfig } from './fixtures/persist-config.mock'; export { auditConfig, diff --git a/packages/utils/src/lib/execute-process.ts b/packages/utils/src/lib/execute-process.ts index 838e2ed52..1d029a4d8 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 { ExecutionMeta } from '@code-pushup/models'; import { calcDuration } from './report'; /** @@ -13,9 +14,7 @@ export type ProcessResult = { stdout: string; stderr: string; code: number | null; - date: string; - duration: number; -}; +} & ExecutionMeta; /** * Error class for process errors. diff --git a/project.json b/project.json deleted file mode 100644 index 085f54a1d..000000000 --- a/project.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "@code-pushup/cli-source", - "$schema": "node_modules/nx/schemas/project-schema.json", - "targets": { - "local-registry": { - "executor": "@nx/js:verdaccio", - "options": { - "port": 4873, - "config": ".verdaccio/config.yml", - "storage": "tmp/local-registry/storage" - } - } - } -}