157 lines
5.2 KiB
TypeScript
157 lines
5.2 KiB
TypeScript
import { readdirSync, readFileSync } from 'fs';
|
|
import { join } from 'path';
|
|
|
|
import { defineConfig } from 'vitest/config';
|
|
|
|
import { svgPath } from './scripts/vite/plugins/svg-path.js';
|
|
|
|
const EXCLUSIONS = [
|
|
'.eslintrc.cjs',
|
|
'docs/**',
|
|
'tests/**',
|
|
|
|
// Web-components.
|
|
'src/card.ts',
|
|
'src/components/**/*.ts',
|
|
'src/editor.ts',
|
|
|
|
// Timeline controller (can be added later).
|
|
'src/components-lib/timeline/controller.ts',
|
|
|
|
// HA patches.
|
|
'src/patches/**/*.ts',
|
|
];
|
|
|
|
const TEST_DIRECTORY = 'tests';
|
|
|
|
// Tests that mount the card in a real browser. They run from
|
|
// `vitest.browser.config.ts` and cannot run in any of the projects below, so
|
|
// they are kept out of the sweep.
|
|
const BROWSER_TEST_SUFFIX = '.browser.test.ts';
|
|
|
|
const findTestFiles = (directory: string): string[] => {
|
|
const files: string[] = [];
|
|
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
const path = join(directory, entry.name);
|
|
if (entry.isDirectory()) {
|
|
files.push(...findTestFiles(path));
|
|
} else if (
|
|
entry.name.endsWith('.test.ts') &&
|
|
!entry.name.endsWith(BROWSER_TEST_SUFFIX)
|
|
) {
|
|
files.push(path);
|
|
}
|
|
}
|
|
return files;
|
|
};
|
|
|
|
// Each test file is read and sorted into one of the projects below, which
|
|
// explain what these two properties cost.
|
|
const REQUIRE_ISOLATION_REGEXP = /\bvi\.(do)?mock\(/;
|
|
const REQUIRE_DOM_REGEXP = /@vitest-environment\s+jsdom/;
|
|
|
|
const getGroup = (file: string): string => {
|
|
const contents = readFileSync(file, 'utf-8');
|
|
if (REQUIRE_ISOLATION_REGEXP.test(contents)) {
|
|
return 'isolated';
|
|
}
|
|
return REQUIRE_DOM_REGEXP.test(contents) ? 'shared-jsdom' : 'shared-node';
|
|
};
|
|
|
|
const getInclusions = (group: string): string[] =>
|
|
findTestFiles(TEST_DIRECTORY).filter((file) => getGroup(file) === group);
|
|
|
|
export default defineConfig({
|
|
plugins: [svgPath()],
|
|
test: {
|
|
server: {
|
|
deps: {
|
|
// These dependencies import without extensions.
|
|
// Related: https://github.com/vitest-dev/vitest/issues/2313
|
|
inline: ['ha-nunjucks', 'ts-py-datetime'],
|
|
},
|
|
},
|
|
|
|
// Forked child processes start and tear down faster here than worker
|
|
// threads, which matters when every test file needs a fresh one.
|
|
pool: 'forks',
|
|
|
|
// Importing the source tree costs far more than running the tests in it, so
|
|
// files are grouped by whether they can share one loaded copy of it. Each
|
|
// project below is a group that can, or the one that cannot.
|
|
projects: [
|
|
{
|
|
extends: true,
|
|
test: {
|
|
// Nothing stops these sharing, so they run against a single loaded
|
|
// copy of the source tree. Most of the suite is here, and anything
|
|
// moved out of here pays to import that tree again.
|
|
name: 'shared',
|
|
include: getInclusions('shared-node'),
|
|
isolate: false,
|
|
},
|
|
},
|
|
{
|
|
extends: true,
|
|
test: {
|
|
// These share too, but only with each other. A module loaded under
|
|
// jsdom holds a reference to that DOM, so a worker that moves from a
|
|
// DOM file to a non-DOM one drops its loaded copy of the source tree
|
|
// and imports it again. A project's files are spread across workers
|
|
// without regard to environment, so putting both kinds in one project
|
|
// leaves every worker alternating between them and reloading as it
|
|
// goes, and the more workers there are the more often that happens. A
|
|
// project per environment hands each worker a run of files that all
|
|
// want the same one. The file-level `@vitest-environment jsdom`
|
|
// comments are what sort files into here, and are redundant once they
|
|
// arrive.
|
|
//
|
|
// They share one `document` as well, so a file that redefines part of
|
|
// it must leave the property configurable for the files that follow.
|
|
name: 'shared (jsdom)',
|
|
include: getInclusions('shared-jsdom'),
|
|
environment: 'jsdom',
|
|
isolate: false,
|
|
},
|
|
},
|
|
{
|
|
extends: true,
|
|
test: {
|
|
// `vi.mock()` cannot replace a module that an earlier file already
|
|
// loaded unmocked, so these cannot share a loaded source tree with
|
|
// anything, including each other. They get a registry per file and
|
|
// pay the full import cost.
|
|
name: 'isolated',
|
|
include: getInclusions('isolated'),
|
|
isolate: true,
|
|
},
|
|
},
|
|
],
|
|
|
|
// Hide console writing to keep output clean, usual sources of noise:
|
|
// - Unnecessary Lit dev-mode warnings.
|
|
// - Various console outputs (that are expected/tested).
|
|
onConsoleLog: () => false,
|
|
|
|
coverage: {
|
|
exclude: EXCLUSIONS,
|
|
|
|
// 'v8' reads coverage from the JavaScript engine, so it runs about twice
|
|
// as fast as 'istanbul', which has to rewrite every source file first.
|
|
// Known gap: 'istanbul' will flag a default parameter value that no test
|
|
// ever falls back on -- 'v8' will not, but it is not worth the
|
|
// significant performance slowdown to cover this rare case.
|
|
provider: 'v8',
|
|
thresholds: {
|
|
perFile: true,
|
|
'src/**/*.ts': {
|
|
statements: 100,
|
|
branches: 100,
|
|
functions: 100,
|
|
lines: 100,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|