@interfere/test-utils 9.1.1 → 10.0.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.
@@ -1,5 +1,7 @@
1
1
  //#region src/playwright/protection-bypass.d.ts
2
- type ProtectionProvider = "cloudflare-access";
3
- declare function protectionBypassHeaders(baseURL: string | undefined, provider: ProtectionProvider): Record<string, string> | undefined;
4
- //#endregion
5
- export { ProtectionProvider, protectionBypassHeaders };
2
+ export type ProtectionProvider = "cloudflare-access";
3
+ export declare function protectionBypassHeaders(baseURL: string | undefined, provider: ProtectionProvider): {
4
+ "CF-Access-Client-Id": string;
5
+ "CF-Access-Client-Secret": string;
6
+ } | undefined;
7
+ //#endregion
@@ -1 +1 @@
1
- const LOCAL_HOSTNAMES=new Set([`localhost`,`127.0.0.1`,`0.0.0.0`,`[::1]`]);function isLocal(baseURL){try{return LOCAL_HOSTNAMES.has(new URL(baseURL).hostname)}catch{return!1}}function cloudflareAccessHeaders(baseURL){let clientId=process.env.CF_ACCESS_CLIENT_ID,clientSecret=process.env.CF_ACCESS_CLIENT_SECRET;if(!(clientId&&clientSecret))throw Error([`Missing CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET while BASE_URL points at a remote Cloudflare deployment.`,`BASE_URL=${baseURL}`,"Playwright cannot bypass Cloudflare Access without the `cf-iac-ci` service token.","Fetch both values from GSM: `cf-access-service-token-id` and `cf-access-service-token-secret`."].join(` `));return{"CF-Access-Client-Id":clientId,"CF-Access-Client-Secret":clientSecret}}function protectionBypassHeaders(baseURL,provider){if(!(!baseURL||isLocal(baseURL))){if(provider===`cloudflare-access`)return cloudflareAccessHeaders(baseURL);throw Error(`Unsupported protection provider: ${provider}`)}}export{protectionBypassHeaders};
1
+ const LOCAL_HOSTNAMES=new Set([`localhost`,`127.0.0.1`,`0.0.0.0`,`[::1]`]);function isLocal(baseURL){try{return LOCAL_HOSTNAMES.has(new URL(baseURL).hostname)}catch{return!1}}function cloudflareAccessHeaders(baseURL){let clientId=process.env.CF_ACCESS_CLIENT_ID,clientSecret=process.env.CF_ACCESS_CLIENT_SECRET;if(!(clientId&&clientSecret))throw Error([`Missing CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET while baseURL points at a remote Cloudflare deployment.`,`baseURL=${baseURL}`,`Set both environment variables to a Cloudflare Access service token authorized for this deployment.`].join(` `));return{"CF-Access-Client-Id":clientId,"CF-Access-Client-Secret":clientSecret}}function protectionBypassHeaders(baseURL,provider){if(baseURL&&!isLocal(baseURL)){if(provider===`cloudflare-access`)return cloudflareAccessHeaders(baseURL);throw Error(`Unsupported protection provider: ${String(provider)}`)}}export{protectionBypassHeaders};
@@ -1,15 +1,15 @@
1
1
  //#region src/vitest/package-contract.d.ts
