Skip to content

Commit d9e43ee

Browse files
authored
test(test-setup): add custom matchers for ui logger (code-pushup#923)
1 parent 1686dda commit d9e43ee

32 files changed

+422
-125
lines changed

packages/cli/src/lib/compare/compare-command.unit.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
DEFAULT_PERSIST_FORMAT,
66
DEFAULT_PERSIST_OUTPUT_DIR,
77
} from '@code-pushup/models';
8-
import { getLogMessages } from '@code-pushup/test-utils';
98
import { ui } from '@code-pushup/utils';
109
import { DEFAULT_CLI_CONFIGURATION } from '../../../mocks/constants.js';
1110
import { yargsCli } from '../yargs-cli.js';
@@ -75,7 +74,8 @@ describe('compare-command', () => {
7574
{ ...DEFAULT_CLI_CONFIGURATION, commands: [yargsCompareCommandObject()] },
7675
).parseAsync();
7776

78-
expect(getLogMessages(ui().logger).at(-1)).toContain(
77+
expect(ui()).toHaveLogged(
78+
'info',
7979
`Reports diff written to ${bold(
8080
'.code-pushup/report-diff.json',
8181
)} and ${bold('.code-pushup/report-diff.md')}`,

packages/cli/src/lib/implementation/filter.middleware.unit.test.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,6 @@ describe('filterMiddleware', () => {
291291
);
292292

293293
it('should trigger verbose logging when skipPlugins or onlyPlugins removes categories', () => {
294-
const loggerSpy = vi.spyOn(ui().logger, 'info');
295-
296294
filterMiddleware({
297295
onlyPlugins: ['p1'],
298296
skipPlugins: ['p2'],
@@ -316,8 +314,15 @@ describe('filterMiddleware', () => {
316314
verbose: true,
317315
});
318316

319-
expect(loggerSpy).toHaveBeenCalledWith(
320-
expect.stringContaining('removed the following categories'),
317+
expect(ui()).toHaveNthLogged(
318+
1,
319+
'info',
320+
'The --skipPlugins argument removed the following categories: c1, c2.',
321+
);
322+
expect(ui()).toHaveNthLogged(
323+
2,
324+
'info',
325+
'The --onlyPlugins argument removed the following categories: c1, c2.',
321326
);
322327
});
323328

packages/cli/src/lib/implementation/global.utils.unit.test.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,6 @@ describe('logErrorBeforeThrow', () => {
5555
});
5656

5757
it('should log a custom error when OptionValidationError is thrown', async () => {
58-
const loggerSpy = vi.spyOn(ui().logger, 'error').mockImplementation(() => {
59-
/* empty */
60-
});
61-
6258
const errorFn = vi
6359
.fn()
6460
.mockRejectedValue(new OptionValidationError('Option validation failed'));
@@ -68,8 +64,7 @@ describe('logErrorBeforeThrow', () => {
6864
} catch {
6965
/* suppress */
7066
}
71-
72-
expect(loggerSpy).toHaveBeenCalledWith('Option validation failed');
67+
expect(ui()).toHaveLogged('error', 'Option validation failed');
7368
});
7469

7570
it('should rethrow errors other than OptionValidationError', async () => {

packages/cli/src/lib/implementation/validate-filter-options.utils.unit.test.ts

+25-21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { describe, expect } from 'vitest';
22
import type { CategoryConfig, PluginConfig } from '@code-pushup/models';
3-
import { getLogMessages } from '@code-pushup/test-utils';
43
import { ui } from '@code-pushup/utils';
54
import type { FilterOptionType, Filterables } from './filter.model.js';
65
import {
@@ -19,22 +18,22 @@ describe('validateFilterOption', () => {
1918
[
2019
'onlyPlugins',
2120
['p1', 'p3', 'p4'],
22-
'The --onlyPlugins argument references plugins that do not exist: p3, p4.',
21+
'The --onlyPlugins argument references plugins that do not exist: p3, p4. The only valid plugin is p1.',
2322
],
2423
[
2524
'onlyPlugins',
2625
['p1', 'p3'],
27-
'The --onlyPlugins argument references a plugin that does not exist: p3.',
26+
'The --onlyPlugins argument references a plugin that does not exist: p3. The only valid plugin is p1.',
2827
],
2928
[
3029
'onlyCategories',
3130
['c1', 'c3', 'c4'],
32-
'The --onlyCategories argument references categories that do not exist: c3, c4.',
31+
'The --onlyCategories argument references categories that do not exist: c3, c4. The only valid category is c1.',
3332
],
3433
[
3534
'onlyCategories',
3635
['c1', 'c3'],
37-
'The --onlyCategories argument references a category that does not exist: c3.',
36+
'The --onlyCategories argument references a category that does not exist: c3. The only valid category is c1.',
3837
],
3938
])(
4039
'should log a warning if the only argument %s references nonexistent slugs %o along with valid ones',
@@ -51,31 +50,30 @@ describe('validateFilterOption', () => {
5150
},
5251
{ itemsToFilter, skippedItems: [], verbose: false },
5352
);
54-
const logs = getLogMessages(ui().logger);
55-
expect(logs[0]).toContain(expected);
53+
expect(ui()).toHaveLogged('warn', expected);
5654
},
5755
);
5856

5957
it.each([
6058
[
6159
'skipPlugins',
6260
['p3', 'p4'],
63-
'The --skipPlugins argument references plugins that do not exist: p3, p4.',
61+
'The --skipPlugins argument references plugins that do not exist: p3, p4. The only valid plugin is p1.',
6462
],
6563
[
6664
'skipPlugins',
6765
['p3'],
68-
'The --skipPlugins argument references a plugin that does not exist: p3.',
66+
'The --skipPlugins argument references a plugin that does not exist: p3. The only valid plugin is p1.',
6967
],
7068
[
7169
'skipCategories',
7270
['c3', 'c4'],
73-
'The --skipCategories argument references categories that do not exist: c3, c4.',
71+
'The --skipCategories argument references categories that do not exist: c3, c4. The only valid category is c1.',
7472
],
7573
[
7674
'skipCategories',
7775
['c3'],
78-
'The --skipCategories argument references a category that does not exist: c3.',
76+
'The --skipCategories argument references a category that does not exist: c3. The only valid category is c1.',
7977
],
8078
])(
8179
'should log a warning if the skip argument %s references nonexistent slugs %o',
@@ -95,8 +93,7 @@ describe('validateFilterOption', () => {
9593
},
9694
{ itemsToFilter, skippedItems: [], verbose: false },
9795
);
98-
const logs = getLogMessages(ui().logger);
99-
expect(logs[0]).toContain(expected);
96+
expect(ui()).toHaveLogged('warn', expected);
10097
},
10198
);
10299

@@ -111,7 +108,7 @@ describe('validateFilterOption', () => {
111108
},
112109
{ itemsToFilter: ['p1'], skippedItems: [], verbose: false },
113110
);
114-
expect(getLogMessages(ui().logger)).toHaveLength(0);
111+
expect(ui()).not.toHaveLogs();
115112
});
116113

117114
it('should log a category ignored as a result of plugin filtering', () => {
@@ -130,9 +127,10 @@ describe('validateFilterOption', () => {
130127
},
131128
{ itemsToFilter: ['p1'], skippedItems: [], verbose: true },
132129
);
133-
expect(getLogMessages(ui().logger)).toHaveLength(1);
134-
expect(getLogMessages(ui().logger)[0]).toContain(
135-
'The --onlyPlugins argument removed the following categories: c1, c3',
130+
expect(ui()).toHaveLoggedTimes(1);
131+
expect(ui()).toHaveLogged(
132+
'info',
133+
'The --onlyPlugins argument removed the following categories: c1, c3.',
136134
);
137135
});
138136

@@ -221,10 +219,16 @@ describe('validateFilterOption', () => {
221219
{ plugins, categories },
222220
{ itemsToFilter: ['p1'], skippedItems: ['p1'], verbose: true },
223221
);
224-
const logs = getLogMessages(ui().logger);
225-
expect(logs[0]).toContain(
222+
expect(ui()).toHaveNthLogged(
223+
1,
224+
'warn',
226225
'The --skipPlugins argument references a skipped plugin: p1.',
227226
);
227+
expect(ui()).toHaveNthLogged(
228+
2,
229+
'info',
230+
'The --skipPlugins argument removed the following categories: c1.',
231+
);
228232
});
229233
});
230234

@@ -446,7 +450,6 @@ describe('validateSkippedCategories', () => {
446450
] as NonNullable<Filterables['categories']>;
447451

448452
it('should log info when categories are removed', () => {
449-
const loggerSpy = vi.spyOn(ui().logger, 'info');
450453
validateSkippedCategories(
451454
categories,
452455
[
@@ -457,7 +460,8 @@ describe('validateSkippedCategories', () => {
457460
] as NonNullable<Filterables['categories']>,
458461
true,
459462
);
460-
expect(loggerSpy).toHaveBeenCalledWith(
463+
expect(ui()).toHaveLogged(
464+
'info',
461465
'Category c1 was removed because all its refs were skipped. Affected refs: g1 (group)',
462466
);
463467
});

packages/cli/src/lib/merge-diffs/merge-diffs-command.unit.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
DEFAULT_PERSIST_FORMAT,
66
DEFAULT_PERSIST_OUTPUT_DIR,
77
} from '@code-pushup/models';
8-
import { getLogMessages } from '@code-pushup/test-utils';
98
import { ui } from '@code-pushup/utils';
109
import { DEFAULT_CLI_CONFIGURATION } from '../../../mocks/constants.js';
1110
import { yargsCli } from '../yargs-cli.js';
@@ -65,7 +64,8 @@ describe('merge-diffs-command', () => {
6564
},
6665
).parseAsync();
6766

68-
expect(getLogMessages(ui().logger).at(-1)).toContain(
67+
expect(ui()).toHaveLogged(
68+
'info',
6969
`Reports diff written to ${bold('.code-pushup/report-diff.md')}`,
7070
);
7171
});

packages/cli/src/lib/print-config/print-config-command.unit.test.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { describe, expect, vi } from 'vitest';
2-
import { getLogMessages } from '@code-pushup/test-utils';
32
import { ui } from '@code-pushup/utils';
43
import { DEFAULT_CLI_CONFIGURATION } from '../../../mocks/constants.js';
54
import { yargsCli } from '../yargs-cli.js';
@@ -27,11 +26,16 @@ describe('print-config-command', () => {
2726
{ ...DEFAULT_CLI_CONFIGURATION, commands: [yargsConfigCommandObject()] },
2827
).parseAsync();
2928

30-
const log = getLogMessages(ui().logger)[0];
31-
expect(log).not.toContain('"$0":');
32-
expect(log).not.toContain('"_":');
29+
expect(ui()).not.toHaveLogged('log', expect.stringContaining('"$0":'));
30+
expect(ui()).not.toHaveLogged('log', expect.stringContaining('"_":'));
3331

34-
expect(log).toContain('"outputDir": "destinationDir"');
35-
expect(log).not.toContain('"output-dir":');
32+
expect(ui()).toHaveLogged(
33+
'log',
34+
expect.stringContaining('"outputDir": "destinationDir"'),
35+
);
36+
expect(ui()).not.toHaveLogged(
37+
'log',
38+
expect.stringContaining('"output-dir":'),
39+
);
3640
});
3741
});

packages/cli/tsconfig.test.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"src/**/*.test.tsx",
1313
"src/**/*.test.js",
1414
"src/**/*.test.jsx",
15-
"src/**/*.d.ts"
15+
"src/**/*.d.ts",
16+
"../../testing/test-setup/src/vitest.d.ts"
1617
]
1718
}

packages/cli/vite.config.unit.ts

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export default defineConfig({
2828
'../../testing/test-setup/src/lib/console.mock.ts',
2929
'../../testing/test-setup/src/lib/portal-client.mock.ts',
3030
'../../testing/test-setup/src/lib/reset.mocks.ts',
31+
'../../testing/test-setup/src/lib/extend/ui-logger.matcher.ts',
3132
],
3233
},
3334
});

packages/core/src/lib/collect-and-persist.unit.test.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
ISO_STRING_REGEXP,
44
MINIMAL_CONFIG_MOCK,
55
MINIMAL_REPORT_MOCK,
6-
getLogMessages,
76
} from '@code-pushup/test-utils';
87
import {
98
type ScoredReport,
@@ -115,7 +114,6 @@ describe('collectAndPersistReports', () => {
115114
await collectAndPersistReports(
116115
MINIMAL_CONFIG_MOCK as CollectAndPersistReportsOptions,
117116
);
118-
const logs = getLogMessages(ui().logger);
119-
expect(logs.at(-2)).toContain('Made with ❤ by code-pushup.dev');
117+
expect(ui()).toHaveLogged('log', 'Made with ❤ by code-pushup.dev');
120118
});
121119
});

