@interfere/test-utils 1.0.0 → 9.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.
Files changed (55) hide show
  1. package/dist/lib/tags.d.mts +6 -0
  2. package/dist/lib/tags.d.mts.map +1 -0
  3. package/dist/lib/tags.mjs +6 -0
  4. package/dist/lib/tags.mjs.map +1 -0
  5. package/dist/lib/viewports.d.mts +14 -0
  6. package/dist/lib/viewports.d.mts.map +1 -0
  7. package/dist/lib/viewports.mjs +11 -0
  8. package/dist/lib/viewports.mjs.map +1 -0
  9. package/dist/playwright/base.d.mts +46 -0
  10. package/dist/playwright/base.d.mts.map +1 -0
  11. package/dist/playwright/base.mjs +32 -0
  12. package/dist/playwright/base.mjs.map +1 -0
  13. package/dist/playwright/define-config.d.mts +6 -0
  14. package/dist/playwright/define-config.d.mts.map +1 -0
  15. package/dist/playwright/define-config.mjs +9 -0
  16. package/dist/playwright/define-config.mjs.map +1 -0
  17. package/dist/vitest/base.d.mts +43 -0
  18. package/dist/vitest/base.d.mts.map +1 -0
  19. package/dist/vitest/base.mjs +57 -0
  20. package/dist/vitest/base.mjs.map +1 -0
  21. package/dist/vitest/define-config.d.mts +6 -0
  22. package/dist/vitest/define-config.d.mts.map +1 -0
  23. package/dist/vitest/define-config.mjs +9 -0
  24. package/dist/vitest/define-config.mjs.map +1 -0
  25. package/dist/vitest/msw-browser.d.mts +7 -0
  26. package/dist/vitest/msw-browser.d.mts.map +1 -0
  27. package/dist/vitest/msw-browser.mjs +20 -0
  28. package/dist/vitest/msw-browser.mjs.map +1 -0
  29. package/dist/vitest/msw-http.d.mts +15 -0
  30. package/dist/vitest/msw-http.d.mts.map +1 -0
  31. package/dist/vitest/msw-http.mjs +33 -0
  32. package/dist/vitest/msw-http.mjs.map +1 -0
  33. package/dist/vitest/presets/browser.d.mts +29 -0
  34. package/dist/vitest/presets/browser.d.mts.map +1 -0
  35. package/dist/vitest/presets/browser.mjs +60 -0
  36. package/dist/vitest/presets/browser.mjs.map +1 -0
  37. package/dist/vitest/presets/node.d.mts +15 -0
  38. package/dist/vitest/presets/node.d.mts.map +1 -0
  39. package/dist/vitest/presets/node.mjs +26 -0
  40. package/dist/vitest/presets/node.mjs.map +1 -0
  41. package/package.json +71 -11
  42. package/.turbo/turbo-typecheck.log +0 -2
  43. package/src/lib/deep-merge.ts +0 -71
  44. package/src/lib/tags.ts +0 -3
  45. package/src/lib/viewports.ts +0 -6
  46. package/src/playwright/base.ts +0 -44
  47. package/src/playwright/define-config.ts +0 -13
  48. package/src/vitest/base.ts +0 -50
  49. package/src/vitest/define-config.ts +0 -13
  50. package/src/vitest/msw-browser.ts +0 -18
  51. package/src/vitest/msw-http.ts +0 -60
  52. package/src/vitest/presets/browser.ts +0 -55
  53. package/src/vitest/presets/node.ts +0 -23
  54. package/tsconfig.json +0 -10
  55. package/tsconfig.tsbuildinfo +0 -1
