@untestutils/vitest 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/browsers.d.mts +6 -0
- package/dist/browsers.d.ts +7 -0
- package/dist/browsers.d.ts.map +1 -0
- package/dist/browsers.mjs +22 -0
- package/dist/coverage.d.mts +55 -0
- package/dist/coverage.d.ts +56 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/coverage.mjs +53 -0
- package/dist/fixtures.d.ts +56 -0
- package/dist/fixtures.d.ts.map +1 -0
- package/dist/fixtures.mjs +141 -0
- package/dist/fixtures.test-d.d.mts +1 -0
- package/dist/fixtures.test-d.mjs +14 -0
- package/dist/global-setup.d.mts +2 -0
- package/dist/global-setup.d.ts +3 -0
- package/dist/global-setup.d.ts.map +1 -0
- package/dist/global-setup.mjs +41 -0
- package/dist/index.d.mts +12 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +5 -0
- package/dist/plugin.d.mts +53 -0
- package/dist/plugin.d.ts +65 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.mjs +88 -0
- package/dist/setup-file.d.mts +1 -0
- package/dist/setup-file.d.ts +3 -0
- package/dist/setup-file.d.ts.map +1 -0
- package/dist/setup-file.mjs +15 -0
- package/package.json +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 s00d
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Supported Playwright browser engines for harness fixtures. */
|
|
2
|
+
export type HarnessBrowserName = "chromium" | "firefox" | "webkit";
|
|
3
|
+
export declare const HARNESS_BROWSER_NAMES: HarnessBrowserName[];
|
|
4
|
+
export declare function normalizeHarnessBrowsers(browsers: HarnessBrowserName[] | undefined): HarnessBrowserName[];
|
|
5
|
+
/** Resolve active browser for this worker (env wins). */
|
|
6
|
+
export declare function resolveHarnessBrowserName(fallback?: HarnessBrowserName): HarnessBrowserName;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Supported Playwright browser engines for harness fixtures. */
|
|
2
|
+
export type HarnessBrowserName = 'chromium' | 'firefox' | 'webkit';
|
|
3
|
+
export declare const HARNESS_BROWSER_NAMES: HarnessBrowserName[];
|
|
4
|
+
export declare function normalizeHarnessBrowsers(browsers: HarnessBrowserName[] | undefined): HarnessBrowserName[];
|
|
5
|
+
/** Resolve active browser for this worker (env wins). */
|
|
6
|
+
export declare function resolveHarnessBrowserName(fallback?: HarnessBrowserName): HarnessBrowserName;
|
|
7
|
+
//# sourceMappingURL=browsers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browsers.d.ts","sourceRoot":"","sources":["../src/browsers.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEnE,eAAO,MAAM,qBAAqB,EAAE,kBAAkB,EAAsC,CAAC;AAE7F,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,SAAS,GACzC,kBAAkB,EAAE,CAYtB;AAED,yDAAyD;AACzD,wBAAgB,yBAAyB,CACvC,QAAQ,GAAE,kBAA+B,GACxC,kBAAkB,CAIpB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const HARNESS_BROWSER_NAMES = [
|
|
2
|
+
"chromium",
|
|
3
|
+
"firefox",
|
|
4
|
+
"webkit"
|
|
5
|
+
];
|
|
6
|
+
export function normalizeHarnessBrowsers(browsers) {
|
|
7
|
+
if (!browsers?.length) return ["chromium"];
|
|
8
|
+
const out = [];
|
|
9
|
+
for (const b of browsers) {
|
|
10
|
+
if (!HARNESS_BROWSER_NAMES.includes(b)) {
|
|
11
|
+
throw new Error(`[untestutils] unknown browser "${b}". Expected one of: ${HARNESS_BROWSER_NAMES.join(", ")}`);
|
|
12
|
+
}
|
|
13
|
+
if (!out.includes(b)) out.push(b);
|
|
14
|
+
}
|
|
15
|
+
return out;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function resolveHarnessBrowserName(fallback = "chromium") {
|
|
19
|
+
const fromEnv = process.env.UNTESTUTILS_BROWSER;
|
|
20
|
+
if (fromEnv && HARNESS_BROWSER_NAMES.includes(fromEnv)) return fromEnv;
|
|
21
|
+
return fallback;
|
|
22
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vitest coverage helpers — shared defaults for apps and this monorepo.
|
|
3
|
+
* Import from `untestutils/vitest/plugin` (config-safe; no test.extend).
|
|
4
|
+
*/
|
|
5
|
+
export interface CoverageThresholds {
|
|
6
|
+
lines: number;
|
|
7
|
+
functions: number;
|
|
8
|
+
statements: number;
|
|
9
|
+
branches: number;
|
|
10
|
+
}
|
|
11
|
+
export interface CreateCoverageConfigOptions {
|
|
12
|
+
/** Explicit include globs (wins over workspacePackages / default app globs). */
|
|
13
|
+
include?: string[];
|
|
14
|
+
/** Extra exclude globs merged with defaults. */
|
|
15
|
+
exclude?: string[];
|
|
16
|
+
/**
|
|
17
|
+
* Monorepo package folder names under packages/<name>/src.
|
|
18
|
+
* Expands to packages/<name>/src with ts/tsx/mjs/js globs.
|
|
19
|
+
*/
|
|
20
|
+
workspacePackages?: string[];
|
|
21
|
+
thresholds?: Partial<CoverageThresholds>;
|
|
22
|
+
reportsDirectory?: string;
|
|
23
|
+
reporter?: string[];
|
|
24
|
+
all?: boolean;
|
|
25
|
+
provider?: "v8" | "istanbul";
|
|
26
|
+
/** Also include environment package sources when present. */
|
|
27
|
+
includeEnvironment?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface UntestutilsCoverageConfig {
|
|
30
|
+
provider: "v8" | "istanbul";
|
|
31
|
+
reportsDirectory: string;
|
|
32
|
+
reporter: string[];
|
|
33
|
+
all: boolean;
|
|
34
|
+
include: string[];
|
|
35
|
+
exclude: string[];
|
|
36
|
+
thresholds: CoverageThresholds;
|
|
37
|
+
}
|
|
38
|
+
export declare const DEFAULT_COVERAGE_THRESHOLDS: CoverageThresholds;
|
|
39
|
+
/** Default workspace packages covered in the untestutils monorepo. */
|
|
40
|
+
export declare const UNTESTUTILS_WORKSPACE_PACKAGES: readonly ["core", "nuxt", "vitest", "playwright", "ai", "vite", "next", "astro", "sveltekit", "untestutils"];
|
|
41
|
+
/**
|
|
42
|
+
* Build a Vitest `test.coverage` object.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* // consumer app
|
|
47
|
+
* export default defineConfig({
|
|
48
|
+
* test: { coverage: createCoverageConfig() },
|
|
49
|
+
* })
|
|
50
|
+
*
|
|
51
|
+
* // this monorepo
|
|
52
|
+
* createCoverageConfig({ workspacePackages: [...UNTESTUTILS_WORKSPACE_PACKAGES] })
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function createCoverageConfig(opts?: CreateCoverageConfigOptions): UntestutilsCoverageConfig;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vitest coverage helpers — shared defaults for apps and this monorepo.
|
|
3
|
+
* Import from `untestutils/vitest/plugin` (config-safe; no test.extend).
|
|
4
|
+
*/
|
|
5
|
+
export interface CoverageThresholds {
|
|
6
|
+
lines: number;
|
|
7
|
+
functions: number;
|
|
8
|
+
statements: number;
|
|
9
|
+
branches: number;
|
|
10
|
+
}
|
|
11
|
+
export interface CreateCoverageConfigOptions {
|
|
12
|
+
/** Explicit include globs (wins over workspacePackages / default app globs). */
|
|
13
|
+
include?: string[];
|
|
14
|
+
/** Extra exclude globs merged with defaults. */
|
|
15
|
+
exclude?: string[];
|
|
16
|
+
/**
|
|
17
|
+
* Monorepo package folder names under packages/<name>/src.
|
|
18
|
+
* Expands to packages/<name>/src with ts/tsx/mjs/js globs.
|
|
19
|
+
*/
|
|
20
|
+
workspacePackages?: string[];
|
|
21
|
+
thresholds?: Partial<CoverageThresholds>;
|
|
22
|
+
reportsDirectory?: string;
|
|
23
|
+
reporter?: string[];
|
|
24
|
+
all?: boolean;
|
|
25
|
+
provider?: 'v8' | 'istanbul';
|
|
26
|
+
/** Also include environment package sources when present. */
|
|
27
|
+
includeEnvironment?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface UntestutilsCoverageConfig {
|
|
30
|
+
provider: 'v8' | 'istanbul';
|
|
31
|
+
reportsDirectory: string;
|
|
32
|
+
reporter: string[];
|
|
33
|
+
all: boolean;
|
|
34
|
+
include: string[];
|
|
35
|
+
exclude: string[];
|
|
36
|
+
thresholds: CoverageThresholds;
|
|
37
|
+
}
|
|
38
|
+
export declare const DEFAULT_COVERAGE_THRESHOLDS: CoverageThresholds;
|
|
39
|
+
/** Default workspace packages covered in the untestutils monorepo. */
|
|
40
|
+
export declare const UNTESTUTILS_WORKSPACE_PACKAGES: readonly ["core", "nuxt", "vitest", "playwright", "ai", "vite", "next", "astro", "sveltekit", "untestutils"];
|
|
41
|
+
/**
|
|
42
|
+
* Build a Vitest `test.coverage` object.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* // consumer app
|
|
47
|
+
* export default defineConfig({
|
|
48
|
+
* test: { coverage: createCoverageConfig() },
|
|
49
|
+
* })
|
|
50
|
+
*
|
|
51
|
+
* // this monorepo
|
|
52
|
+
* createCoverageConfig({ workspacePackages: [...UNTESTUTILS_WORKSPACE_PACKAGES] })
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function createCoverageConfig(opts?: CreateCoverageConfigOptions): UntestutilsCoverageConfig;
|
|
56
|
+
//# sourceMappingURL=coverage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../src/coverage.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,2BAA2B;IAC1C,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,QAAQ,CAAC,EAAE,IAAI,GAAG,UAAU,CAAC;IAC7B,6DAA6D;IAC7D,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,IAAI,GAAG,UAAU,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,kBAAkB,CAAC;CAChC;AAED,eAAO,MAAM,2BAA2B,EAAE,kBAKzC,CAAC;AAcF,sEAAsE;AACtE,eAAO,MAAM,8BAA8B,8GAWjC,CAAC;AAMX;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,GAAE,2BAAgC,GACrC,yBAAyB,CAwB3B"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export const DEFAULT_COVERAGE_THRESHOLDS = {
|
|
2
|
+
lines: 80,
|
|
3
|
+
functions: 80,
|
|
4
|
+
statements: 80,
|
|
5
|
+
branches: 70
|
|
6
|
+
};
|
|
7
|
+
const DEFAULT_EXCLUDE = [
|
|
8
|
+
"**/*.d.ts",
|
|
9
|
+
"**/*.test.ts",
|
|
10
|
+
"**/*.spec.ts",
|
|
11
|
+
"**/prompts/**",
|
|
12
|
+
"**/templates/**",
|
|
13
|
+
"**/node_modules/**",
|
|
14
|
+
"**/dist/**"
|
|
15
|
+
];
|
|
16
|
+
const DEFAULT_APP_INCLUDE = ["src/**/*.{ts,tsx,js,jsx,mjs,vue}"];
|
|
17
|
+
|
|
18
|
+
export const UNTESTUTILS_WORKSPACE_PACKAGES = [
|
|
19
|
+
"core",
|
|
20
|
+
"nuxt",
|
|
21
|
+
"vitest",
|
|
22
|
+
"playwright",
|
|
23
|
+
"ai",
|
|
24
|
+
"vite",
|
|
25
|
+
"next",
|
|
26
|
+
"astro",
|
|
27
|
+
"sveltekit",
|
|
28
|
+
"untestutils"
|
|
29
|
+
];
|
|
30
|
+
function workspaceIncludes(names) {
|
|
31
|
+
return names.map((name) => `packages/${name}/src/**/*.{ts,tsx,mjs,js}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createCoverageConfig(opts = {}) {
|
|
35
|
+
const include = opts.include ?? (opts.workspacePackages?.length ? [...workspaceIncludes(opts.workspacePackages), ...opts.includeEnvironment ? ["packages/vitest-environment-untestutils/**/*.{mjs,js,ts}"] : []] : [...DEFAULT_APP_INCLUDE]);
|
|
36
|
+
return {
|
|
37
|
+
provider: opts.provider ?? "v8",
|
|
38
|
+
reportsDirectory: opts.reportsDirectory ?? "./coverage",
|
|
39
|
+
reporter: opts.reporter ?? [
|
|
40
|
+
"text",
|
|
41
|
+
"text-summary",
|
|
42
|
+
"json",
|
|
43
|
+
"json-summary"
|
|
44
|
+
],
|
|
45
|
+
all: opts.all ?? true,
|
|
46
|
+
include,
|
|
47
|
+
exclude: [...DEFAULT_EXCLUDE, ...opts.exclude ?? []],
|
|
48
|
+
thresholds: {
|
|
49
|
+
...DEFAULT_COVERAGE_THRESHOLDS,
|
|
50
|
+
...opts.thresholds
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { APIRequestContext, Browser, BrowserContext, Page, Response } from 'playwright-core';
|
|
2
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, type TestAPI } from 'vitest';
|
|
3
|
+
import { type Recipe } from '@untestutils/core';
|
|
4
|
+
import { type HarnessBrowserName } from './browsers';
|
|
5
|
+
export { afterAll, afterEach, beforeAll, beforeEach, describe };
|
|
6
|
+
export type { HarnessBrowserName } from './browsers';
|
|
7
|
+
export { HARNESS_BROWSER_NAMES, normalizeHarnessBrowsers, resolveHarnessBrowserName, } from './browsers';
|
|
8
|
+
export interface HarnessFixtures {
|
|
9
|
+
/** Recipe id or Recipe — set via `test.override({ harness: 'site' })`. */
|
|
10
|
+
harness: string | Recipe | undefined;
|
|
11
|
+
/** Playwright engine — from `UNTESTUTILS_BROWSER` or plugin `browsers`. */
|
|
12
|
+
browserName: HarnessBrowserName;
|
|
13
|
+
page: Page;
|
|
14
|
+
goto: (path: string, options?: Record<string, unknown>) => Promise<Response | null>;
|
|
15
|
+
baseURL: string;
|
|
16
|
+
request: APIRequestContext;
|
|
17
|
+
}
|
|
18
|
+
/** @internal */
|
|
19
|
+
export declare const browserApi: {
|
|
20
|
+
launch(name?: HarnessBrowserName): Promise<Browser>;
|
|
21
|
+
};
|
|
22
|
+
/** @internal */
|
|
23
|
+
export declare function getBrowser(name?: HarnessBrowserName): Promise<Browser>;
|
|
24
|
+
/** @internal reset for tests */
|
|
25
|
+
export declare function resetBrowserPromise(): void;
|
|
26
|
+
/** @internal */
|
|
27
|
+
export declare function resolveBaseURL(): Promise<string>;
|
|
28
|
+
type CookieInput = Parameters<BrowserContext['addCookies']>[0][number];
|
|
29
|
+
/** Rewrite cookies so `domain: 'localhost'` applies on 127.0.0.1 harness URLs. */
|
|
30
|
+
export declare function normalizeHarnessCookies(cookies: CookieInput[], baseURL: string): CookieInput[];
|
|
31
|
+
/** @internal */
|
|
32
|
+
export declare function createGoto(page: Page, baseURL: string): (path: string, options?: Record<string, unknown>) => Promise<Response | null>;
|
|
33
|
+
/** @internal */
|
|
34
|
+
export declare function waitForNuxt(page: Page, mode: 'hydration' | 'route'): Promise<void>;
|
|
35
|
+
/** @internal — body of the page fixture (callable from unit tests) */
|
|
36
|
+
export declare function usePageFixture(baseURL: string, use: (page: Page) => Promise<void>): Promise<void>;
|
|
37
|
+
/** @internal */
|
|
38
|
+
export declare function useRequestFixture(baseURL: string, use: (ctx: APIRequestContext) => Promise<void>): Promise<void>;
|
|
39
|
+
/** @internal */
|
|
40
|
+
export declare function importRequestApi(): Promise<import("playwright-core").APIRequest>;
|
|
41
|
+
/** Prefer Playwright web-first expect when available. */
|
|
42
|
+
export declare function loadExpect(): Promise<import("@playwright/test").Expect<{}>>;
|
|
43
|
+
/** @internal */
|
|
44
|
+
export declare function importExpectModule(): Promise<typeof import("@playwright/test")>;
|
|
45
|
+
export { expect } from '@playwright/test';
|
|
46
|
+
/**
|
|
47
|
+
* Builder `extend` (Vitest 4.1+) so types keep real fixture keys.
|
|
48
|
+
* Object `extend<HarnessFixtures>(…)` hits the wrong overload and emits
|
|
49
|
+
* `Record<HarnessFixtures, …>` — wiping `page`/`goto`/`harness` from context.
|
|
50
|
+
* Vitest has no Playwright `{ option: true }`; use `test.override({ harness })`.
|
|
51
|
+
*
|
|
52
|
+
* Asserted as `TestAPI<HarnessFixtures>` so published dts stays flat (builder
|
|
53
|
+
* otherwise nests `Omit & Record<"page", …> & { $__test?: … }` noise).
|
|
54
|
+
*/
|
|
55
|
+
export declare const test: TestAPI<HarnessFixtures>;
|
|
56
|
+
//# sourceMappingURL=fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../src/fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAClG,OAAO,EACL,QAAQ,EACR,SAAS,EACT,SAAS,EACT,UAAU,EACV,QAAQ,EAER,KAAK,OAAO,EACb,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAmD,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACjG,OAAO,EAAE,KAAK,kBAAkB,EAA6B,MAAM,YAAY,CAAC;AAEhF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AAChE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACrC,2EAA2E;IAC3E,WAAW,EAAE,kBAAkB,CAAC;IAChC,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IACpF,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAID,gBAAgB;AAChB,eAAO,MAAM,UAAU;kBAEF,kBAAkB;CAQtC,CAAC;AAEF,gBAAgB;AAChB,wBAAgB,UAAU,CAAC,IAAI,CAAC,EAAE,kBAAkB,oBAQnD;AAED,gCAAgC;AAChC,wBAAgB,mBAAmB,SAElC;AAED,gBAAgB;AAChB,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAItD;AAED,KAAK,WAAW,GAAG,UAAU,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvE,kFAAkF;AAClF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,CAmB9F;AAOD,gBAAgB;AAChB,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,IACtC,MAAM,MAAM,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,8BAe9D;AAED,gBAAgB;AAChB,wBAAsB,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAcxF;AAED,sEAAsE;AACtE,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,iBAOvF;AAED,gBAAgB;AAChB,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,iBAM/C;AAED,gBAAgB;AAChB,wBAAsB,gBAAgB,kDAGrC;AAED,yDAAyD;AACzD,wBAAsB,UAAU,mDAG/B;AAED,gBAAgB;AAChB,wBAAsB,kBAAkB,+CAEvC;AAED,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C;;;;;;;;GAQG;AACH,eAAO,MAAM,IAAI,EAAE,OAAO,CAAC,eAAe,CAqBV,CAAC"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, test as base } from "vitest";
|
|
2
|
+
import { getCurrentHarness, normalizeBaseUrl, useHarness } from "@untestutils/core";
|
|
3
|
+
import { resolveHarnessBrowserName } from "./browsers.mjs";
|
|
4
|
+
export { afterAll, afterEach, beforeAll, beforeEach, describe };
|
|
5
|
+
export { HARNESS_BROWSER_NAMES, normalizeHarnessBrowsers, resolveHarnessBrowserName } from "./browsers.mjs";
|
|
6
|
+
const browserPromises = new Map();
|
|
7
|
+
|
|
8
|
+
export const browserApi = {
|
|
9
|
+
|
|
10
|
+
async launch(name = "chromium") {
|
|
11
|
+
const pw = await import("playwright-core");
|
|
12
|
+
const launcher = pw[name];
|
|
13
|
+
if (!launcher?.launch) {
|
|
14
|
+
throw new Error(`[untestutils] playwright-core has no launcher for "${name}"`);
|
|
15
|
+
}
|
|
16
|
+
return launcher.launch({ headless: true });
|
|
17
|
+
} };
|
|
18
|
+
|
|
19
|
+
export function getBrowser(name) {
|
|
20
|
+
const browserName = name ?? resolveHarnessBrowserName();
|
|
21
|
+
let p = browserPromises.get(browserName);
|
|
22
|
+
if (!p) {
|
|
23
|
+
p = browserApi.launch(browserName);
|
|
24
|
+
browserPromises.set(browserName, p);
|
|
25
|
+
}
|
|
26
|
+
return p;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function resetBrowserPromise() {
|
|
30
|
+
browserPromises.clear();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function resolveBaseURL() {
|
|
34
|
+
const h = getCurrentHarness();
|
|
35
|
+
if (!h?.url) throw new Error("[untestutils] no harness url — call await useHarness(...) first");
|
|
36
|
+
return normalizeBaseUrl(h.url);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function normalizeHarnessCookies(cookies, baseURL) {
|
|
40
|
+
return cookies.map((c) => {
|
|
41
|
+
if (!("domain" in c) || c.domain !== "localhost" && c.domain !== "127.0.0.1") {
|
|
42
|
+
return c;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const next = {
|
|
46
|
+
name: c.name,
|
|
47
|
+
value: c.value,
|
|
48
|
+
url: baseURL
|
|
49
|
+
};
|
|
50
|
+
if ("expires" in c && typeof c.expires === "number" && c.expires >= 0) {
|
|
51
|
+
next.expires = c.expires;
|
|
52
|
+
}
|
|
53
|
+
if ("httpOnly" in c && c.httpOnly !== undefined) next.httpOnly = c.httpOnly;
|
|
54
|
+
if ("secure" in c && c.secure !== undefined) next.secure = c.secure;
|
|
55
|
+
if ("sameSite" in c && c.sameSite !== undefined) next.sameSite = c.sameSite;
|
|
56
|
+
return next;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function patchContextCookies(context, baseURL) {
|
|
60
|
+
const orig = context.addCookies.bind(context);
|
|
61
|
+
context.addCookies = async (cookies) => orig(normalizeHarnessCookies([...cookies], baseURL));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function createGoto(page, baseURL) {
|
|
65
|
+
return async (path, options) => {
|
|
66
|
+
const url = path.startsWith("http") ? path : new URL(path, baseURL).toString();
|
|
67
|
+
const waitUntil = options?.waitUntil;
|
|
68
|
+
if (waitUntil === "hydration" || waitUntil === "route") {
|
|
69
|
+
const res = await page.goto(url, {
|
|
70
|
+
...options,
|
|
71
|
+
waitUntil: "load"
|
|
72
|
+
});
|
|
73
|
+
await waitForNuxt(page, waitUntil);
|
|
74
|
+
return res;
|
|
75
|
+
}
|
|
76
|
+
const res = await page.goto(url, options);
|
|
77
|
+
|
|
78
|
+
if (waitUntil === "networkidle" || waitUntil === "load" || waitUntil === undefined) {
|
|
79
|
+
await waitForNuxt(page, "hydration").catch(() => {});
|
|
80
|
+
}
|
|
81
|
+
return res;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function waitForNuxt(page, mode) {
|
|
86
|
+
await page.waitForFunction((m) => {
|
|
87
|
+
const w = globalThis;
|
|
88
|
+
const app = w.useNuxtApp?.();
|
|
89
|
+
if (!app) return false;
|
|
90
|
+
if (m === "hydration") return app.isHydrating === false;
|
|
91
|
+
return Boolean(app._route?.fullPath);
|
|
92
|
+
}, mode, { timeout: 3e4 });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function usePageFixture(baseURL, use) {
|
|
96
|
+
const browser = await getBrowser();
|
|
97
|
+
const context = await browser.newContext({ baseURL });
|
|
98
|
+
patchContextCookies(context, baseURL);
|
|
99
|
+
const page = await context.newPage();
|
|
100
|
+
await use(page);
|
|
101
|
+
await context.close().catch(() => {});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function useRequestFixture(baseURL, use) {
|
|
105
|
+
const requestApi = await importRequestApi();
|
|
106
|
+
const ctx = await requestApi.newContext({ baseURL });
|
|
107
|
+
await use(ctx);
|
|
108
|
+
await ctx.dispose().catch(() => {});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function importRequestApi() {
|
|
112
|
+
const { request } = await import("@playwright/test");
|
|
113
|
+
return request;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function loadExpect() {
|
|
117
|
+
const mod = await importExpectModule();
|
|
118
|
+
return mod.expect;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export async function importExpectModule() {
|
|
122
|
+
return import("@playwright/test");
|
|
123
|
+
}
|
|
124
|
+
export { expect } from "@playwright/test";
|
|
125
|
+
|
|
126
|
+
export const test = base.extend("harness", undefined).extend("browserName", () => resolveHarnessBrowserName()).extend("baseURL", async ({ harness }) => {
|
|
127
|
+
if (harness) await useHarness(harness);
|
|
128
|
+
return resolveBaseURL();
|
|
129
|
+
}).extend("page", async ({ baseURL, browserName }, { onCleanup }) => {
|
|
130
|
+
const browser = await getBrowser(browserName);
|
|
131
|
+
const context = await browser.newContext({ baseURL });
|
|
132
|
+
patchContextCookies(context, baseURL);
|
|
133
|
+
const page = await context.newPage();
|
|
134
|
+
onCleanup(() => context.close().catch(() => {}));
|
|
135
|
+
return page;
|
|
136
|
+
}).extend("goto", ({ page, baseURL }) => createGoto(page, baseURL)).extend("request", async ({ baseURL }, { onCleanup }) => {
|
|
137
|
+
const requestApi = await importRequestApi();
|
|
138
|
+
const ctx = await requestApi.newContext({ baseURL });
|
|
139
|
+
onCleanup(() => ctx.dispose().catch(() => {}));
|
|
140
|
+
return ctx;
|
|
141
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { expectTypeOf } from "vitest";
|
|
2
|
+
import { test } from "./fixtures.mjs";
|
|
3
|
+
expectTypeOf(test).toEqualTypeOf();
|
|
4
|
+
test.override({ harness: "site" });
|
|
5
|
+
// @ts-expect-error unknown fixture key
|
|
6
|
+
test.override({ notAFixture: true });
|
|
7
|
+
test("fixtures", async ({ page, goto, baseURL, request, harness, browserName }) => {
|
|
8
|
+
expectTypeOf(page).toEqualTypeOf();
|
|
9
|
+
expectTypeOf(goto).toEqualTypeOf();
|
|
10
|
+
expectTypeOf(baseURL).toEqualTypeOf();
|
|
11
|
+
expectTypeOf(request).toEqualTypeOf();
|
|
12
|
+
expectTypeOf(harness).toEqualTypeOf();
|
|
13
|
+
expectTypeOf(browserName).toEqualTypeOf();
|
|
14
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-setup.d.ts","sourceRoot":"","sources":["../src/global-setup.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAkB/C,wBAA8B,WAAW,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CA0C7F"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { pathToFileURL } from "node:url";
|
|
2
|
+
import { ensurePrepared, stopAllTargets, reclaimStaleTargets, getRegisteredRecipe, listRegisteredRecipes, resolveArtifactsRoot, TargetRegistry, progress } from "@untestutils/core";
|
|
3
|
+
async function loadRecipesModule() {
|
|
4
|
+
const mod = process.env.UNTESTUTILS_RECIPES_MODULE;
|
|
5
|
+
if (!mod) return;
|
|
6
|
+
await import(pathToFileURL(mod).href);
|
|
7
|
+
}
|
|
8
|
+
export default async function globalSetup(_project) {
|
|
9
|
+
await loadRecipesModule();
|
|
10
|
+
const artifactsRoot = resolveArtifactsRoot();
|
|
11
|
+
|
|
12
|
+
await reclaimStaleTargets(artifactsRoot);
|
|
13
|
+
const prewarm = JSON.parse(process.env.UNTESTUTILS_PREWARM || "[]");
|
|
14
|
+
|
|
15
|
+
let recipesMap;
|
|
16
|
+
if (process.env.UNTESTUTILS_RECIPES_MODULE) {
|
|
17
|
+
const loaded = await import(pathToFileURL(process.env.UNTESTUTILS_RECIPES_MODULE).href);
|
|
18
|
+
recipesMap = loaded.recipes;
|
|
19
|
+
}
|
|
20
|
+
if (prewarm.length) {
|
|
21
|
+
progress.waveStart(prewarm);
|
|
22
|
+
for (const id of prewarm) {
|
|
23
|
+
const recipe = recipesMap?.[id] ?? getRegisteredRecipe(id);
|
|
24
|
+
if (!recipe) {
|
|
25
|
+
throw new Error(`[untestutils] prewarm id "${id}" not found. Pass recipesModule to untestutils() and call defineRecipes() in that module.`);
|
|
26
|
+
}
|
|
27
|
+
await ensurePrepared(recipe, { artifactsRoot });
|
|
28
|
+
}
|
|
29
|
+
progress.waveReady();
|
|
30
|
+
progress.massRun();
|
|
31
|
+
} else {
|
|
32
|
+
const registry = new TargetRegistry(artifactsRoot);
|
|
33
|
+
const map = await registry.read();
|
|
34
|
+
registry.applyAllToEnv(map);
|
|
35
|
+
}
|
|
36
|
+
void listRegisteredRecipes();
|
|
37
|
+
return async () => {
|
|
38
|
+
progress.teardown();
|
|
39
|
+
await stopAllTargets(artifactsRoot);
|
|
40
|
+
};
|
|
41
|
+
}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public Vitest barrel for specs.
|
|
3
|
+
* Config files must import the plugin from `untestutils/vitest/plugin`
|
|
4
|
+
* so `test.extend` is not evaluated during config load.
|
|
5
|
+
*/
|
|
6
|
+
export { untestutils } from "./plugin.mjs";
|
|
7
|
+
export type { UntestutilsPluginOptions } from "./plugin.mjs";
|
|
8
|
+
export { useHarness, defineRecipes, defineRecipe } from "@untestutils/core";
|
|
9
|
+
export type { HarnessHandle, Recipe } from "@untestutils/core";
|
|
10
|
+
export { describe, expect, test, beforeAll, afterAll, beforeEach, afterEach } from "./fixtures.mjs";
|
|
11
|
+
export type { HarnessFixtures, HarnessBrowserName } from "./fixtures.mjs";
|
|
12
|
+
export { HARNESS_BROWSER_NAMES, normalizeHarnessBrowsers, resolveHarnessBrowserName } from "./browsers.mjs";
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public Vitest barrel for specs.
|
|
3
|
+
* Config files must import the plugin from `untestutils/vitest/plugin`
|
|
4
|
+
* so `test.extend` is not evaluated during config load.
|
|
5
|
+
*/
|
|
6
|
+
export { untestutils } from './plugin';
|
|
7
|
+
export type { UntestutilsPluginOptions } from './plugin';
|
|
8
|
+
export { useHarness, defineRecipes, defineRecipe } from '@untestutils/core';
|
|
9
|
+
export type { HarnessHandle, Recipe } from '@untestutils/core';
|
|
10
|
+
export { describe, expect, test, beforeAll, afterAll, beforeEach, afterEach } from './fixtures';
|
|
11
|
+
export type { HarnessFixtures, HarnessBrowserName } from './fixtures';
|
|
12
|
+
export { HARNESS_BROWSER_NAMES, normalizeHarnessBrowsers, resolveHarnessBrowserName, } from './browsers';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC5E,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAChG,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,YAAY,CAAC"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
|
|
2
|
+
export { untestutils } from "./plugin.mjs";
|
|
3
|
+
export { useHarness, defineRecipes, defineRecipe } from "@untestutils/core";
|
|
4
|
+
export { describe, expect, test, beforeAll, afterAll, beforeEach, afterEach } from "./fixtures.mjs";
|
|
5
|
+
export { HARNESS_BROWSER_NAMES, normalizeHarnessBrowsers, resolveHarnessBrowserName } from "./browsers.mjs";
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type RecipeRegistry } from "@untestutils/core";
|
|
2
|
+
import { type CreateCoverageConfigOptions, type UntestutilsCoverageConfig } from "./coverage.mjs";
|
|
3
|
+
import { type HarnessBrowserName } from "./browsers.mjs";
|
|
4
|
+
export type { CreateCoverageConfigOptions, CoverageThresholds, UntestutilsCoverageConfig } from "./coverage.mjs";
|
|
5
|
+
export { createCoverageConfig, DEFAULT_COVERAGE_THRESHOLDS, UNTESTUTILS_WORKSPACE_PACKAGES } from "./coverage.mjs";
|
|
6
|
+
export type { HarnessBrowserName } from "./browsers.mjs";
|
|
7
|
+
export { HARNESS_BROWSER_NAMES, normalizeHarnessBrowsers, resolveHarnessBrowserName } from "./browsers.mjs";
|
|
8
|
+
export interface UntestutilsPluginOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Recipe map from `defineRecipes(...)` — import and pass it.
|
|
11
|
+
* Workers re-load via the module path recorded by `defineRecipes` (pass `import.meta.url`
|
|
12
|
+
* as the second arg for reliability).
|
|
13
|
+
*/
|
|
14
|
+
recipes?: RecipeRegistry;
|
|
15
|
+
/**
|
|
16
|
+
* Absolute path override for the recipes module. Usually unnecessary when
|
|
17
|
+
* `defineRecipes` ran in that file (or you passed `import.meta.url`).
|
|
18
|
+
*/
|
|
19
|
+
recipesModule?: string;
|
|
20
|
+
prewarm?: string[];
|
|
21
|
+
/** Artifacts directory (sets `UNTESTUTILS_ARTIFACTS_DIR`). */
|
|
22
|
+
artifactsRoot?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Playwright engines for the `page` / `goto` fixtures.
|
|
25
|
+
* Default `['chromium']`. First entry sets `UNTESTUTILS_BROWSER` for workers;
|
|
26
|
+
* run multiple engines via separate Vitest projects / env overrides.
|
|
27
|
+
*/
|
|
28
|
+
browsers?: HarnessBrowserName[];
|
|
29
|
+
/**
|
|
30
|
+
* Merge Vitest coverage config (thresholds + include globs).
|
|
31
|
+
* `true` applies app defaults for the src tree; object passes through createCoverageConfig.
|
|
32
|
+
*/
|
|
33
|
+
coverage?: boolean | CreateCoverageConfigOptions;
|
|
34
|
+
}
|
|
35
|
+
interface VitestPlugin {
|
|
36
|
+
name: string;
|
|
37
|
+
configureVitest?: (ctx: {
|
|
38
|
+
project: {
|
|
39
|
+
config: Record<string, unknown> & {
|
|
40
|
+
sequence?: {
|
|
41
|
+
setupFiles?: string;
|
|
42
|
+
};
|
|
43
|
+
globalSetup?: string | string[];
|
|
44
|
+
setupFiles?: string | string[];
|
|
45
|
+
coverage?: UntestutilsCoverageConfig | Record<string, unknown>;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
}) => void;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Vitest/Vite plugin — import from `untestutils/vitest/plugin` in vitest.config.
|
|
52
|
+
*/
|
|
53
|
+
export declare function untestutils(options?: UntestutilsPluginOptions): VitestPlugin;
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { type RecipeRegistry } from '@untestutils/core';
|
|
5
|
+
import { type CreateCoverageConfigOptions, type UntestutilsCoverageConfig } from './coverage';
|
|
6
|
+
import { type HarnessBrowserName } from './browsers';
|
|
7
|
+
export type { CreateCoverageConfigOptions, CoverageThresholds, UntestutilsCoverageConfig, } from './coverage';
|
|
8
|
+
export { createCoverageConfig, DEFAULT_COVERAGE_THRESHOLDS, UNTESTUTILS_WORKSPACE_PACKAGES, } from './coverage';
|
|
9
|
+
export type { HarnessBrowserName } from './browsers';
|
|
10
|
+
export { HARNESS_BROWSER_NAMES, normalizeHarnessBrowsers, resolveHarnessBrowserName, } from './browsers';
|
|
11
|
+
export interface UntestutilsPluginOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Recipe map from `defineRecipes(...)` — import and pass it.
|
|
14
|
+
* Workers re-load via the module path recorded by `defineRecipes` (pass `import.meta.url`
|
|
15
|
+
* as the second arg for reliability).
|
|
16
|
+
*/
|
|
17
|
+
recipes?: RecipeRegistry;
|
|
18
|
+
/**
|
|
19
|
+
* Absolute path override for the recipes module. Usually unnecessary when
|
|
20
|
+
* `defineRecipes` ran in that file (or you passed `import.meta.url`).
|
|
21
|
+
*/
|
|
22
|
+
recipesModule?: string;
|
|
23
|
+
prewarm?: string[];
|
|
24
|
+
/** Artifacts directory (sets `UNTESTUTILS_ARTIFACTS_DIR`). */
|
|
25
|
+
artifactsRoot?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Playwright engines for the `page` / `goto` fixtures.
|
|
28
|
+
* Default `['chromium']`. First entry sets `UNTESTUTILS_BROWSER` for workers;
|
|
29
|
+
* run multiple engines via separate Vitest projects / env overrides.
|
|
30
|
+
*/
|
|
31
|
+
browsers?: HarnessBrowserName[];
|
|
32
|
+
/**
|
|
33
|
+
* Merge Vitest coverage config (thresholds + include globs).
|
|
34
|
+
* `true` applies app defaults for the src tree; object passes through createCoverageConfig.
|
|
35
|
+
*/
|
|
36
|
+
coverage?: boolean | CreateCoverageConfigOptions;
|
|
37
|
+
}
|
|
38
|
+
interface VitestPlugin {
|
|
39
|
+
name: string;
|
|
40
|
+
configureVitest?: (ctx: {
|
|
41
|
+
project: {
|
|
42
|
+
config: Record<string, unknown> & {
|
|
43
|
+
sequence?: {
|
|
44
|
+
setupFiles?: string;
|
|
45
|
+
};
|
|
46
|
+
globalSetup?: string | string[];
|
|
47
|
+
setupFiles?: string | string[];
|
|
48
|
+
coverage?: UntestutilsCoverageConfig | Record<string, unknown>;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
}) => void;
|
|
52
|
+
}
|
|
53
|
+
/** @internal injectable fs/require for companion resolution tests */
|
|
54
|
+
export declare const companionIo: {
|
|
55
|
+
createRequire: typeof createRequire;
|
|
56
|
+
existsSync: typeof existsSync;
|
|
57
|
+
fileURLToPath: typeof fileURLToPath;
|
|
58
|
+
};
|
|
59
|
+
/** @internal */
|
|
60
|
+
export declare function resolveCompanion(name: 'global-setup' | 'setup-file'): string;
|
|
61
|
+
/**
|
|
62
|
+
* Vitest/Vite plugin — import from `untestutils/vitest/plugin` in vitest.config.
|
|
63
|
+
*/
|
|
64
|
+
export declare function untestutils(options?: UntestutilsPluginOptions): VitestPlugin;
|
|
65
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAA8C,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACpG,OAAO,EAEL,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,EAC/B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,kBAAkB,EAA4B,MAAM,YAAY,CAAC;AAE/E,YAAY,EACV,2BAA2B,EAC3B,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,2BAA2B,EAC3B,8BAA8B,GAC/B,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAChC;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,2BAA2B,CAAC;CAClD;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE;QACtB,OAAO,EAAE;YACP,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;gBAChC,QAAQ,CAAC,EAAE;oBAAE,UAAU,CAAC,EAAE,MAAM,CAAA;iBAAE,CAAC;gBACnC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;gBAChC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;gBAC/B,QAAQ,CAAC,EAAE,yBAAyB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;aAChE,CAAC;SACH,CAAC;KACH,KAAK,IAAI,CAAC;CACZ;AAWD,qEAAqE;AACrE,eAAO,MAAM,WAAW;;;;CAIvB,CAAC;AAEF,gBAAgB;AAChB,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,YAAY,GAAG,MAAM,CAsB5E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,wBAA6B,GAAG,YAAY,CA0DhF"}
|
package/dist/plugin.mjs
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { dirname, join } from "pathe";
|
|
5
|
+
import { ensureRecipes, debug, getRecipesModulePath } from "@untestutils/core";
|
|
6
|
+
import { createCoverageConfig } from "./coverage.mjs";
|
|
7
|
+
import { normalizeHarnessBrowsers } from "./browsers.mjs";
|
|
8
|
+
export { createCoverageConfig, DEFAULT_COVERAGE_THRESHOLDS, UNTESTUTILS_WORKSPACE_PACKAGES } from "./coverage.mjs";
|
|
9
|
+
export { HARNESS_BROWSER_NAMES, normalizeHarnessBrowsers, resolveHarnessBrowserName } from "./browsers.mjs";
|
|
10
|
+
|
|
11
|
+
function companion(name) {
|
|
12
|
+
return resolveCompanion(name);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const companionIo = {
|
|
16
|
+
createRequire,
|
|
17
|
+
existsSync,
|
|
18
|
+
fileURLToPath
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function resolveCompanion(name) {
|
|
22
|
+
const req = companionIo.createRequire(join(process.cwd(), "package.json"));
|
|
23
|
+
for (const id of [`untestutils/vitest/${name}`, `@untestutils/vitest/${name}`]) {
|
|
24
|
+
try {
|
|
25
|
+
return req.resolve(id);
|
|
26
|
+
} catch {}
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const pkgRoot = dirname(req.resolve("@untestutils/vitest/package.json"));
|
|
30
|
+
const mjs = join(pkgRoot, "dist", `${name}.mjs`);
|
|
31
|
+
if (companionIo.existsSync(mjs)) return mjs;
|
|
32
|
+
const ts = join(pkgRoot, "src", `${name}.ts`);
|
|
33
|
+
if (companionIo.existsSync(ts)) return ts;
|
|
34
|
+
} catch {}
|
|
35
|
+
const here = dirname(companionIo.fileURLToPath(import.meta.url));
|
|
36
|
+
const localTs = join(here, `${name}.ts`);
|
|
37
|
+
if (companionIo.existsSync(localTs)) return localTs;
|
|
38
|
+
throw new Error(`[untestutils] missing companion ${name}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function untestutils(options = {}) {
|
|
42
|
+
if (options.recipes) {
|
|
43
|
+
ensureRecipes(options.recipes);
|
|
44
|
+
}
|
|
45
|
+
const artifactsRoot = options.artifactsRoot;
|
|
46
|
+
const prewarm = options.prewarm ?? [];
|
|
47
|
+
const recipesModule = options.recipesModule ?? getRecipesModulePath();
|
|
48
|
+
const browsers = normalizeHarnessBrowsers(options.browsers);
|
|
49
|
+
return {
|
|
50
|
+
name: "untestutils",
|
|
51
|
+
configureVitest({ project }) {
|
|
52
|
+
const config = project.config;
|
|
53
|
+
config.sequence ??= {};
|
|
54
|
+
config.sequence.setupFiles = "list";
|
|
55
|
+
config.globalSetup = [...asArray(config.globalSetup), companion("global-setup")];
|
|
56
|
+
config.setupFiles = [...asArray(config.setupFiles), companion("setup-file")];
|
|
57
|
+
if (options.coverage) {
|
|
58
|
+
const next = options.coverage === true ? createCoverageConfig() : createCoverageConfig(options.coverage);
|
|
59
|
+
config.coverage = {
|
|
60
|
+
...typeof config.coverage === "object" && config.coverage ? config.coverage : {},
|
|
61
|
+
...next,
|
|
62
|
+
exclude: [...config.coverage?.exclude ?? [], ...next.exclude],
|
|
63
|
+
include: next.include,
|
|
64
|
+
thresholds: {
|
|
65
|
+
...config.coverage?.thresholds ?? {},
|
|
66
|
+
...next.thresholds
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
process.env.UNTESTUTILS_PREWARM = JSON.stringify(prewarm);
|
|
71
|
+
process.env.UNTESTUTILS_BROWSERS = JSON.stringify(browsers);
|
|
72
|
+
if (!process.env.UNTESTUTILS_BROWSER) {
|
|
73
|
+
process.env.UNTESTUTILS_BROWSER = browsers[0];
|
|
74
|
+
}
|
|
75
|
+
if (artifactsRoot) process.env.UNTESTUTILS_ARTIFACTS_DIR = artifactsRoot;
|
|
76
|
+
if (recipesModule) {
|
|
77
|
+
process.env.UNTESTUTILS_RECIPES_MODULE = recipesModule;
|
|
78
|
+
} else if (prewarm.length) {
|
|
79
|
+
debug("vitest", "prewarm set but recipes module path unknown — pass recipes from defineRecipes(..., import.meta.url)");
|
|
80
|
+
}
|
|
81
|
+
debug("vitest", "configured globalSetup + setupFiles");
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function asArray(v) {
|
|
86
|
+
if (!v) return [];
|
|
87
|
+
return Array.isArray(v) ? v : [v];
|
|
88
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup-file.d.ts","sourceRoot":"","sources":["../src/setup-file.ts"],"names":[],"mappings":"AAOA,gBAAgB;AAChB,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAUtD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import { resolveArtifactsRoot, TargetRegistry } from "@untestutils/core";
|
|
4
|
+
|
|
5
|
+
export async function applyWorkerSetup() {
|
|
6
|
+
const recipesModule = process.env.UNTESTUTILS_RECIPES_MODULE;
|
|
7
|
+
if (recipesModule) {
|
|
8
|
+
await import(pathToFileURL(recipesModule).href);
|
|
9
|
+
}
|
|
10
|
+
const artifactsRoot = resolveArtifactsRoot();
|
|
11
|
+
const registry = new TargetRegistry(artifactsRoot);
|
|
12
|
+
const map = await registry.read();
|
|
13
|
+
registry.applyAllToEnv(map);
|
|
14
|
+
}
|
|
15
|
+
await applyWorkerSetup();
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@untestutils/vitest",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist"
|
|
6
|
+
],
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": [
|
|
9
|
+
"./dist/setup-file.mjs"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.mjs",
|
|
15
|
+
"default": "./dist/index.mjs"
|
|
16
|
+
},
|
|
17
|
+
"./plugin": {
|
|
18
|
+
"types": "./dist/plugin.d.ts",
|
|
19
|
+
"import": "./dist/plugin.mjs",
|
|
20
|
+
"default": "./dist/plugin.mjs"
|
|
21
|
+
},
|
|
22
|
+
"./global-setup": {
|
|
23
|
+
"types": "./dist/global-setup.d.ts",
|
|
24
|
+
"import": "./dist/global-setup.mjs",
|
|
25
|
+
"default": "./dist/global-setup.mjs"
|
|
26
|
+
},
|
|
27
|
+
"./setup-file": {
|
|
28
|
+
"types": "./dist/setup-file.d.ts",
|
|
29
|
+
"import": "./dist/setup-file.mjs",
|
|
30
|
+
"default": "./dist/setup-file.mjs"
|
|
31
|
+
},
|
|
32
|
+
"./package.json": "./package.json"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"pathe": "^2.0.3",
|
|
36
|
+
"@untestutils/core": "0.5.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^26.6.1",
|
|
40
|
+
"publint": "^0.3.24",
|
|
41
|
+
"typescript": "^6.0.3",
|
|
42
|
+
"vitest": "^5.0.1",
|
|
43
|
+
"obuild": "^0.4.40"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@playwright/test": "^1.63.0",
|
|
47
|
+
"playwright-core": "^1.63.0",
|
|
48
|
+
"vitest": "^5.0.1"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"@playwright/test": {
|
|
52
|
+
"optional": true
|
|
53
|
+
},
|
|
54
|
+
"playwright-core": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
57
|
+
"vitest": {
|
|
58
|
+
"optional": true
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=20"
|
|
63
|
+
},
|
|
64
|
+
"license": "MIT",
|
|
65
|
+
"homepage": "https://s00d.github.io/untestutils/",
|
|
66
|
+
"repository": {
|
|
67
|
+
"type": "git",
|
|
68
|
+
"url": "git+https://github.com/s00d/untestutils.git",
|
|
69
|
+
"directory": "packages/vitest"
|
|
70
|
+
},
|
|
71
|
+
"main": "./dist/index.mjs",
|
|
72
|
+
"types": "./dist/index.d.ts",
|
|
73
|
+
"publishConfig": {
|
|
74
|
+
"access": "public"
|
|
75
|
+
},
|
|
76
|
+
"scripts": {
|
|
77
|
+
"build": "obuild && tsc -p tsconfig.build.json",
|
|
78
|
+
"publint": "publint --strict"
|
|
79
|
+
}
|
|
80
|
+
}
|