packages/core/src/lib/implementation/persist.unit.test.ts

+38-15
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
MEMFS_VOLUME,
88
MINIMAL_REPORT_MOCK,
99
REPORT_MOCK,
10-
getLogMessages,
1110
} from '@code-pushup/test-utils';
1211
import { scoreReport, sortReport, ui } from '@code-pushup/utils';
1312
import { logPersistedResults, persistReport } from './persist.js';
@@ -91,30 +90,54 @@ describe('persistReport', () => {
9190
describe('logPersistedResults', () => {
9291
it('should log report sizes correctly`', () => {
9392
logPersistedResults([{ status: 'fulfilled', value: ['out.json', 10_000] }]);
94-
const logs = getLogMessages(ui().logger);
95-
expect(logs[0]).toBe('[ green(success) ] Generated reports successfully: ');
96-
expect(logs[1]).toContain('9.77 kB');
97-
expect(logs[1]).toContain('out.json');
93+
expect(ui()).toHaveNthLogged(
94+
1,
95+
'success',
96+
expect.stringContaining('Generated reports successfully: '),
97+
);
98+
expect(ui()).toHaveNthLogged(
99+
2,
100+
'success',
101+
expect.stringContaining('9.77 kB'),
102+
);
103+
expect(ui()).toHaveNthLogged(
104+
2,
105+
'success',
106+
expect.stringContaining('out.json'),
107+
);
98108
});
99109

100110
it('should log fails correctly`', () => {
101111
logPersistedResults([{ status: 'rejected', reason: 'fail' }]);
102-
const logs = getLogMessages(ui().logger);
103-
expect(logs[0]).toBe('[ yellow(warn) ] Generated reports failed: ');
104-
expect(logs[1]).toContain('fail');
112+
expect(ui()).toHaveNthLogged(1, 'warn', 'Generated reports failed: ');
113+
expect(ui()).toHaveNthLogged(2, 'warn', expect.stringContaining('fail'));
105114
});
106115

107116
it('should log report sizes and fails correctly`', () => {
108117
logPersistedResults([
109118
{ status: 'fulfilled', value: ['out.json', 10_000] },
110119
{ status: 'rejected', reason: 'fail' },
111120
]);
112-
const logs = getLogMessages(ui().logger);
113-
expect(logs[0]).toBe('[ green(success) ] Generated reports successfully: ');
114-
expect(logs[1]).toContain('out.json');
115-
expect(logs[1]).toContain('9.77 kB');
116-
117-
expect(logs[2]).toContain('Generated reports failed: ');
118-
expect(logs[2]).toContain('fail');
121+
expect(ui()).toHaveNthLogged(
122+
1,
123+
'success',
124+
'Generated reports successfully: ',
125+
);
126+
expect(ui()).toHaveNthLogged(
127+
2,
128+
'success',
129+
expect.stringContaining('out.json'),
130+
);
131+
expect(ui()).toHaveNthLogged(
132+
2,
133+
'success',
134+
expect.stringContaining('9.77 kB'),
135+
);
136+
expect(ui()).toHaveNthLogged(
137+
3,
138+
'warn',
139+
expect.stringContaining('Generated reports failed: '),
140+
);
141+
expect(ui()).toHaveNthLogged(3, 'warn', expect.stringContaining('fail'));
119142
});
120143
});

packages/core/src/lib/merge-diffs.unit.test.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import path from 'node:path';
44
import type { PersistConfig } from '@code-pushup/models';
55
import {
66
MEMFS_VOLUME,
7-
getLogMessages,
87
reportsDiffAddedPluginMock,
98
reportsDiffAltMock,
109
reportsDiffMock,
@@ -64,13 +63,19 @@ describe('mergeDiffs', () => {
6463
),
6564
).resolves.toBe(path.join(MEMFS_VOLUME, 'report-diff.md'));
6665

67-
expect(getLogMessages(ui().logger)).toEqual([
66+
expect(ui()).toHaveNthLogged(
67+
1,
68+
'warn',
6869
expect.stringContaining(
6970
'Skipped invalid report diff - Failed to read JSON file missing-report-diff.json',
7071
),
72+
);
73+
expect(ui()).toHaveNthLogged(
74+
2,
75+
'warn',
7176
expect.stringContaining(
7277
'Skipped invalid report diff - Invalid reports diff in invalid-report-diff.json',
7378
),
74-
]);
79+
);
7580
});
7681
});

0 commit comments

Comments
 (0)