package/package.json CHANGED
@@ -1,26 +1,86 @@
1
1
  {
2
2
  "name": "@interfere/test-utils",
3
- "version": "1.0.0",
3
+ "version": "9.0.0",
4
4
  "license": "MIT",
5
- "publishConfig": {
6
- "access": "public"
7
- },
8
5
  "description": "Shared test utilities used across Interfere packages.",
6
+ "homepage": "https://interfere.com",
7
+ "bugs": {
8
+ "url": "mailto:support@interfere.com"
9
+ },
10
+ "author": "Interfere <support@interfere.com> (https://interfere.com)",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/interfere-inc/interfere.git",
14
+ "directory": "src/packages/public/test-utils"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
9
19
  "type": "module",
10
20
  "exports": {
11
- "./*": "./src/*.ts"
21
+ "./lib/*": {
22
+ "@source": "./src/lib/*.ts",
23
+ "types": "./dist/lib/*.d.mts",
24
+ "default": "./dist/lib/*.mjs"
25
+ },
26
+ "./playwright/*": {
27
+ "@source": "./src/playwright/*.ts",
28
+ "types": "./dist/playwright/*.d.mts",
29
+ "default": "./dist/playwright/*.mjs"
30
+ },
31
+ "./vitest/*": {
32
+ "@source": "./src/vitest/*.ts",
33
+ "types": "./dist/vitest/*.d.mts",
34
+ "default": "./dist/vitest/*.mjs"
35
+ },
36
+ "./vitest/presets/*": {
37
+ "@source": "./src/vitest/presets/*.ts",
38
+ "types": "./dist/vitest/presets/*.d.mts",
39
+ "default": "./dist/vitest/presets/*.mjs"
40
+ },
41
+ "./package.json": "./package.json"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
12
45
  },
13
46
  "scripts": {
47
+ "build": "tsdown",
14
48
  "typecheck": "tsc --noEmit --incremental"
15
49
  },
50
+ "peerDependencies": {
51
+ "@playwright/test": ">=1.59.0",
52
+ "@vitejs/plugin-react": ">=6.0.1",
53
+ "@vitest/browser-playwright": ">=4.1.4",
54
+ "msw": ">=2.13.4",
55
+ "vitest": ">=4.1.5"
56
+ },
57
+ "peerDependenciesMeta": {
58
+ "@playwright/test": {
59
+ "optional": true
60
+ },
61
+ "@vitejs/plugin-react": {
62
+ "optional": true
63
+ },
64
+ "@vitest/browser-playwright": {
65
+ "optional": true
66
+ },
67
+ "msw": {
68
+ "optional": true
69
+ }
70
+ },
71
+ "dependencies": {
72
+ "@interfere/helpers": "^9.0.0",
73
+ "dotenv": "^17.4.2"
74
+ },
16
75
  "devDependencies": {
17
- "@interfere/typescript-config": "^8.1.0",
76
+ "@interfere/typescript-config": "^9.0.0",
18
77
  "@playwright/test": "^1.59.0",
19
78
  "@types/node": "^24.12.0",
20
- "@vitest/browser-playwright": "4.1.2",
21
- "dotenv": "17.3.1",
22
- "msw": "^2.12.14",
23
- "typescript": "6.0.2",
24
- "vitest": "^4.1.2"
79
+ "@vitejs/plugin-react": "^6.0.1",
80
+ "@vitest/browser-playwright": "4.1.5",
81
+ "msw": "^2.13.4",
82
+ "tsdown": "^0.21.10",
83
+ "typescript": "6.0.3",
84
+ "vitest": "^4.1.5"
25
85
  }
26
86
  }
