-
Couldn't load subscription status.
- Fork 16
refactor: common vitest configurations #1107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
View your CI Pipeline Execution ↗ for commit 38b7a6e
☁️ Nx Cloud last updated this comment at |
@code-pushup/ci
@code-pushup/cli
@code-pushup/create-cli
@code-pushup/core
@code-pushup/models
@code-pushup/nx-plugin
@code-pushup/coverage-plugin
@code-pushup/eslint-plugin
@code-pushup/js-packages-plugin
@code-pushup/jsdocs-plugin
@code-pushup/lighthouse-plugin
@code-pushup/typescript-plugin
@code-pushup/utils
@code-pushup/models-transformers
commit: |
Code PushUp🤨 Code PushUp report has both improvements and regressions – compared current commit cc49e4f with previous commit 471c847. 🕵️ See full comparison in Code PushUp portal 🔍 🏷️ Categories👍 1 group improved, 👎 2 groups regressed, 👍 2 audits improved, 👎 5 audits regressed, 14 audits changed without impacting score🗃️ Groups
18 other groups are unchanged. 🛡️ Audits
589 other audits are unchanged. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for cleaning up our test config mess :)
I left some comments.
- please add a proper PR description that contains the scope and planned changes
- move the config code under a Nx project in
testing/vitest-setup. I updated the issue accordingly.
testing/test-setup-config/src/lib/vitest-setup-presets.unit.test.ts
Outdated
Show resolved
Hide resolved
testing/test-setup-config/src/lib/vitest-setup-presets.unit.test.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the changes! Nice testing tool! There is Readme, and tests, lovely.
I left some first comments and will wait for one feedback on the override logic for further comments.
9433ca5 to
7008a48
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, thanks for your contribution 🙂 I think it looks promising, but ...
I don't think this PR goes far enough towards unification. Ideally, only 2 parameters1 would determine the entire Vitest config:
- Nx project name (e.g.,
cli,plugin-eslint,ci-e2e) 2 - test kind (
'unit' | 'int' | 'e2e')
My main criticism of this PR is that it doesn't commit to unification over flexibility. I believe we need less flexibility than it may appear, so I would go all in on unification. If we find some exception, we should question whether that exception is justified and get rid of it if possible.
In the current implementation, some things are automatically inferred, but there are many (IMHO unnecessary) optional arguments, and anything can be overridden by providing a Vitest config object. To me, this is a leaky abstraction.
I've suggested specifics on what can be unified in my comments. Let me know if I can clarify anything 🙂 I realize you couldn't reasonably have known many of these things, so no worries.
Footnotes
-
By "parameter", I don't strictly mean a function parameter. Your approach of exporting different factory functions for each test kind also works, for example. ↩
-
It may turn out we also need something like
projectRoot(e.g.,'packages/core'). In that case, we could either derive it via@nx/devkit'screateProjectGraphAsync, provide the project folder instead and derive its name usingpath.basename, or add another parameter. ↩
|
|
||
| ## Shared config | ||
|
|
||
| [README](./src/lib/config/README.md) how to use vitest config factory. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The link is broken, and this doesn't really work as an English sentence.
| [README](./src/lib/config/README.md) how to use vitest config factory. | |
| See [`@code-pushup/test-setup-config` docs](../test-setup-config/README.md) on how to use our Vitest config factory. |
|
|
||
| export function tsconfigPathAliases(): AliasOptions { | ||
| const result = loadConfig('tsconfig.base.json'); | ||
| export function tsconfigPathAliases(projectRootUrl?: URL): AliasOptions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is now duplicated in testing/test-setup-config/src/lib/vitest-tsconfig-path-aliases.ts. I'm guessing we don't need it here in tools anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We actually need both for some time. The one from tool is used in other not migrated config files.
Once we migrate all configs then we can remove it.
| /.sass-cache | ||
| /connect.lock | ||
| /coverage | ||
| **/.coverage/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which tool uses .coverage folders? 🤨
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one could be used to exclude colocated coverage folders of each project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one could be used to exclude colocated coverage folders of each project.
I've no clue what you mean by that 😅
There's only 1 coverage folder with all projects at the workspace root, and it's called coverage, not .coverage. So what does ignoring **/.coverage/** achieve?
7008a48 to
a11d027
Compare
a11d027 to
38b7a6e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is headed in the right direction. I also reviewed the internal implementation this time, where I have some minor suggestions.
However, I think the pattern is now fairly clear, so please go ahead and apply it to other Vitest configs. 🙏
| } | ||
|
|
||
| function getGlobalSetup(kind: TestKind): string[] | undefined { | ||
| return kind === 'e2e' ? undefined : ['../../global-setup.ts']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The global-setup.ts should be included for all tests, including E2E.
| testTimeout?: number; | ||
| }; | ||
|
|
||
| export type VitestConfig = ViteUserConfig & { test?: InlineConfig }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to use the original UserConfig type from vitest/config, instead of defining a custom type.
| * | ||
| * // Override any config using spread operator | ||
| * const baseConfig = createE2ETestConfig('my-e2e', { testTimeout: 60_000 }); | ||
| * export default { | ||
| * ...baseConfig, | ||
| * test: { | ||
| * ...(baseConfig as any).test, | ||
| * globalSetup: ['./custom-setup.ts'], | ||
| * }, | ||
| * }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd remove this example, because I wouldn't like to encourage this type of usage.
| ### Advanced: Overriding Config | ||
|
|
||
| For edge cases, use the spread operator to override any property: | ||
|
|
||
| ```typescript | ||
| const baseConfig = createE2ETestConfig('my-e2e'); | ||
| export default { | ||
| ...baseConfig, | ||
| test: { | ||
| ...(baseConfig as any).test, | ||
| globalSetup: ['./custom-setup.ts'], | ||
| }, | ||
| }; | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same reasoning as previous comment:
| ### Advanced: Overriding Config | |
| For edge cases, use the spread operator to override any property: | |
| ```typescript | |
| const baseConfig = createE2ETestConfig('my-e2e'); | |
| export default { | |
| ...baseConfig, | |
| test: { | |
| ...(baseConfig as any).test, | |
| globalSetup: ['./custom-setup.ts'], | |
| }, | |
| }; | |
| ``` |
| **Unit tests:** | ||
|
|
||
| ```typescript | ||
| import { createUnitTestConfig } from '@code-pushup/test-setup-config'; | ||
|
|
||
| export default createUnitTestConfig('my-package'); | ||
| ``` | ||
|
|
||
| **Integration tests:** | ||
|
|
||
| ```typescript | ||
| import { createIntTestConfig } from '@code-pushup/test-setup-config'; | ||
|
|
||
| export default createIntTestConfig('my-package'); | ||
| ``` | ||
|
|
||
| **E2E tests:** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's more semantic to use headings in Markdown - e.g.: #### Unit tests instead of **Unit tests:**.
| const defaultExclude = ['mocks/**', '**/types.ts', 'perf/**']; | ||
| const reportsDirectory = `../../coverage/${projectKey}/${kind}-tests`; | ||
|
|
||
| return { | ||
| reporter: ['text', 'lcov'], | ||
| reportsDirectory, | ||
| exclude: defaultExclude, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name defaultExclude implies there'll be some additional overrides, which isn't the case.
| const defaultExclude = ['mocks/**', '**/types.ts', 'perf/**']; | |
| const reportsDirectory = `../../coverage/${projectKey}/${kind}-tests`; | |
| return { | |
| reporter: ['text', 'lcov'], | |
| reportsDirectory, | |
| exclude: defaultExclude, | |
| }; | |
| const exclude = ['mocks/**', '**/types.ts', 'perf/**']; | |
| const reportsDirectory = `../../coverage/${projectKey}/${kind}-tests`; | |
| return { | |
| reporter: ['text', 'lcov'], | |
| reportsDirectory, | |
| exclude, | |
| }; |
| beforeEach(() => { | ||
| vi.clearAllMocks(); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Including reset.mocks.ts already takes care of this:
| vi.clearAllMocks(); |
| expect(config).toEqual( | ||
| expect.objectContaining({ | ||
| cacheDir: '../../node_modules/.vite/test-package', | ||
| test: expect.objectContaining({ | ||
| reporters: ['basic'], | ||
| globals: true, | ||
| cache: { dir: '../../node_modules/.vitest' }, | ||
| alias: expect.any(Array), | ||
| pool: 'threads', | ||
| poolOptions: { threads: { singleThread: true } }, | ||
| environment: 'node', | ||
| include: [ | ||
| 'src/**/*.unit.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', | ||
| 'src/**/*.type.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', | ||
| ], | ||
| globalSetup: ['../../global-setup.ts'], | ||
| setupFiles: expect.arrayContaining([ | ||
| '../../testing/test-setup/src/lib/console.mock.ts', | ||
| '../../testing/test-setup/src/lib/reset.mocks.ts', | ||
| '../../testing/test-setup/src/lib/fs.mock.ts', | ||
| ]), | ||
| coverage: expect.objectContaining({ | ||
| reporter: ['text', 'lcov'], | ||
| reportsDirectory: '../../coverage/test-package/unit-tests', | ||
| exclude: ['mocks/**', '**/types.ts', 'perf/**'], | ||
| }), | ||
| typecheck: { include: ['**/*.type.test.ts'] }, | ||
| }), | ||
| }), | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No properties other than cacheDir and test should be created, so expect.objectContaining shouldn't be needed.
| expect((config as any).test.include).toContain( | ||
| 'src/**/*.type.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', | ||
| ); | ||
| }); | ||
|
|
||
| it('should enable typecheck for unit tests', () => { | ||
| const config = createVitestConfig('test-package', 'unit'); | ||
|
|
||
| expect((config as any).test.typecheck).toEqual({ | ||
| include: ['**/*.type.test.ts'], | ||
| }); | ||
| }); | ||
|
|
||
| it('should always include perf/** in coverage exclusions', () => { | ||
| const config = createVitestConfig('test-package', 'unit'); | ||
|
|
||
| expect((config as any).test.coverage.exclude).toContain('perf/**'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /** | ||
| * Setup files for unit tests. | ||
| * | ||
| * These paths are relative to the config file location (typically `packages/<project>/vitest.unit.config.ts`), | ||
| * which is why they use `../../` to navigate to the workspace root first. | ||
| */ | ||
| const UNIT_TEST_SETUP_FILES = [ | ||
| '../../testing/test-setup/src/lib/console.mock.ts', | ||
| '../../testing/test-setup/src/lib/reset.mocks.ts', | ||
| '../../testing/test-setup/src/lib/cliui.mock.ts', | ||
| '../../testing/test-setup/src/lib/fs.mock.ts', | ||
| '../../testing/test-setup/src/lib/git.mock.ts', | ||
| '../../testing/test-setup/src/lib/portal-client.mock.ts', | ||
| '../../testing/test-setup/src/lib/extend/ui-logger.matcher.ts', | ||
| '../../testing/test-setup/src/lib/extend/markdown-table.matcher.ts', | ||
| '../../testing/test-setup/src/lib/extend/jest-extended.matcher.ts', | ||
| '../../testing/test-setup/src/lib/extend/path.matcher.ts', | ||
| ] as const; | ||
|
|
||
| /** | ||
| * Setup files for integration tests. | ||
| * | ||
| * These paths are relative to the config file location (typically `packages/<project>/vitest.int.config.ts`), | ||
| * which is why they use `../../` to navigate to the workspace root first. | ||
| */ | ||
| const INT_TEST_SETUP_FILES = [ | ||
| '../../testing/test-setup/src/lib/console.mock.ts', | ||
| '../../testing/test-setup/src/lib/reset.mocks.ts', | ||
| '../../testing/test-setup/src/lib/extend/ui-logger.matcher.ts', | ||
| '../../testing/test-setup/src/lib/extend/markdown-table.matcher.ts', | ||
| '../../testing/test-setup/src/lib/extend/jest-extended.matcher.ts', | ||
| '../../testing/test-setup/src/lib/extend/path.matcher.ts', | ||
| ] as const; | ||
|
|
||
| /** | ||
| * Setup files for E2E tests. | ||
| * | ||
| * These paths are relative to the config file location (typically `e2e/<project-e2e>/vitest.e2e.config.ts`), | ||
| * which is why they use `../../` to navigate to the workspace root first. | ||
| */ | ||
| const E2E_TEST_SETUP_FILES = [ | ||
| '../../testing/test-setup/src/lib/reset.mocks.ts', | ||
| '../../testing/test-setup/src/lib/extend/ui-logger.matcher.ts', | ||
| '../../testing/test-setup/src/lib/extend/markdown-table.matcher.ts', | ||
| '../../testing/test-setup/src/lib/extend/jest-extended.matcher.ts', | ||
| '../../testing/test-setup/src/lib/extend/path.matcher.ts', | ||
| ] as const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For easier maintenance, I would extract all matchers to a shared variable that's spread into each array of setup files.
const CUSTOM_MATCHERS = [
'../../testing/test-setup/src/lib/extend/ui-logger.matcher.ts',
'../../testing/test-setup/src/lib/extend/markdown-table.matcher.ts',
'../../testing/test-setup/src/lib/extend/jest-extended.matcher.ts',
'../../testing/test-setup/src/lib/extend/path.matcher.ts',
] as const;
const UNIT_TEST_SETUP_FILES = [
// ... mocks ...
...CUSTOM_MATCHERS,
] as const;
const INT_TEST_SETUP_FILES = [
// ... mocks ...
...CUSTOM_MATCHERS,
] as const;
const E2E_TEST_SETUP_FILES = [
// ... mocks ...
...CUSTOM_MATCHERS,
] as const;
Centralize and Standardize Vitest Configurations
What Changed
Benefits
The new system provides
createUnitConfig,createIntConfig, andcreateE2eConfigfactory functions that automatically handle common configuration patterns while still allowing customization through override parameters.Relates to first part of #1065