2
- declare const PACKAGE_CONTRACT_TIMEOUT_MS = 30000;
3
- interface PackageContractOptions {
4
- packageJsonUrl: URL;
2
+ interface PackageJson {
3
+ dependencies?: Record<string, string>;
4
+ devDependencies?: Record<string, string>;
5
+ name: string;
6
+ peerDependencies?: Record<string, string>;
7
+ private?: boolean;
5
8
  }
6
- interface PackedPackageFilesOptions extends PackageContractOptions {
7
- expectedFiles: string[];
8
- }
9
- interface NodePackageResolutionOptions extends PackageContractOptions {
10
- specifiers: string[];
11
- }
12
- declare function expectPackedPackageFiles({ expectedFiles, packageJsonUrl }: PackedPackageFilesOptions): Promise<void>;
13
- declare function expectNodeCanLoadPackageSpecifiers({ packageJsonUrl, specifiers }: NodePackageResolutionOptions): Promise<void>;
14
- //#endregion
15
- export { PACKAGE_CONTRACT_TIMEOUT_MS, expectNodeCanLoadPackageSpecifiers, expectPackedPackageFiles };
9
+ export declare function createPackedPackage(packageJsonUrl: URL): Promise<{
10
+ cleanup: () => Promise<void>;
11
+ consumerDir: string;
12
+ files: string[];
13
+ packageJson: PackageJson;
14
+ }>;
15
+ //#endregion
@@ -1,4 +1,2 @@
1
- import path from"node:path";import{execFile}from"node:child_process";import{mkdir,mkdtemp,readFile,rename,rm,symlink}from"node:fs/promises";import{tmpdir}from"node:os";import{fileURLToPath}from"node:url";import{promisify}from"node:util";import{expect}from"vitest";const execFileAsync=promisify(execFile),PACKED_LINE=/^packed\s+\S+\s+(?<file>.+)$/u,TGZ_LINE=/^(?<filename>\S+\.tgz)$/u,PACKAGE_CONTRACT_TIMEOUT_MS=3e4;async function expectPackedPackageFiles({expectedFiles,packageJsonUrl}){let packedPackage=await createPackedPackage(packageJsonUrl);try{expect(packedPackage.files).toEqual(expect.arrayContaining(expectedFiles)),expect(packedPackage.files.filter(file=>file.startsWith(`src/`))).toEqual([]),expect(packedPackage.files.filter(file=>file.endsWith(`.map`))).toEqual([]),expect(packedPackage.packageJson.private).toBeUndefined(),expect(Object.keys(packedPackage.packageJson.devDependencies??{})).not.toContain(`@interfere/tsdown-config`)}finally{await packedPackage.cleanup()}}async function expectNodeCanLoadPackageSpecifiers({packageJsonUrl,specifiers}){let packedPackage=await createPackedPackage(packageJsonUrl);try{await runNodeScript({args:[`--input-type=module`,`--eval`,importScript(specifiers)],cwd:packedPackage.consumerDir,label:`ESM import`}),await runNodeScript({args:[`--eval`,requireScript(specifiers)],cwd:packedPackage.consumerDir,label:`CommonJS require`})}finally{await packedPackage.cleanup()}}async function createPackedPackage(packageJsonUrl){let packageDir=path.dirname(fileURLToPath(packageJsonUrl)),tempDir=await mkdtemp(path.join(tmpdir(),`interfere-package-contract-`)),consumerDir=path.join(tempDir,`consumer`),nodeModulesDir=path.join(consumerDir,`node_modules`),packageJson=JSON.parse(await readFile(packageJsonUrl,`utf-8`)),installedPackageDir=joinPackageName(nodeModulesDir,packageJson.name);await mkdir(path.dirname(installedPackageDir),{recursive:!0});let{stdout}=await execFileAsync(`bun`,[`pm`,`pack`,`--ignore-scripts`],{cwd:packageDir}),{filename,files}=parseBunPackOutput(stdout),tarballPath=path.join(packageDir,filename),extractedDir=path.join(tempDir,`extracted`);await mkdir(extractedDir),await execFileAsync(`tar`,[`-xzf`,tarballPath,`-C`,extractedDir]),await rm(tarballPath),await rename(path.join(extractedDir,`package`),installedPackageDir),await linkRuntimeDependencies({nodeModulesDir,packageDir,packageJson});let packedPackageJson=JSON.parse(await readFile(path.join(installedPackageDir,`package.json`),`utf-8`));return{cleanup:()=>rm(tempDir,{force:!0,recursive:!0}),consumerDir,files:files.toSorted(),packageJson:packedPackageJson}}function parseBunPackOutput(stdout){let files=[],filename=``;for(let line of stdout.split(`
2
- `)){let packMatch=line.match(PACKED_LINE);if(packMatch?.[1]){files.push(packMatch[1]);continue}let tgzName=line.match(TGZ_LINE)?.groups?.filename;tgzName&&(filename=tgzName)}if(!filename)throw Error(`bun pm pack did not produce a tarball:\n${stdout}`);return{filename,files}}async function linkRuntimeDependencies({nodeModulesDir,packageDir,packageJson}){let dependencies=new Set([...Object.keys(packageJson.dependencies??{}),...Object.keys(packageJson.peerDependencies??{})]);for(let dependencyName of dependencies){if(dependencyName===packageJson.name)continue;let source=joinPackageName(path.join(packageDir,`node_modules`),dependencyName),target=joinPackageName(nodeModulesDir,dependencyName);await mkdir(path.dirname(target),{recursive:!0}),await symlink(source,target,`junction`)}}function joinPackageName(nodeModulesDir,packageName){return path.join(nodeModulesDir,...packageName.split(`/`))}async function runNodeScript({args,cwd,label}){try{await execFileAsync(process.execPath,args,{cwd})}catch(error){throw error instanceof Error?TypeError(`${label} package contract failed:\n${error.message}`,{cause:error}):error}}function importScript(specifiers){return specifiers.map(specifier=>`await import(${JSON.stringify(specifier)});`).join(`
3
- `)}function requireScript(specifiers){return specifiers.map(specifier=>`require(${JSON.stringify(specifier)});`).join(`
4
- `)}export{PACKAGE_CONTRACT_TIMEOUT_MS,expectNodeCanLoadPackageSpecifiers,expectPackedPackageFiles};
1
+ import{mapWithConcurrency}from"@interfere/helpers/map-with-concurrency";import{execFile}from"node:child_process";import{mkdir,mkdtemp,readFile,rename,rm,symlink}from"node:fs/promises";import{tmpdir}from"node:os";import path from"node:path";import{fileURLToPath}from"node:url";import{promisify}from"node:util";const execFileAsync=promisify(execFile),PACKED_LINE=/^packed\s+\S+\s+(?<file>.+)$/u,TGZ_LINE=/^(?<filename>\S+\.tgz)$/u;function isDependencyMap(value){return typeof value==`object`&&!!value&&!Array.isArray(value)&&Object.values(value).every(version=>typeof version==`string`)}function isPackageJson(value){return typeof value==`object`&&!!value&&`name`in value&&typeof value.name==`string`&&(!(`private`in value)||typeof value.private==`boolean`)&&(!(`dependencies`in value)||isDependencyMap(value.dependencies))&&(!(`devDependencies`in value)||isDependencyMap(value.devDependencies))&&(!(`peerDependencies`in value)||isDependencyMap(value.peerDependencies))}async function readPackageJson(file){let value=JSON.parse(await readFile(file,`utf8`));if(!isPackageJson(value))throw TypeError(`Package manifest has an invalid name, privacy flag, or dependency map.`);return value}async function createPackedPackage(packageJsonUrl){let packageDir=path.dirname(fileURLToPath(packageJsonUrl)),packageJson=await readPackageJson(packageJsonUrl),tempDir=await mkdtemp(path.join(tmpdir(),`interfere-package-contract-`)),consumerDir=path.join(tempDir,`consumer`),nodeModulesDir=path.join(consumerDir,`node_modules`);try{let installedPackageDir=joinPackageName(nodeModulesDir,packageJson.name);await mkdir(path.dirname(installedPackageDir),{recursive:!0});let{stdout}=await execFileAsync(`bun`,[`pm`,`pack`,`--ignore-scripts`,`--destination`,tempDir],{cwd:packageDir}),{filename,files}=parseBunPackOutput(stdout),tarballPath=path.join(tempDir,path.basename(filename)),extractedDir=path.join(tempDir,`extracted`);await mkdir(extractedDir),await execFileAsync(`tar`,[`-xzf`,tarballPath,`-C`,extractedDir]),await rm(tarballPath),await rename(path.join(extractedDir,`package`),installedPackageDir),await linkRuntimeDependencies({nodeModulesDir,packageDir,packageJson});let packedPackageJson=await readPackageJson(path.join(installedPackageDir,`package.json`));return{cleanup:async()=>rm(tempDir,{force:!0,recursive:!0}),consumerDir,files:files.toSorted(),packageJson:packedPackageJson}}catch(error){throw await rm(tempDir,{force:!0,recursive:!0}),error}}function parseBunPackOutput(stdout){let files=[],filename=``;for(let line of stdout.split(`
2
+ `)){let packMatch=PACKED_LINE.exec(line);if(packMatch?.[1]){files.push(packMatch[1]);continue}let tgzName=TGZ_LINE.exec(line)?.groups?.filename;tgzName&&(filename=tgzName)}if(!filename)throw Error(`bun pm pack did not produce a tarball:\n${stdout}`);return{filename,files}}async function linkRuntimeDependencies({nodeModulesDir,packageDir,packageJson}){let dependencies=new Set([...Object.keys(packageJson.dependencies??{}),...Object.keys(packageJson.peerDependencies??{})]),results=await mapWithConcurrency([...dependencies],16,async dependencyName=>{if(dependencyName===packageJson.name)return{ok:!0};try{let source=joinPackageName(path.join(packageDir,`node_modules`),dependencyName),target=joinPackageName(nodeModulesDir,dependencyName);return await mkdir(path.dirname(target),{recursive:!0}),await symlink(source,target,`junction`),{ok:!0}}catch(error){return{ok:!1,error}}});for(let result of results)if(!result.ok)throw result.error}function joinPackageName(nodeModulesDir,packageName){let segments=packageName.split(`/`),validSegments=packageName.startsWith(`@`)?segments.length===2&&segments[0]!==`@`:segments.length===1,target=path.resolve(nodeModulesDir,packageName),relative=path.relative(nodeModulesDir,target);if(!validSegments||packageName.includes(`\\`)||segments.some(segment=>!segment||segment===`.`||segment===`..`)||!relative||relative===`..`||relative.startsWith(`..${path.sep}`)||path.isAbsolute(relative))throw Error(`Package path must remain inside the owned node_modules directory.`);return target}export{createPackedPackage};
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@interfere/test-utils",
3
- "version": "9.1.1",
4
- "license": "MIT",
3
+ "version": "10.0.0",
5
4
  "description": "Shared test utilities used across Interfere packages.",
6
5
  "homepage": "https://interfere.com",
7
6
  "bugs": {
8
7
  "url": "mailto:support@interfere.com"
9
8
  },
9
+ "license": "MIT",
10
10
  "author": "Interfere <support@interfere.com> (https://interfere.com)",
11
11
  "repository": {
12
12
  "type": "git",
@@ -18,11 +18,6 @@
18
18
  ],
19
19
  "type": "module",
20
20
  "exports": {
21
- "./lib/*": {
22
- "monorepo": "./src/lib/*.ts",
23
- "types": "./dist/lib/*.d.mts",
24
- "default": "./dist/lib/*.mjs"
25
- },
26
21
  "./playwright/*": {
27
22
  "monorepo": "./src/playwright/*.ts",
28
23
  "types": "./dist/playwright/*.d.mts",
@@ -33,16 +28,6 @@
33
28
  "types": "./dist/vitest/*.d.mts",
34
29
  "default": "./dist/vitest/*.mjs"
35
30
  },
36
- "./vitest/presets/*": {
37
- "monorepo": "./src/vitest/presets/*.ts",
38
- "types": "./dist/vitest/presets/*.d.mts",
39
- "default": "./dist/vitest/presets/*.mjs"
40
- },
41
- "./vitest/msw/*": {
42
- "monorepo": "./src/vitest/msw/*.ts",
43
- "types": "./dist/vitest/msw/*.d.mts",
44
- "default": "./dist/vitest/msw/*.mjs"
45
- },
46
31
  "./package.json": "./package.json"
47
32
  },
48
33
  "publishConfig": {
@@ -50,47 +35,18 @@
50
35
  },
51
36
  "scripts": {
52
37
  "build": "tsdown",
38
+ "test": "vitest run",
53
39
  "typecheck": "tsc --noEmit"
54
40
  },
55
- "peerDependencies": {
56
- "@cloudflare/vitest-pool-workers": "^0.21.0",
57
- "@playwright/test": "^1.62.1",
58
- "@vitejs/plugin-react": "^6.0.5",
59
- "@vitest/browser-playwright": "4.1.10",
60
- "msw": "^2.14.6",
61
- "vitest": "4.1.10"
62
- },
63
- "peerDependenciesMeta": {
64
- "@cloudflare/vitest-pool-workers": {
65
- "optional": true
66
- },
67
- "@playwright/test": {
68
- "optional": true
69
- },
70
- "@vitejs/plugin-react": {
71
- "optional": true
72
- },
73
- "@vitest/browser-playwright": {
74
- "optional": true
75
- },
76
- "msw": {
77
- "optional": true
78
- }
79
- },
80
41
  "dependencies": {
81
- "@interfere/helpers": "^9.1.1",
82
- "dotenv": "^17.4.2"
42
+ "@interfere/helpers": "^9.2.0"
83
43
  },
84
44
  "devDependencies": {
85
- "@cloudflare/vitest-pool-workers": "^0.21.0",
86
45
  "@interfere/typescript-config": "^9.0.2",
87
- "@playwright/test": "^1.62.1",
88
- "@types/node": "^26.2.0",
89
- "@vitejs/plugin-react": "^6.0.5",
90
- "@vitest/browser-playwright": "4.1.10",
91
- "msw": "^2.14.6",
92
- "tsdown": "^0.22.2",
46
+ "@interfere/vitest-config": "0.0.0",
47
+ "@types/node": "^26.4.1",
48
+ "tsdown": "^0.23.0",
93
49
  "typescript": "^7.0.2",
94
- "vitest": "4.1.10"
50
+ "vitest": "5.0.1"
95
51
  }
96
52
  }
