Skip to content

Commit

Permalink
feat: implement skip-plugins option
Browse files Browse the repository at this point in the history
  • Loading branch information
nacho-vazquez authored and matejchalk committed Jul 11, 2024
1 parent 7e55fd3 commit 7369585
Show file tree
Hide file tree
Showing 6 changed files with 285 additions and 0 deletions.
46 changes: 46 additions & 0 deletions packages/cli/src/lib/implementation/skip-plugins.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { filterItemRefsBy } from '@code-pushup/utils';
import { SkipPluginsOptions } from './skip-plugins.model';
import { validateSkipPluginsOption } from './skip-plugins.utils';

export function skipPluginsMiddleware<T extends SkipPluginsOptions>(
originalProcessArgs: T,
): T {
const { categories = [], skipPlugins: originalSkipPlugins } =
originalProcessArgs;

if (originalSkipPlugins && originalSkipPlugins.length > 0) {
const { verbose, plugins, skipPlugins = [] } = originalProcessArgs;

validateSkipPluginsOption(
{ plugins, categories },
{ skipPlugins, verbose },
);

const validSkipPlugins = skipPlugins.filter(sP =>
plugins.find(p => p.slug === sP),
);

const skipPluginsSet = new Set(validSkipPlugins);

return {
...originalProcessArgs,
plugins:
skipPluginsSet.size > 0
? plugins.filter(({ slug }) => !skipPluginsSet.has(slug))
: plugins,
categories:
skipPluginsSet.size > 0
? filterItemRefsBy(
categories,
({ plugin }) => !skipPluginsSet.has(plugin),
)
: categories,
};
}

return {
...originalProcessArgs,
// if undefined fill categories with empty array
categories,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { describe, expect, vi } from 'vitest';
import { CategoryConfig, PluginConfig } from '@code-pushup/models';
import { skipPluginsMiddleware } from './skip-plugins.middleware';

vi.mock('@code-pushup/core', async () => {
const { CORE_CONFIG_MOCK }: typeof import('@code-pushup/test-utils') =
await vi.importActual('@code-pushup/test-utils');
const core: object = await vi.importActual('@code-pushup/core');
return {
...core,
readRcByPath: vi.fn().mockResolvedValue(CORE_CONFIG_MOCK),
autoloadRc: vi.fn().mockResolvedValue(CORE_CONFIG_MOCK),
};
});

describe('skipPluginsMiddleware', () => {
it('should fill undefined categories with empty array', () => {
expect(
skipPluginsMiddleware({
plugins: [{ slug: 'p1' } as PluginConfig],
}),
).toStrictEqual({
plugins: [{ slug: 'p1' }],
categories: [],
});
});

it('should forward equal values if not set', () => {
expect(
skipPluginsMiddleware({
plugins: [{ slug: 'p1' } as PluginConfig],
categories: [
{ slug: 'c1', refs: [{ plugin: 'p1' }] } as CategoryConfig,
],
}),
).toStrictEqual({
plugins: [{ slug: 'p1' }],
categories: [{ slug: 'c1', refs: [{ plugin: 'p1' }] }],
});
});

it('should filter plugins plugins for slug "p1"', () => {
const { plugins } = skipPluginsMiddleware({
skipPlugins: ['p1'],
plugins: [{ slug: 'p1' }, { slug: 'p2' }] as PluginConfig[],
categories: [],
});
expect(plugins).toStrictEqual([expect.objectContaining({ slug: 'p2' })]);
});

it('should forward plugins and categories for a slug not present in plugins', () => {
const originalCategories = [
{
slug: 'c1',
refs: [
{ plugin: 'p1', slug: 'a1-p1' },
{ plugin: 'p2', slug: 'a2-p1' },
],
},
{ slug: 'c2', refs: [{ plugin: 'p2', slug: 'a1-p2' }] },
] as CategoryConfig[];
const originalPlugins = [{ slug: 'p1' }, { slug: 'p2' }] as PluginConfig[];
const { categories, plugins } = skipPluginsMiddleware({
skipPlugins: ['wrong-slug'],
plugins: originalPlugins,
categories: originalCategories,
});
expect(categories).toBe(originalCategories);
expect(plugins).toBe(originalPlugins);
});

it('should filter categories for slug "p1"', () => {
const { categories } = skipPluginsMiddleware({
skipPlugins: ['p1'],
plugins: [{ slug: 'p1' }, { slug: 'p2' }] as PluginConfig[],
categories: [
{
slug: 'c1',
refs: [
{ plugin: 'p1', slug: 'a1-p1' },
{ plugin: 'p2', slug: 'a2-p1' },
],
},
{ slug: 'c2', refs: [{ plugin: 'p2', slug: 'a1-p2' }] },
] as CategoryConfig[],
});
expect(categories).toStrictEqual([
expect.objectContaining({
slug: 'c1',
refs: [{ plugin: 'p2', slug: 'a2-p1' }],
}),
expect.objectContaining({
slug: 'c2',
refs: [{ plugin: 'p2', slug: 'a1-p2' }],
}),
]);
});
});
9 changes: 9 additions & 0 deletions packages/cli/src/lib/implementation/skip-plugins.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { GlobalOptions } from '@code-pushup/core';
import { CoreConfig } from '@code-pushup/models';

export type SkipPluginsCliOptions = {
skipPlugins?: string[];
};
export type SkipPluginsOptions = Partial<GlobalOptions> &
Pick<CoreConfig, 'categories' | 'plugins'> &
SkipPluginsCliOptions;
18 changes: 18 additions & 0 deletions packages/cli/src/lib/implementation/skip-plugins.options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Options } from 'yargs';
import { coerceArray } from './global.utils';

export const skipPluginsOption: Options = {
describe: 'List of plugins to skip. If not set all plugins are run.',
type: 'array',
default: [],
coerce: coerceArray,
};

export function yargsSkipPluginsOptionsDefinition(): Record<
'skipPlugins',
Options
> {
return {
skipPlugins: skipPluginsOption,
};
}
46 changes: 46 additions & 0 deletions packages/cli/src/lib/implementation/skip-plugins.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import chalk from 'chalk';
import type { CategoryConfig, PluginConfig } from '@code-pushup/models';
import { filterItemRefsBy, ui } from '@code-pushup/utils';

export function validateSkipPluginsOption(
{
plugins,
categories,
}: {
plugins: PluginConfig[];
categories: CategoryConfig[];
},
{
skipPlugins = [],
verbose = false,
}: { skipPlugins?: string[]; verbose?: boolean } = {},
): void {
const skipPluginsSet = new Set(skipPlugins);
const missingPlugins = skipPlugins.filter(
plugin => !plugins.some(({ slug }) => slug === plugin),
);

if (missingPlugins.length > 0 && verbose) {
ui().logger.info(
`${chalk.yellow(
'⚠',
)} The --skipPlugin argument references plugins with "${missingPlugins.join(
'", "',
)}" slugs, but no such plugins are present in the configuration. Expected one of the following plugin slugs: "${plugins
.map(({ slug }) => slug)
.join('", "')}".`,
);
}

if (categories.length > 0) {
const removedCategorieSlugs = filterItemRefsBy(categories, ({ plugin }) =>
skipPluginsSet.has(plugin),
).map(({ slug }) => slug);
ui().logger.info(
`The --skipPlugin argument removed categories with "${removedCategorieSlugs.join(
'", "',
)}" slugs.
`,
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { describe, expect } from 'vitest';
import { CategoryConfig, PluginConfig } from '@code-pushup/models';
import { getLogMessages } from '@code-pushup/test-utils';
import { ui } from '@code-pushup/utils';
import { validateSkipPluginsOption } from './skip-plugins.utils';

describe('validateSkipPluginsOption', () => {
it('should warn if skipPlugins option contains non-existing plugin', () => {
validateSkipPluginsOption(
{
plugins: [
{ slug: 'plugin1', audits: [{ slug: 'a1' }] },
] as PluginConfig[],
categories: [],
},
{
skipPlugins: ['plugin1', 'plugin3', 'plugin4'],
verbose: true,
},
);
const logs = getLogMessages(ui().logger);
expect(logs[0]).toContain(
'The --skipPlugin argument references plugins with "plugin3", "plugin4" slugs',
);
});

it('should not log if skipPlugins option contains only existing plugins', () => {
validateSkipPluginsOption(
{
plugins: [
{ slug: 'plugin1', audits: [{ slug: 'a1-p1' }] },
{ slug: 'plugin2', audits: [{ slug: 'a1-p2' }] },
] as PluginConfig[],
categories: [],
},
{
skipPlugins: ['plugin1'],
verbose: true,
},
);
expect(getLogMessages(ui().logger)).toHaveLength(0);
});

it('should print ignored category and its first violating plugin', () => {
validateSkipPluginsOption(
{
plugins: [
{ slug: 'plugin1', audits: [{ slug: 'a1-p1' }] },
{ slug: 'plugin2', audits: [{ slug: 'a1-p2' }] },
] as PluginConfig[],
categories: [
{ slug: 'c1', refs: [{ plugin: 'plugin2' }] } as CategoryConfig,
{ slug: 'c2', refs: [{ plugin: 'plugin1' }] } as CategoryConfig,
{ slug: 'c3', refs: [{ plugin: 'plugin2' }] } as CategoryConfig,
],
},
{
skipPlugins: ['plugin2'],
verbose: true,
},
);
console.log(getLogMessages(ui().logger));
expect(getLogMessages(ui().logger)).toHaveLength(1);
expect(getLogMessages(ui().logger)[0]).toContain(
'The --skipPlugin argument removed categories with "c1", "c3" slugs',
);
});
});

0 comments on commit 7369585

Please sign in to comment.