@@ -1,2 +0,0 @@
1
-
2
- $ tsc --noEmit --incremental
@@ -1,71 +0,0 @@
1
- function isPlainObject(value: unknown): value is Record<string, unknown> {
2
- return (
3
- typeof value === "object" &&
4
- value !== null &&
5
- !Array.isArray(value) &&
6
- Object.getPrototypeOf(value) === Object.prototype
7
- );
8
- }
9
-
10
- function cloneValue<T>(value: T): T {
11
- if (Array.isArray(value)) {
12
- return value.map((item) => cloneValue(item)) as T;
13
- }
14
-
15
- if (isPlainObject(value)) {
16
- const out: Record<string, unknown> = {};
17
- for (const [key, nested] of Object.entries(value)) {
18
- out[key] = cloneValue(nested);
19
- }
20
- return out as T;
21
- }
22
-
23
- return value;
24
- }
25
-
26
- function mergeValues(base: unknown, override: unknown): unknown {
27
- if (typeof override === "undefined") {
28
- return cloneValue(base);
29
- }
30
-
31
- if (Array.isArray(base) || Array.isArray(override)) {
32
- const baseArr = Array.isArray(base) ? base : [];
33
- const overrideArr = Array.isArray(override) ? override : [];
34
- return [...baseArr, ...overrideArr].map((item) => cloneValue(item));
35
- }
36
-
37
- if (isPlainObject(base) && isPlainObject(override)) {
38
- const out: Record<string, unknown> = {};
39
- const keys = new Set(Object.keys(base)).union(
40
- new Set(Object.keys(override))
41
- );
42
- for (const key of keys) {
43
- out[key] = mergeValues(base[key], override[key]);
44
- }
45
- return out;
46
- }
47
-
48
- return cloneValue(override);
49
- }
50
-
51
- /**
52
- * Deep-merge any number of config objects.
53
- * - Plain objects merge recursively.
54
- * - Arrays are concatenated (base first, then overrides).
55
- * - Primitives take the rightmost value.
56
- */
57
- export function deepMerge<T extends object>(
58
- base: T,
59
- ...overrides: Array<Record<string, unknown> | undefined>
60
- ): T {
61
- let result = cloneValue(base);
62
-
63
- for (const override of overrides) {
64
- if (override === undefined) {
65
- continue;
66
- }
67
- result = mergeValues(result, override) as T;
68
- }
69
-
70
- return result;
71
- }
package/src/lib/tags.ts DELETED
@@ -1,3 +0,0 @@
1
- export const COMMON = "@common" as const;
2
- export const DESKTOP = "@desktop" as const;
3
- export const MOBILE = "@mobile" as const;
@@ -1,6 +0,0 @@
1
- import type { TestUserConfig } from "vitest/config";
2
-
3
- export type Viewport = NonNullable<TestUserConfig["browser"]>["viewport"];
4
-
5
- export const DESKTOP = { width: 1280, height: 720 } as const satisfies Viewport;
6
- export const MOBILE = { width: 390, height: 844 } as const satisfies Viewport;
@@ -1,44 +0,0 @@
1
- import { devices, type PlaywrightTestConfig } from "@playwright/test";
2
-
3
- import { COMMON, DESKTOP, MOBILE as MOBILE_TAG } from "../lib/tags";
4
- import { MOBILE } from "../lib/viewports";
5
-
6
- const ci = !!process.env["CI"];
7
-
8
- export default {
9
- fullyParallel: true,
10
- forbidOnly: ci,
11
- reporter: ci
12
- ? [["github"], ["junit", { outputFile: "./test-results/e2e-junit.xml" }]]
13
- : [["html"]],
14
-
15
- use: {
16
- trace: "on-first-retry",
17
- screenshot: "only-on-failure",
18
- ...(ci && {
19
- actionTimeout: 10_000,
20
- }),
21
- },
22
-
23
- ...(ci && {
24
- expect: { timeout: 10_000 },
25
- }),
26
-
27
- projects: [
28
- {
29
- name: "desktop",
30
- grep: new RegExp(`${DESKTOP}|${COMMON}`),
31
- use: { ...devices["Desktop Chrome"] },
32
- },
33
- {
34
- name: "mobile",
35
- grep: new RegExp(`${MOBILE_TAG}|${COMMON}`),
36
- use: {
37
- ...devices["Desktop Chrome"],
38
- viewport: MOBILE,
39
- isMobile: true,
40
- hasTouch: true,
41
- },
42
- },
43
- ],
44
- } satisfies PlaywrightTestConfig;
@@ -1,13 +0,0 @@
1
- import {
2
- type PlaywrightTestConfig,
3
- defineConfig as pwDefineConfig,
4
- } from "@playwright/test";
5
-
6
- import { deepMerge } from "../lib/deep-merge";
7
- import base from "./base";
8
-
9
- export function defineConfig(
10
- ...overrides: Array<Partial<PlaywrightTestConfig> | undefined>
11
- ) {
12
- return pwDefineConfig(deepMerge(base, ...overrides));
13
- }
@@ -1,50 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { config } from "dotenv";
3
- import { configDefaults, type ViteUserConfig } from "vitest/config";
4
-
5
- const env =
6
- config({ path: resolve(process.cwd(), ".env.test"), quiet: true }).parsed ??
7
- {};
8
-
9
- export default {
10
- root: process.cwd(),
11
- resolve: {
12
- conditions: ["@source"],
13
- },
14
- ssr: {
15
- resolve: {
16
- conditions: ["@source"],
17
- },
18
- },
19
- test: {
20
- env: {
21
- ...env,
22
- NODE_ENV: "test",
23
- },
24
- environment: "node",
25
- globals: true,
26
- isolate: true,
27
- pool: "forks",
28
- maxWorkers: "50%",
29
- clearMocks: true,
30
- restoreMocks: true,
31
- unstubEnvs: true,
32
- unstubGlobals: true,
33
- sequence: {
34
- shuffle: true,
35
- },
36
- include: [
37
- "src/**/*.{test,spec}.?(c|m)[jt]s?(x)",
38
- "test/**/*.{test,spec}.?(c|m)[jt]s?(x)",
39
- ],
40
- exclude: [...configDefaults.exclude],
41
- reporters: process.env["CI"] ? ["default", "junit"] : ["default"],
42
- ...(process.env["CI"] && {
43
- outputFile: { junit: "./test-results/junit.xml" },
44
- }),
45
- coverage: {
46
- include: ["src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}"],
47
- exclude: [...(configDefaults.coverage?.exclude ?? [])],
48
- },
49
- },
50
- } satisfies ViteUserConfig;
@@ -1,13 +0,0 @@
1
- import {
2
- type ViteUserConfig,
3
- defineConfig as vitestDefineConfig,
4
- } from "vitest/config";
5
-
6
- import { deepMerge } from "../lib/deep-merge.ts";
7
- import base from "./base.ts";
8
-
9
- export function defineConfig(
10
- ...overrides: Array<Partial<ViteUserConfig> | undefined>
11
- ) {
12
- return vitestDefineConfig(deepMerge(base, ...overrides));
13
- }
@@ -1,18 +0,0 @@
1
- import { setupWorker } from "msw/browser";
2
- import { afterAll, afterEach, beforeAll } from "vitest";
3
-
4
- export const worker = setupWorker();
5
-
6
- export function enableMSW(): void {
7
- beforeAll(async () => {
8
- await worker.start({ onUnhandledRequest: "bypass", quiet: true });
9
- });
10
-
11
- afterEach(() => {
12
- worker.resetHandlers();
13
- });
14
-
15
- afterAll(() => {
16
- worker.stop();
17
- });
18
- }
@@ -1,60 +0,0 @@
1
- import { HttpResponse, http } from "msw";
2
-
3
- import { worker } from "./msw-browser.js";
4
-
5
- export type HttpMethod = "post" | "get" | "put" | "delete" | "patch";
6
- export type JsonValue =
7
- | string
8
- | number
9
- | boolean
10
- | null
11
- | JsonValue[]
12
- | { [key: string]: JsonValue };
13
-
14
- export interface CapturedHttpCall {
15
- body: unknown;
16
- params: Record<string, string | readonly string[] | undefined>;
17
- }
18
-
19
- async function parseBody(request: Request): Promise<unknown> {
20
- const clone = request.clone();
21
- const contentType = clone.headers.get("content-type") ?? "";
22
- if (contentType.includes("multipart/form-data")) {
23
- const formData = await clone.formData();
24
- return Object.fromEntries(formData.entries());
25
- }
26
- return clone.json().catch(() => null);
27
- }
28
-
29
- export function createHttpInterceptor(baseUrl: string) {
30
- function intercept(
31
- method: HttpMethod,
32
- path: string,
33
- response: JsonValue,
34
- status = 200
35
- ): CapturedHttpCall[] {
36
- const calls: CapturedHttpCall[] = [];
37
-
38
- worker.use(
39
- http[method](`${baseUrl}${path}`, async ({ request, params }) => {
40
- calls.push({
41
- body: await parseBody(request),
42
- params,
43
- });
44
-
45
- return HttpResponse.json(response, { status });
46
- })
47
- );
48
-
49
- return calls;
50
- }
51
-
52
- function interceptNetworkError(method: HttpMethod, path: string): void {
53
- worker.use(http[method](`${baseUrl}${path}`, () => HttpResponse.error()));
54
- }
55
-
56
- return {
57
- intercept,
58
- interceptNetworkError,
59
- };
60
- }
@@ -1,55 +0,0 @@
1
- import { playwright } from "@vitest/browser-playwright";
2
- import type { TestUserConfig, ViteUserConfig } from "vitest/config";
3
-
4
- import { deepMerge } from "../../lib/deep-merge.ts";
5
- import base from "../base.ts";
6
-
7
- type BrowserInstance = NonNullable<
8
- NonNullable<TestUserConfig["browser"]>["instances"]
9
- >[number];
10
- type InlineProjectConfig = NonNullable<TestUserConfig["projects"]>[number];
11
-
12
- export function browserProject(opts: {
13
- instances: BrowserInstance[];
14
- name?: string;
15
- setupFiles?: string[];
16
- plugins?: ViteUserConfig["plugins"];
17
- include?: string[];
18
- exclude?: string[];
19
- trace?: boolean;
20
- optimizeDeps?: ViteUserConfig["optimizeDeps"];
21
- define?: ViteUserConfig["define"];
22
- resolve?: ViteUserConfig["resolve"];
23
- }): InlineProjectConfig {
24
- return deepMerge(
25
- {
26
- ...(opts.plugins && { plugins: opts.plugins }),
27
- ...(opts.define && { define: opts.define }),
28
- ...(opts.resolve && { resolve: opts.resolve }),
29
- ...(opts.optimizeDeps && { optimizeDeps: opts.optimizeDeps }),
30
- server: { port: 0, strictPort: false },
31
- },
32
- {
33
- test: {
34
- ...base.test,
35
- name: opts.name ?? "browser",
36
- ...(opts.setupFiles && { setupFiles: opts.setupFiles }),
37
- ...(opts.include && { include: opts.include }),
38
- ...(opts.exclude && { exclude: opts.exclude }),
39
- browser: {
40
- enabled: true,
41
- provider: playwright(),
42
- screenshotFailures: false,
43
- instances: opts.instances,
44
- ...(opts.trace && {
45
- trace: {
46
- mode: "retain-on-failure" as const,
47
- },
48
- }),
49
- },
50
- },
51
- }
52
- ) as InlineProjectConfig;
53
- }
54
-
55
- export type { BrowserInstance };
@@ -1,23 +0,0 @@
1
- import type { TestUserConfig } from "vitest/config";
2
-
3
- import base from "../base.ts";
4
-
5
- type InlineProjectConfig = NonNullable<TestUserConfig["projects"]>[number];
6
-
7
- export function nodeProject(opts?: {
8
- name?: string;
9
- include?: string[];
10
- exclude?: string[];
11
- setupFiles?: string[];
12
- }): InlineProjectConfig {
13
- return {
14
- test: {
15
- ...base.test,
16
- name: opts?.name ?? "node",
17
- environment: "node",
18
- ...(opts?.include && { include: opts.include }),
19
- ...(opts?.exclude && { exclude: opts.exclude }),
20
- ...(opts?.setupFiles && { setupFiles: opts.setupFiles }),
21
- },
22
- };
23
- }
package/tsconfig.json DELETED
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "@interfere/typescript-config/base.json",
3
- "compilerOptions": {
4
- "allowImportingTsExtensions": true,
5
- "outDir": "./dist",
6
- "rootDir": "./src",
7
- "types": ["node"]
8
- },
9
- "include": ["src"]
10
- }