@@ -1,6 +0,0 @@
1
- //#region src/lib/tags.d.ts
2
- declare const COMMON: "@common";
3
- declare const DESKTOP: "@desktop";
4
- declare const MOBILE: "@mobile";
5
- //#endregion
6
- export { COMMON, DESKTOP, MOBILE };
package/dist/lib/tags.mjs DELETED
@@ -1 +0,0 @@
1
- const COMMON=`@common`,DESKTOP=`@desktop`,MOBILE=`@mobile`;export{COMMON,DESKTOP,MOBILE};
@@ -1,13 +0,0 @@
1
- import { TestUserConfig } from "vitest/config";
2
- //#region src/lib/viewports.d.ts
3
- type Viewport = NonNullable<TestUserConfig["browser"]>["viewport"];
4
- declare const DESKTOP: {
5
- readonly width: 1280;
6
- readonly height: 720;
7
- };
8
- declare const MOBILE: {
9
- readonly width: 390;
10
- readonly height: 844;
11
- };
12
- //#endregion
13
- export { DESKTOP, MOBILE, Viewport };
@@ -1 +0,0 @@
1
- const DESKTOP={width:1280,height:720},MOBILE={width:390,height:844};export{DESKTOP,MOBILE};
@@ -1,48 +0,0 @@
1
- //#region src/playwright/base.d.ts
2
- declare const _default: {
3
- fullyParallel: true;
4
- forbidOnly: boolean;
5
- retries: number;
6
- reporter: ["html"][] | (["github"] | ["junit", {
7
- outputFile: string;
8
- }])[];
9
- use: {
10
- trace: "on-first-retry";
11
- screenshot: "only-on-failure";
12
- actionTimeout?: number;
13
- launchOptions?: {
14
- executablePath: string;
15
- };
16
- };
17
- expect?: {
18
- timeout: number;
19
- };
20
- projects: ({
21
- name: string;
22
- grep: RegExp;
23
- use: {
24
- viewport: import("@playwright/test").ViewportSize;
25
- userAgent: string;
26
- deviceScaleFactor: number;
27
- isMobile: boolean;
28
- hasTouch: boolean;
29
- defaultBrowserType: 'chromium' | 'firefox' | 'webkit';
30
- };
31
- } | {
32
- name: string;
33
- grep: RegExp;
34
- use: {
35
- userAgent: string;
36
- deviceScaleFactor: number;
37
- defaultBrowserType: 'chromium' | 'firefox' | 'webkit';
38
- viewport: {
39
- readonly width: 390;
40
- readonly height: 844;
41
- };
42
- isMobile: true;
43
- hasTouch: true;
44
- };
45
- })[];
46
- };
47
- //#endregion
48
- export { _default as default };
@@ -1 +0,0 @@
1
- import{COMMON,DESKTOP,MOBILE}from"../lib/tags.mjs";import{MOBILE as MOBILE$1}from"../lib/viewports.mjs";import{devices}from"@playwright/test";const ci=!!process.env.CI,chromiumExecutablePath=process.env.CHROMIUM_EXECUTABLE_PATH;var base_default={fullyParallel:!0,forbidOnly:ci,retries:ci?2:0,reporter:ci?[[`github`],[`junit`,{outputFile:`./test-results/e2e-junit.xml`}]]:[[`html`]],use:{trace:`on-first-retry`,screenshot:`only-on-failure`,...ci&&{actionTimeout:1e4},...chromiumExecutablePath&&{launchOptions:{executablePath:chromiumExecutablePath}}},...ci&&{expect:{timeout:1e4}},projects:[{name:`desktop`,grep:RegExp(`${DESKTOP}|${COMMON}`,`u`),use:{...devices[`Desktop Chrome`]}},{name:`mobile`,grep:RegExp(`${MOBILE}|${COMMON}`,`u`),use:{...devices[`Desktop Chrome`],viewport:MOBILE$1,isMobile:!0,hasTouch:!0}}]};export{base_default as default};
@@ -1,5 +0,0 @@
1
- import { PlaywrightTestConfig } from "@playwright/test";
2
- //#region src/playwright/define-config.d.ts
3
- declare function defineConfig(...overrides: (Partial<PlaywrightTestConfig> | undefined)[]): PlaywrightTestConfig<{}, {}>;
4
- //#endregion
5
- export { defineConfig };
@@ -1 +0,0 @@
1
- import base_default from"./base.mjs";import{defineConfig as defineConfig$1}from"@playwright/test";import{deepMerge}from"@interfere/helpers/deep-merge";function defineConfig(...overrides){return defineConfig$1(deepMerge(base_default,...overrides))}export{defineConfig};
@@ -1,52 +0,0 @@
1
- //#region src/vitest/base.d.ts
2
- declare const _default: {
3
- root: string;
4
- resolve: {
5
- conditions: string[];
6
- };
7
- ssr: {
8
- resolve: {
9
- conditions: string[];
10
- };
11
- };
12
- test: {
13
- env: {
14
- NODE_ENV: string;
15
- };
16
- environment: "node";
17
- globals: true;
18
- isolate: true;
19
- pool: "forks";
20
- maxWorkers: string;
21
- clearMocks: true;
22
- restoreMocks: true;
23
- ui: boolean;
24
- unstubEnvs: true;
25
- unstubGlobals: true;
26
- sequence: {
27
- shuffle: boolean;
28
- };
29
- server: {
30
- deps: {
31
- inline: string[];
32
- };
33
- };
34
- include: string[];
35
- exclude: string[];
36
- reporters: ("default" | "junit")[];
37
- coverage: {
38
- enabled: true;
39
- provider: "v8";
40
- include: string[];
41
- exclude: string[];
42
- reportOnFailure: true;
43
- reporter: ("html" | "lcov" | "text-summary")[] | ("json" | "json-summary" | "lcov")[];
44
- };
45
- outputFile: {
46
- junit: string;
47
- };
48
- setupFiles: string[];
49
- };
50
- };
51
- //#endregion
52
- export { _default as default };
@@ -1 +0,0 @@
1
- import{existsSync}from"node:fs";import path from"node:path";import{config}from"dotenv";import{configDefaults}from"vitest/config";const root=process.cwd(),isCI=!!process.env.CI,env=config({path:path.resolve(root,`.env.test`),quiet:!0}).parsed??{},setupFiles=[`./test/setup.ts`,`./test/unit/setup.ts`].filter(rel=>existsSync(path.resolve(root,rel)));var base_default={root,resolve:{conditions:[`monorepo`]},ssr:{resolve:{conditions:[`monorepo`]}},test:{env:{...env,NODE_ENV:`test`},environment:`node`,globals:!0,isolate:!0,pool:`forks`,maxWorkers:`50%`,clearMocks:!0,restoreMocks:!0,ui:!!process.env.UI,unstubEnvs:!0,unstubGlobals:!0,sequence:{shuffle:!isCI},server:{deps:{inline:[`zod`]}},include:[`src/**/*.{test,spec}.?(c|m)[jt]s?(x)`,`test/**/*.{test,spec}.?(c|m)[jt]s?(x)`],exclude:[`test/e2e/**/*.{test,spec}.?(c|m)[jt]s?(x)`,...configDefaults.exclude],reporters:[`default`,`junit`],coverage:{enabled:!0,provider:`v8`,include:[`src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}`],exclude:[`**/*.json`,...configDefaults.coverage?.exclude??[]],reportOnFailure:!0,reporter:isCI?[`json`,`json-summary`,`lcov`]:[`text-summary`,`html`,`lcov`]},outputFile:{junit:`./test-results/junit.xml`},setupFiles}};export{base_default as default};
@@ -1,5 +0,0 @@
1
- import { ViteUserConfig } from "vitest/config";
2
- //#region src/vitest/define-config.d.ts
3
- declare function defineConfig(...overrides: (Partial<ViteUserConfig> | undefined)[]): ViteUserConfig;
4
- //#endregion
5
- export { defineConfig };
@@ -1 +0,0 @@
1
- import base_default from"./base.mjs";import{deepMerge}from"@interfere/helpers/deep-merge";import{defineConfig as defineConfig$1}from"vitest/config";function defineConfig(...overrides){return defineConfig$1(deepMerge(base_default,...overrides))}export{defineConfig};
@@ -1,5 +0,0 @@
1
- //#region src/vitest/msw/browser.d.ts
2
- declare const worker: import("msw/browser").SetupWorker;
3
- declare function enableMSW(): void;
4
- //#endregion
5
- export { enableMSW, worker };
@@ -1 +0,0 @@
1
- import{afterAll,afterEach,beforeAll}from"vitest";import{setupWorker}from"msw/browser";const worker=setupWorker();function enableMSW(){beforeAll(async()=>{await worker.start({onUnhandledRequest:`bypass`,quiet:!0})}),afterEach(()=>{worker.resetHandlers()}),afterAll(()=>{worker.stop()})}export{enableMSW,worker};
@@ -1,15 +0,0 @@
1
- //#region src/vitest/msw/http.d.ts
2
- type HttpMethod = "post" | "get" | "put" | "delete" | "patch";
3
- type JsonValue = string | number | boolean | null | JsonValue[] | {
4
- [key: string]: JsonValue;
5
- };
6
- interface CapturedHttpCall {
7
- body: unknown;
8
- params: Record<string, string | readonly string[] | undefined>;
9
- }
10
- declare function createHttpInterceptor(baseUrl: string): {
11
- intercept: (method: HttpMethod, path: string, response: JsonValue, status?: number) => CapturedHttpCall[];
12
- interceptNetworkError: (method: HttpMethod, path: string) => void;
13
- };
14
- //#endregion
15
- export { CapturedHttpCall, HttpMethod, JsonValue, createHttpInterceptor };
@@ -1 +0,0 @@
1
- import{worker}from"./browser.mjs";import{HttpResponse,http}from"msw";async function parseBody(request){let clone=request.clone();if((clone.headers.get(`content-type`)??``).includes(`multipart/form-data`)){let formData=await clone.formData();return Object.fromEntries(formData.entries())}return clone.json().catch(()=>null)}function createHttpInterceptor(baseUrl){function intercept(method,path,response,status=200){let calls=[];return worker.use(http[method](`${baseUrl}${path}`,async({request,params})=>(calls.push({body:await parseBody(request),params}),HttpResponse.json(response,{status})))),calls}function interceptNetworkError(method,path){worker.use(http[method](`${baseUrl}${path}`,()=>HttpResponse.error()))}return{intercept,interceptNetworkError}}export{createHttpInterceptor};
@@ -1,12 +0,0 @@
1
- import { HttpMethod, JsonValue } from "./http.mjs";
2
- //#region src/vitest/msw/node.d.ts
3
- declare const server: import("msw/node").SetupServer;
4
- declare function useMswServer(): typeof server;
5
- interface CapturedRequest {
6
- headers: Headers;
7
- url: URL;
8
- }
9
- declare function interceptJson(method: HttpMethod, url: string, response: JsonValue, status?: number): CapturedRequest[];
10
- declare function interceptError(method: HttpMethod, url: string): void;
11
- //#endregion
12
- export { CapturedRequest, interceptError, interceptJson, server, useMswServer };
@@ -1 +0,0 @@
1
- import{afterAll,afterEach,beforeAll}from"vitest";import{HttpResponse,http}from"msw";import{setupServer}from"msw/node";const server=setupServer();function useMswServer(){return beforeAll(()=>server.listen({onUnhandledRequest:`error`})),afterEach(()=>server.resetHandlers()),afterAll(()=>server.close()),server}function interceptJson(method,url,response,status=200){let calls=[];return server.use(http[method](url,({request})=>(calls.push({headers:request.headers,url:new URL(request.url)}),HttpResponse.json(response,{status})))),calls}function interceptError(method,url){server.use(http[method](url,()=>HttpResponse.error()))}export{interceptError,interceptJson,server,useMswServer};
@@ -1,28 +0,0 @@
1
- import { TestUserConfig, UserWorkspaceConfig } from "vitest/config";
2
- //#region src/vitest/presets/browser.d.ts
3
- type BrowserConfig = NonNullable<TestUserConfig["browser"]>;
4
- type BrowserInstance = NonNullable<BrowserConfig["instances"]>[number];
5
- interface BrowserProjectOptions {
6
- /**
7
- * Playwright browser instances to run against. Use `dualViewport()` for the
8
- * standard Desktop + Mobile split keyed on the `src/**​/mobile/**` glob
9
- * convention.
10
- */
11
- instances: BrowserInstance[];
12
- testAlias?: string | false;
13
- trace?: boolean;
14
- [key: string]: unknown;
15
- }
16
- /**
17
- * Glob matching all tests under a `mobile/` directory. Used by `dualViewport`
18
- * to split the Desktop / Mobile test runs from a single file tree.
19
- */
20
- declare const MOBILE_TESTS = "src/**/mobile/*.{test,spec}.?(c|m)[jt]s?(x)";
21
- /**
22
- * Standard Desktop + Mobile viewport split for UI apps. Desktop runs every
23
- * test except those under `src/**​/mobile/**`; Mobile runs only those.
24
- */
25
- declare function dualViewport(): BrowserInstance[];
26
- declare function browserProject(opts: BrowserProjectOptions): UserWorkspaceConfig;
27
- //#endregion
28
- export { BrowserInstance, BrowserProjectOptions, MOBILE_TESTS, browserProject, dualViewport };
@@ -1 +0,0 @@
1
- import{DESKTOP,MOBILE}from"../../lib/viewports.mjs";import base_default from"../base.mjs";import{deepMerge}from"@interfere/helpers/deep-merge";import path from"node:path";import react from"@vitejs/plugin-react";import{playwright}from"@vitest/browser-playwright";const INSTANCE_BASELINE_EXCLUDE=[`test/e2e/**/*.{test,spec}.?(c|m)[jt]s?(x)`],chromiumExecutablePath=process.env.CHROMIUM_EXECUTABLE_PATH,MOBILE_TESTS=`src/**/mobile/*.{test,spec}.?(c|m)[jt]s?(x)`;function dualViewport(){return[{browser:`chromium`,name:`Chrome - Desktop`,viewport:DESKTOP,exclude:[...INSTANCE_BASELINE_EXCLUDE,MOBILE_TESTS]},{browser:`chromium`,name:`Chrome - Mobile`,viewport:MOBILE,exclude:INSTANCE_BASELINE_EXCLUDE,include:[MOBILE_TESTS]}]}function browserProject(opts){let{instances,trace,testAlias=`test/unit`,...rest}=opts,basis={server:{port:0,strictPort:!1},test:{name:`browser`,browser:{enabled:!0,headless:!process.env.UI,provider:chromiumExecutablePath?playwright({launchOptions:{executablePath:chromiumExecutablePath}}):playwright(),screenshotFailures:!1,instances,...trace&&{trace:{mode:`retain-on-failure`}}}},plugins:[react()],optimizeDeps:{entries:[`src/**/*.{ts,tsx}`,`test/unit/**/*.{ts,tsx}`],exclude:[`fsevents`,`@opentelemetry/otlp-transformer`]},...testAlias!==!1&&{resolve:{tsconfigPaths:!0,alias:{"@test":path.resolve(process.cwd(),testAlias)}}}};return deepMerge(base_default,basis,rest)}export{MOBILE_TESTS,browserProject,dualViewport};
@@ -1,47 +0,0 @@
1
- import { ViteUserConfig } from "vitest/config";
2
- //#region src/vitest/presets/integration.d.ts
3
- /**
4
- * Integration test resources. Each entry maps to a globalSetup module in
5
- * `@interfere/internal-test-utils` that owns a single shared container for
6
- * the duration of the run and tears it down on success, failure, and most
7
- * signal scenarios.
8
- *
9
- * The matching per-worker harness lives at
10
- * `@interfere/internal-test-utils/lifecycle/<resource>/harness` — import it
11
- * from your `test/setup.ts`.
12
- */
13
- type IntegrationResource = "pg" | "redis";
14
- interface IntegrationOptions {
15
- /**
16
- * Container resources to provision once per run. Defaults to `["pg"]`.
17
- */
18
- resources?: IntegrationResource[];
19
- /**
20
- * Give every worker fork its own logical Valkey database, keyed on
21
- * `VITEST_POOL_ID`, so test files can run in parallel. Without this the
22
- * redis harness flushes one shared database between tests, which forces
23
- * `fileParallelism: false` for the whole package.
24
- *
25
- * Every Valkey client in the package MUST come from
26
- * `@interfere/internal-test-utils/lifecycle/redis/harness`. Code that reads
27
- * `REDIS_URL` and connects for itself lands on database 0 and escapes the
28
- * per-worker isolation.
29
- */
30
- redisPerWorkerDatabase?: boolean;
31
- }
32
- /**
33
- * Build a Vitest config for an integration-test package. Wires up the
34
- * shared-container globalSetup modules so each test run spins up exactly
35
- * one of each requested resource (instead of one per worker fork) and tears
36
- * them down reliably on exit.
37
- *
38
- * @example
39
- * ```ts
40
- * export default defineIntegrationConfig({ resources: ["pg", "redis"] }, {
41
- * test: { coverage: { provider: "v8" } },
42
- * });
43
- * ```
44
- */
45
- declare function defineIntegrationConfig(options?: IntegrationOptions, ...overrides: (Partial<ViteUserConfig> | undefined)[]): ViteUserConfig;
46
- //#endregion
47
- export { IntegrationOptions, IntegrationResource, defineIntegrationConfig };
@@ -1 +0,0 @@
1
- import base_default from"../base.mjs";import{deepMerge}from"@interfere/helpers/deep-merge";import{defineConfig}from"vitest/config";const GLOBAL_SETUP_BY_RESOURCE={pg:`@interfere/internal-test-utils/lifecycle/pg/global-setup`,redis:`@interfere/internal-test-utils/lifecycle/redis/global-setup`};function defineIntegrationConfig(options={},...overrides){let resources=options.resources??[`pg`],globalSetup=resources.map(r=>GLOBAL_SETUP_BY_RESOURCE[r]),perWorkerDatabase=resources.includes(`redis`)&&options.redisPerWorkerDatabase===!0,redisOverrides=resources.includes(`redis`)&&!perWorkerDatabase?{test:{fileParallelism:!1}}:{};return defineConfig(deepMerge(base_default,{test:{globalSetup}},redisOverrides,perWorkerDatabase?{test:{env:{TEST_REDIS_PER_WORKER_DATABASE:`1`}}}:{},...overrides))}export{defineIntegrationConfig};
@@ -1,14 +0,0 @@
1
- import { UserWorkspaceConfig } from "vitest/config";
2
- //#region src/vitest/presets/node.d.ts
3
- /**
4
- * Build a Vitest project config running against the Node environment.
5
- * Spreads `opts` over a shared base; caller-provided fields win.
6
- *
7
- * `*.test.{tsx,jsx}` files are excluded by default — React test files can't
8
- * import `vitest-browser-react` outside of Browser Mode, so they always
9
- * belong in the `browserProject`. Opt a specific TSX test in by adding its
10
- * path to the caller's `test.include`.
11
- */
12
- declare function nodeProject(opts?: Partial<UserWorkspaceConfig>): UserWorkspaceConfig;
13
- //#endregion
14
- export { nodeProject };
@@ -1 +0,0 @@
1
- import base_default from"../base.mjs";import{deepMerge}from"@interfere/helpers/deep-merge";function nodeProject(opts={}){return deepMerge(base_default,{test:{name:`node`,environment:`node`,exclude:[`**/*.{test,spec}.tsx`,`**/*.{test,spec}.jsx`,`**/*.{test,spec}.mtsx`,`**/*.{test,spec}.mjsx`]}},opts)}export{nodeProject};
@@ -1,14 +0,0 @@
1
- import { TestUserConfig, UserWorkspaceConfig } from "vitest/config";
2
- //#region src/vitest/presets/vite.d.ts
3
- type BrowserConfig = NonNullable<TestUserConfig["browser"]>;
4
- type BrowserInstance = NonNullable<BrowserConfig["instances"]>[number];
5
- interface BrowserProjectOptions {
6
- instances: BrowserInstance[];
7
- testAlias?: string | false;
8
- trace?: boolean;
9
- [key: string]: unknown;
10
- }
11
- declare function dualViewport(): BrowserInstance[];
12
- declare function viteProject(opts: BrowserProjectOptions): UserWorkspaceConfig;
13
- //#endregion
14
- export { BrowserInstance, BrowserProjectOptions, dualViewport, viteProject };
@@ -1 +0,0 @@
1
- import{DESKTOP,MOBILE}from"../../lib/viewports.mjs";import base_default from"../base.mjs";import{deepMerge}from"@interfere/helpers/deep-merge";import{playwright}from"@vitest/browser-playwright";const INSTANCE_BASELINE_EXCLUDE=[`test/e2e/**/*.{test,spec}.?(c|m)[jt]s?(x)`],chromiumExecutablePath=process.env.CHROMIUM_EXECUTABLE_PATH;function dualViewport(){return[{browser:`chromium`,name:`Chrome - Desktop`,viewport:DESKTOP,exclude:INSTANCE_BASELINE_EXCLUDE},{browser:`chromium`,name:`Chrome - Mobile`,viewport:MOBILE,exclude:INSTANCE_BASELINE_EXCLUDE}]}function viteProject(opts){let{instances,trace,testAlias:_testAlias,...rest}=opts;return deepMerge({server:{port:0,strictPort:!1}},base_default,{test:{name:`vite`,browser:{enabled:!0,provider:chromiumExecutablePath?playwright({launchOptions:{executablePath:chromiumExecutablePath}}):playwright(),screenshotFailures:!1,instances,...trace&&{trace:{mode:`retain-on-failure`}}}}},rest)}export{dualViewport,viteProject};
@@ -1,24 +0,0 @@
1
- import { ViteUserConfig } from "vitest/config";
2
- import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
3
- //#region src/vitest/presets/workers.d.ts
4
- type CloudflareTestOptions = NonNullable<Parameters<typeof cloudflareTest>[0]>;
5
- interface WorkersProjectOptions extends Partial<ViteUserConfig> {
6
- /**
7
- * Passed through to `cloudflareTest()`, merged over a default that points at
8
- * the worker's own `wrangler.jsonc`. Use it for Miniflare service bindings
9
- * and outbound request mocks.
10
- */
11
- cloudflare?: CloudflareTestOptions;
12
- }
13
- /**
14
- * Build a Vitest config that runs a Cloudflare Worker's tests inside workerd
15
- * via the Workers Vitest integration.
16
- *
17
- * Intentionally does not extend the shared Node base config: that pins
18
- * `environment: "node"` and `pool: "forks"`, both of which contradict running
19
- * inside the Workers runtime. Coverage uses Istanbul because V8 coverage is
20
- * unavailable in workerd.
21
- */
22
- declare function workersProject(opts?: WorkersProjectOptions): ViteUserConfig;
23
- //#endregion
24
- export { WorkersProjectOptions, workersProject };
@@ -1 +0,0 @@
1
- import{deepMerge}from"@interfere/helpers/deep-merge";import{cloudflareTest}from"@cloudflare/vitest-pool-workers";const isCI=!!process.env.CI;function workersProject(opts={}){let{cloudflare={},...overrides}=opts;return deepMerge({plugins:[cloudflareTest(deepMerge({wrangler:{configPath:`./wrangler.jsonc`}},cloudflare))],test:{reporters:isCI?[`default`,`junit`]:[`default`],...isCI&&{outputFile:{junit:`./test-results/junit.xml`}},coverage:{enabled:!0,provider:`istanbul`,include:[`src/**/*.{ts,mts}`],reporter:isCI?[`json`,`json-summary`,`lcov`]:[`text`,`html`]}}},overrides)}export{workersProject};