Skip to content

Commit

Permalink
feat: support custom options (#160)
Browse files Browse the repository at this point in the history
  • Loading branch information
AriPerkkio committed Feb 17, 2024
1 parent 9532cbe commit 7159d2e
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 37 deletions.
14 changes: 8 additions & 6 deletions README.md
Expand Up @@ -35,13 +35,14 @@ import { defineConfig } from 'vitest/config';

export default defineConfig({
test: {
reporters: 'vitest-sonar-reporter',
outputFile: 'sonar-report.xml',
reporters: [
['vitest-sonar-reporter', { outputFile: 'sonar-report.xml' }],
],
},
});
```

If you have multiple outputFile's defined, add one for `vitest-sonar-reporter`:
If you are using Vitest below version `^1.3.0` you can define file in `test.outputFile`:

```ts
test: {
Expand All @@ -64,16 +65,17 @@ sonar.testExecutionReportPaths=sonar-report.xml

### Options

You can pass additional options using `test.sonarReporterOptions` in `vite.config.ts`. Note that passing custom options to Vitest reporters is unconventional and may require you to use `@ts-ignore` when using TypeScript.
You can pass additional options to reporter. Note that this requires `vitest@^1.3.0`.

#### `silent`

Silence reporter's verbose logging.

```ts
test: {
reporters: 'vitest-sonar-reporter',
sonarReporterOptions: { silent: true }
reporters: [
['vitest-sonar-reporter', { silent: true }]
],
}
```

Expand Down
53 changes: 38 additions & 15 deletions src/sonar-reporter.ts
Expand Up @@ -4,35 +4,55 @@ import type { Reporter, File, Vitest } from 'vitest';

import { generateXml } from './xml.js';

export interface SonarReporterOptions {
outputFile: string;
silent?: boolean;
}

/**
* Reporter used by `vitest`
*/
export default class SonarReporter implements Reporter {
ctx!: Vitest;
outputFile!: string;
silent!: boolean;
options: SonarReporterOptions;

constructor(options?: Partial<SonarReporterOptions>) {
this.options = {
silent: options?.silent ?? false,

// @ts-expect-error -- Can also be initialized during onInit()
outputFile: options?.outputFile,
};
}

onInit(ctx: Vitest) {
this.ctx = ctx;

// @ts-expect-error -- untyped
this.silent = ctx.config.sonarReporterOptions?.silent === true;
this.options.silent =
this.options.silent ||
// TODO: Remove in v2.0.0
// @ts-expect-error -- untyped
ctx.config.sonarReporterOptions?.silent === true;

if (!this.ctx.config.outputFile) {
if (!this.ctx.config.outputFile && !this.options.outputFile) {
throw new Error(
'SonarReporter requires config.outputFile to be defined in vite config',
'SonarReporter requires outputFile to be defined in config',
);
}

this.outputFile = resolveOutputfile(this.ctx.config);
this.options.outputFile =
this.options.outputFile ?? resolveOutputfile(this.ctx.config);

if (existsSync(this.outputFile)) {
rmSync(this.outputFile);
if (existsSync(this.options.outputFile)) {
rmSync(this.options.outputFile);
}
}

onFinished(rawFiles?: File[]) {
const reportFile = resolve(this.ctx.config.root, this.outputFile);
const reportFile = resolve(
this.ctx.config.root,
this.options.outputFile,
);

// Map filepaths to be relative to root for workspace support
const files = rawFiles?.map((file) => ({
Expand All @@ -49,7 +69,7 @@ export default class SonarReporter implements Reporter {

writeFileSync(reportFile, generateXml(sorted), 'utf-8');

if (!this.silent) {
if (!this.options.silent) {
this.ctx.logger.log(`SonarQube report written to ${reportFile}`);
}
}
Expand All @@ -67,13 +87,16 @@ function resolveOutputfile(config: Vitest['config']) {
throw new Error(
[
'Unable to resolve outputFile for vitest-sonar-reporter.',
'Define outputFile as string or add entry for it:',
'Define outputFile in reporter options:',
JSON.stringify(
{
test: {
outputFile: {
'vitest-sonar-reporter': 'sonar-report.xml',
},
reporters: [
[
'vitest-sonar-reporter',
{ outputFile: 'sonar-report.xml' },
],
],
},
},
null,
Expand Down
23 changes: 18 additions & 5 deletions test/index.test.ts
Expand Up @@ -4,6 +4,7 @@ import { startVitest } from 'vitest/node';
import { stabilizeReport } from './utils';

const outputFile = 'report-from-tests.xml';
const reporterPath = new URL('../src/index.ts', import.meta.url).href;

beforeEach(() => {
if (existsSync(outputFile)) {
Expand Down Expand Up @@ -81,21 +82,33 @@ test('report location is logged', async () => {
);
});

test('logging can be silenced', async () => {
test('logging can be silenced, legacy config', async () => {
const spy = vi.spyOn(console, 'log');
await runVitest({ sonarReporterOptions: { silent: true } });
await runVitest({ config: { sonarReporterOptions: { silent: true } } });

expect(existsSync(outputFile)).toBe(true);
expect(spy).not.toHaveBeenCalled();
spy.mockRestore();
});

async function runVitest(opts = {}) {
test('logging can be silenced via options', async () => {
const spy = vi.spyOn(console, 'log');
await runVitest({ reporterOptions: { silent: true } });

expect(existsSync(outputFile)).toBe(true);
expect(spy).not.toHaveBeenCalled();
spy.mockRestore();
});

async function runVitest(options?: {
reporterOptions?: Record<string, unknown>;
config?: Record<string, unknown>;
}) {
await startVitest('test', [], {
watch: false,
reporters: new URL('../src/index.ts', import.meta.url).href,
reporters: [[reporterPath, options?.reporterOptions || {}]],
outputFile,
include: ['test/fixtures/*.test.ts'],
...opts,
...options?.config,
});
}
29 changes: 18 additions & 11 deletions test/sonar-reporter.test.ts
Expand Up @@ -7,7 +7,9 @@ test('resolves outputFile from string', () => {

reporter.onInit(getConfig({ outputFile: 'test-report.xml' }));

expect(reporter.outputFile).toMatchInlineSnapshot('"test-report.xml"');
expect(reporter.options.outputFile).toMatchInlineSnapshot(
'"test-report.xml"',
);
});

test('resolves outputFile from object', () => {
Expand All @@ -21,7 +23,7 @@ test('resolves outputFile from object', () => {
}),
);

expect(reporter.outputFile).toMatchInlineSnapshot(
expect(reporter.options.outputFile).toMatchInlineSnapshot(
'"test-report-from-object.xml"',
);
});
Expand All @@ -32,7 +34,7 @@ test('throws when outputFile is missing', () => {
expect(() =>
reporter.onInit(getConfig({ outputFile: undefined })),
).toThrowErrorMatchingInlineSnapshot(
`[Error: SonarReporter requires config.outputFile to be defined in vite config]`,
`[Error: SonarReporter requires outputFile to be defined in config]`,
);
});

Expand All @@ -45,14 +47,19 @@ test('throws when outputFile object is missing entry', () => {
),
).toThrowErrorMatchingInlineSnapshot(`
[Error: Unable to resolve outputFile for vitest-sonar-reporter.
Define outputFile as string or add entry for it:
{
"test": {
"outputFile": {
"vitest-sonar-reporter": "sonar-report.xml"
}
}
}]
Define outputFile in reporter options:
{
"test": {
"reporters": [
[
"vitest-sonar-reporter",
{
"outputFile": "sonar-report.xml"
}
]
]
}
}]
`);
});

Expand Down

0 comments on commit 7159d2e

Please sign in to comment.