@pokujs/dom 0.0.1 → 1.0.1
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/README.md +150 -0
- package/dist/index.d.ts +149 -0
- package/dist/index.js +587 -0
- package/dist/index.js.map +1 -0
- package/package.json +83 -3
- package/index.js +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lojhan
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img height="180" alt="Poku Logo" src="https://raw.githubusercontent.com/wellwelwel/poku/main/.github/assets/readme/poku.svg">
|
|
3
|
+
|
|
4
|
+
# @pokujs/dom
|
|
5
|
+
|
|
6
|
+
Shared DOM testing core for Poku framework adapters.
|
|
7
|
+
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
@pokujs/dom centralizes cross-framework testing infrastructure used by adapter libraries such as @pokujs/react and @pokujs/vue.
|
|
13
|
+
|
|
14
|
+
Use this package when building or maintaining framework adapters that need:
|
|
15
|
+
|
|
16
|
+
- Runtime command injection for Node, Bun, and Deno.
|
|
17
|
+
- In-process test environment setup.
|
|
18
|
+
- Metrics normalization and reporting.
|
|
19
|
+
- happy-dom/jsdom environment bootstrap.
|
|
20
|
+
- Shared testing runtime primitives.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm i @pokujs/dom
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
### 1) Build runtime option prefixes and parse flags
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import {
|
|
34
|
+
createRuntimeOptionArgPrefixes,
|
|
35
|
+
parseRuntimeOptions,
|
|
36
|
+
} from '@pokujs/dom';
|
|
37
|
+
|
|
38
|
+
const prefixes = createRuntimeOptionArgPrefixes('poku-react');
|
|
39
|
+
const runtimeOptions = parseRuntimeOptions(prefixes);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2) Create a framework plugin factory
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import {
|
|
46
|
+
createFrameworkTestingPluginFactory,
|
|
47
|
+
type FrameworkDescriptor,
|
|
48
|
+
} from '@pokujs/dom';
|
|
49
|
+
|
|
50
|
+
const descriptor: FrameworkDescriptor = {
|
|
51
|
+
pluginName: 'react-testing',
|
|
52
|
+
packageTag: '@pokujs/react',
|
|
53
|
+
runtimeArgBase: 'poku-react',
|
|
54
|
+
metricMessageType: 'POKU_REACT_RENDER_METRIC',
|
|
55
|
+
metricBatchMessageType: 'POKU_REACT_RENDER_METRIC_BATCH',
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const { createTestingPlugin } = createFrameworkTestingPluginFactory(
|
|
59
|
+
descriptor,
|
|
60
|
+
import.meta.url
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
export const createReactTestingPlugin = createTestingPlugin;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 3) Reuse DOM setup helpers
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { setupHappyDomEnvironment, setupJsdomEnvironment } from '@pokujs/dom';
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 4) Reuse shared testing runtime helpers
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import {
|
|
76
|
+
createRenderMetricsEmitter,
|
|
77
|
+
createScreen,
|
|
78
|
+
getNow,
|
|
79
|
+
} from '@pokujs/dom';
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Contracts
|
|
83
|
+
|
|
84
|
+
Adapter packages should follow these contracts.
|
|
85
|
+
|
|
86
|
+
### FrameworkDescriptor contract
|
|
87
|
+
|
|
88
|
+
- pluginName: Plugin registration name.
|
|
89
|
+
- packageTag: Human-readable package tag for messages.
|
|
90
|
+
- runtimeArgBase: Prefix base used to generate runtime CLI flags.
|
|
91
|
+
- metricMessageType: IPC type for single render metrics.
|
|
92
|
+
- metricBatchMessageType: IPC type for batch render metrics.
|
|
93
|
+
- testFileExtensions (optional): Test file extensions the runner should intercept.
|
|
94
|
+
|
|
95
|
+
### Runtime options contract
|
|
96
|
+
|
|
97
|
+
parseRuntimeOptions returns:
|
|
98
|
+
|
|
99
|
+
- domUrl: DOM URL used by happy-dom/jsdom setup.
|
|
100
|
+
- metricsEnabled: Whether metrics collection is active.
|
|
101
|
+
- minMetricMs: Drop metrics below this threshold.
|
|
102
|
+
- metricBatchSize: Batch size before immediate flush.
|
|
103
|
+
- metricFlushMs: Flush interval while buffering.
|
|
104
|
+
|
|
105
|
+
### Metrics contract
|
|
106
|
+
|
|
107
|
+
createMetricsSummary returns either null or:
|
|
108
|
+
|
|
109
|
+
- totalCaptured: Number of collected metrics.
|
|
110
|
+
- totalReported: Number of top metrics reported.
|
|
111
|
+
- topSlowest: Sorted metrics by duration descending.
|
|
112
|
+
|
|
113
|
+
### Testing runtime contract
|
|
114
|
+
|
|
115
|
+
createRenderMetricsEmitter provides:
|
|
116
|
+
|
|
117
|
+
- emitRenderMetric(componentName, durationMs)
|
|
118
|
+
- flushMetricBuffer()
|
|
119
|
+
- clearMetricFlushTimer()
|
|
120
|
+
|
|
121
|
+
These APIs are framework-agnostic. Adapter packages remain responsible for framework-specific semantics such as act, nextTick, render lifecycle, and hook/composable harness behavior.
|
|
122
|
+
|
|
123
|
+
## Release and CI/CD
|
|
124
|
+
|
|
125
|
+
This package includes:
|
|
126
|
+
|
|
127
|
+
- CI build and lint workflows.
|
|
128
|
+
- Node, Bun, and Deno compatibility workflows.
|
|
129
|
+
- CodeQL workflow and config.
|
|
130
|
+
- Release Please + npm publish workflow.
|
|
131
|
+
|
|
132
|
+
## Development
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npm ci
|
|
136
|
+
npm run check
|
|
137
|
+
npm run build
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Testing
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npm test
|
|
144
|
+
npm run test:bun
|
|
145
|
+
npm run test:deno
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { Screen } from '@testing-library/dom';
|
|
2
|
+
|
|
3
|
+
type DomAdapter = 'happy-dom' | 'jsdom' | {
|
|
4
|
+
setupModule: string;
|
|
5
|
+
};
|
|
6
|
+
type RenderMetric = {
|
|
7
|
+
file: string;
|
|
8
|
+
componentName: string;
|
|
9
|
+
durationMs: number;
|
|
10
|
+
};
|
|
11
|
+
type MetricsSummary = {
|
|
12
|
+
totalCaptured: number;
|
|
13
|
+
totalReported: number;
|
|
14
|
+
topSlowest: RenderMetric[];
|
|
15
|
+
};
|
|
16
|
+
type MetricsOptions = {
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
topN?: number;
|
|
19
|
+
minDurationMs?: number;
|
|
20
|
+
reporter?: (summary: MetricsSummary) => void;
|
|
21
|
+
};
|
|
22
|
+
type TestingPluginOptions = {
|
|
23
|
+
dom?: DomAdapter;
|
|
24
|
+
domUrl?: string;
|
|
25
|
+
metrics?: boolean | MetricsOptions;
|
|
26
|
+
};
|
|
27
|
+
type NormalizedMetricsOptions = {
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
topN: number;
|
|
30
|
+
minDurationMs: number;
|
|
31
|
+
reporter?: (summary: MetricsSummary) => void;
|
|
32
|
+
};
|
|
33
|
+
type RuntimeOptionArgPrefixes = {
|
|
34
|
+
metrics: string;
|
|
35
|
+
minMetricMs: string;
|
|
36
|
+
domUrl: string;
|
|
37
|
+
metricBatchSize: string;
|
|
38
|
+
metricFlushMs: string;
|
|
39
|
+
};
|
|
40
|
+
type RuntimeOptions = {
|
|
41
|
+
domUrl: string;
|
|
42
|
+
metricsEnabled: boolean;
|
|
43
|
+
minMetricMs: number;
|
|
44
|
+
metricBatchSize: number;
|
|
45
|
+
metricFlushMs: number;
|
|
46
|
+
};
|
|
47
|
+
type FrameworkDescriptor = {
|
|
48
|
+
pluginName: string;
|
|
49
|
+
packageTag: string;
|
|
50
|
+
runtimeArgBase: string;
|
|
51
|
+
metricMessageType: string;
|
|
52
|
+
metricBatchMessageType: string;
|
|
53
|
+
testFileExtensions?: string[];
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
declare const createRuntimeOptionArgPrefixes: (base: string) => RuntimeOptionArgPrefixes;
|
|
57
|
+
declare const parseRuntimeOptions: (prefixes: RuntimeOptionArgPrefixes, argv?: string[]) => RuntimeOptions;
|
|
58
|
+
|
|
59
|
+
type RuntimeSupport = {
|
|
60
|
+
supportsNodeLikeImport: boolean;
|
|
61
|
+
supportsDenoPreload: boolean;
|
|
62
|
+
};
|
|
63
|
+
type BuildRunnerCommandInput = {
|
|
64
|
+
runtime: string;
|
|
65
|
+
command: string[];
|
|
66
|
+
file: string;
|
|
67
|
+
domSetupPath: string;
|
|
68
|
+
runtimeOptionArgs: string[];
|
|
69
|
+
extensions: Set<string>;
|
|
70
|
+
};
|
|
71
|
+
type BuildRunnerCommandOutput = {
|
|
72
|
+
shouldHandle: boolean;
|
|
73
|
+
command: string[];
|
|
74
|
+
};
|
|
75
|
+
declare const isNodeRuntime: (runtime: string) => runtime is "node";
|
|
76
|
+
declare const getRuntimeSupport: (runtime: string) => RuntimeSupport;
|
|
77
|
+
declare const canHandleRuntime: (runtime: string) => boolean;
|
|
78
|
+
declare const createDomSetupPathResolver: (packageTag: string, happyDomSetupPath: string, jsdomSetupPath: string) => (adapter: DomAdapter | undefined) => string;
|
|
79
|
+
declare const buildRunnerCommand: ({ runtime, command, file, domSetupPath, runtimeOptionArgs, extensions, }: BuildRunnerCommandInput) => BuildRunnerCommandOutput;
|
|
80
|
+
|
|
81
|
+
type InProcessSetupOptions = {
|
|
82
|
+
isolation: string | undefined;
|
|
83
|
+
runtime: string;
|
|
84
|
+
runtimeOptionArgs: string[];
|
|
85
|
+
domSetupPath: string;
|
|
86
|
+
packageTag: string;
|
|
87
|
+
};
|
|
88
|
+
declare const setupInProcessEnvironment: (options: InProcessSetupOptions) => Promise<(() => void) | undefined>;
|
|
89
|
+
|
|
90
|
+
declare const isRenderMetricMessage: (message: unknown, metricMessageType: string) => message is {
|
|
91
|
+
type: string;
|
|
92
|
+
componentName?: string;
|
|
93
|
+
durationMs?: number;
|
|
94
|
+
};
|
|
95
|
+
declare const isRenderMetricBatchMessage: (message: unknown, metricBatchMessageType: string) => message is {
|
|
96
|
+
type: string;
|
|
97
|
+
metrics: Array<{
|
|
98
|
+
componentName?: string;
|
|
99
|
+
durationMs?: number;
|
|
100
|
+
}>;
|
|
101
|
+
};
|
|
102
|
+
declare const getComponentName: (componentName: unknown) => string;
|
|
103
|
+
declare const buildRuntimeOptionArgs: (options: TestingPluginOptions, metricsOptions: NormalizedMetricsOptions, prefixes: RuntimeOptionArgPrefixes) => string[];
|
|
104
|
+
declare const normalizeMetricsOptions: (metrics: boolean | MetricsOptions | undefined) => NormalizedMetricsOptions;
|
|
105
|
+
declare const selectTopSlowestMetrics: (metrics: RenderMetric[], options: NormalizedMetricsOptions) => RenderMetric[];
|
|
106
|
+
declare const createMetricsSummary: (metrics: RenderMetric[], options: NormalizedMetricsOptions) => MetricsSummary | null;
|
|
107
|
+
declare const printMetricsSummary: (summary: MetricsSummary, packageTag: string) => void;
|
|
108
|
+
|
|
109
|
+
type FrameworkPluginInternals = {
|
|
110
|
+
buildRunnerCommand: typeof buildRunnerCommand;
|
|
111
|
+
canHandleRuntime: typeof canHandleRuntime;
|
|
112
|
+
buildRuntimeOptionArgs: typeof buildRuntimeOptionArgs;
|
|
113
|
+
normalizeMetricsOptions: typeof normalizeMetricsOptions;
|
|
114
|
+
selectTopSlowestMetrics: typeof selectTopSlowestMetrics;
|
|
115
|
+
createMetricsSummary: typeof createMetricsSummary;
|
|
116
|
+
getComponentName: typeof getComponentName;
|
|
117
|
+
isRenderMetricMessage: typeof isRenderMetricMessage;
|
|
118
|
+
isRenderMetricBatchMessage: typeof isRenderMetricBatchMessage;
|
|
119
|
+
resolveDomSetupPath: (adapter: DomAdapter | undefined) => string;
|
|
120
|
+
};
|
|
121
|
+
declare const createFrameworkTestingPluginFactory: (descriptor: FrameworkDescriptor, moduleUrl: string) => {
|
|
122
|
+
createTestingPlugin: (options?: TestingPluginOptions) => any;
|
|
123
|
+
resolveDomSetupPath: (adapter: DomAdapter | undefined) => string;
|
|
124
|
+
runtimeOptionArgPrefixes: RuntimeOptionArgPrefixes;
|
|
125
|
+
internals: FrameworkPluginInternals;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
type SetupDomEnvironmentOptions = {
|
|
129
|
+
runtimeOptions: RuntimeOptions;
|
|
130
|
+
packageTag: string;
|
|
131
|
+
enableReactActEnvironment?: boolean;
|
|
132
|
+
};
|
|
133
|
+
declare const setupHappyDomEnvironment: (options: SetupDomEnvironmentOptions) => Promise<void>;
|
|
134
|
+
declare const setupJsdomEnvironment: (options: SetupDomEnvironmentOptions) => Promise<void>;
|
|
135
|
+
|
|
136
|
+
declare const getNow: () => number;
|
|
137
|
+
type RenderMetricsEmitterOptions = {
|
|
138
|
+
runtimeOptions: RuntimeOptions;
|
|
139
|
+
metricsStateKey: symbol;
|
|
140
|
+
metricsBatchMessageType: string;
|
|
141
|
+
};
|
|
142
|
+
declare const createRenderMetricsEmitter: (options: RenderMetricsEmitterOptions) => {
|
|
143
|
+
emitRenderMetric: (componentName: string, durationMs: number) => void;
|
|
144
|
+
flushMetricBuffer: () => void;
|
|
145
|
+
clearMetricFlushTimer: () => void;
|
|
146
|
+
};
|
|
147
|
+
declare const createScreen: () => Screen;
|
|
148
|
+
|
|
149
|
+
export { type BuildRunnerCommandInput, type BuildRunnerCommandOutput, type DomAdapter, type FrameworkDescriptor, type FrameworkPluginInternals, type InProcessSetupOptions, type MetricsOptions, type MetricsSummary, type NormalizedMetricsOptions, type RenderMetric, type RenderMetricsEmitterOptions, type RuntimeOptionArgPrefixes, type RuntimeOptions, type RuntimeSupport, type TestingPluginOptions, buildRunnerCommand, buildRuntimeOptionArgs, canHandleRuntime, createDomSetupPathResolver, createFrameworkTestingPluginFactory, createMetricsSummary, createRenderMetricsEmitter, createRuntimeOptionArgPrefixes, createScreen, getComponentName, getNow, getRuntimeSupport, isNodeRuntime, isRenderMetricBatchMessage, isRenderMetricMessage, normalizeMetricsOptions, parseRuntimeOptions, printMetricsSummary, selectTopSlowestMetrics, setupHappyDomEnvironment, setupInProcessEnvironment, setupJsdomEnvironment };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
// src/runtime-options.ts
|
|
2
|
+
var DEFAULT_DOM_URL = "http://localhost:3000/";
|
|
3
|
+
var DEFAULT_METRIC_BATCH_SIZE = 50;
|
|
4
|
+
var DEFAULT_METRIC_FLUSH_MS = 50;
|
|
5
|
+
var toNumber = (value) => {
|
|
6
|
+
if (!value) return NaN;
|
|
7
|
+
const parsed = Number(value);
|
|
8
|
+
return Number.isFinite(parsed) ? parsed : NaN;
|
|
9
|
+
};
|
|
10
|
+
var findValueByPrefix = (argv, prefix) => {
|
|
11
|
+
const match = argv.find((arg) => arg.startsWith(prefix));
|
|
12
|
+
return match ? match.slice(prefix.length) : void 0;
|
|
13
|
+
};
|
|
14
|
+
var parsePositiveInteger = (value, fallback) => {
|
|
15
|
+
const numeric = toNumber(value);
|
|
16
|
+
if (!Number.isFinite(numeric) || numeric <= 0) return fallback;
|
|
17
|
+
return Math.floor(numeric);
|
|
18
|
+
};
|
|
19
|
+
var parseNonNegativeNumber = (value, fallback) => {
|
|
20
|
+
const numeric = toNumber(value);
|
|
21
|
+
if (!Number.isFinite(numeric) || numeric < 0) return fallback;
|
|
22
|
+
return numeric;
|
|
23
|
+
};
|
|
24
|
+
var createRuntimeOptionArgPrefixes = (base) => ({
|
|
25
|
+
metrics: `--${base}-metrics=`,
|
|
26
|
+
minMetricMs: `--${base}-min-metric-ms=`,
|
|
27
|
+
domUrl: `--${base}-dom-url=`,
|
|
28
|
+
metricBatchSize: `--${base}-metric-batch-size=`,
|
|
29
|
+
metricFlushMs: `--${base}-metric-flush-ms=`
|
|
30
|
+
});
|
|
31
|
+
var parseRuntimeOptions = (prefixes, argv = process.argv) => {
|
|
32
|
+
const metricsEnabled = findValueByPrefix(argv, prefixes.metrics) === "1" || findValueByPrefix(argv, prefixes.metrics) === "true";
|
|
33
|
+
const domUrl = findValueByPrefix(argv, prefixes.domUrl)?.trim() || DEFAULT_DOM_URL;
|
|
34
|
+
const minMetricMs = parseNonNegativeNumber(
|
|
35
|
+
findValueByPrefix(argv, prefixes.minMetricMs),
|
|
36
|
+
0
|
|
37
|
+
);
|
|
38
|
+
const metricBatchSize = parsePositiveInteger(
|
|
39
|
+
findValueByPrefix(argv, prefixes.metricBatchSize),
|
|
40
|
+
DEFAULT_METRIC_BATCH_SIZE
|
|
41
|
+
);
|
|
42
|
+
const metricFlushMs = parsePositiveInteger(
|
|
43
|
+
findValueByPrefix(argv, prefixes.metricFlushMs),
|
|
44
|
+
DEFAULT_METRIC_FLUSH_MS
|
|
45
|
+
);
|
|
46
|
+
return {
|
|
47
|
+
domUrl,
|
|
48
|
+
metricsEnabled,
|
|
49
|
+
minMetricMs,
|
|
50
|
+
metricBatchSize,
|
|
51
|
+
metricFlushMs
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// src/plugin-command.ts
|
|
56
|
+
import { existsSync } from "fs";
|
|
57
|
+
import { extname, resolve } from "path";
|
|
58
|
+
var isTsxImport = (arg) => arg === "--import=tsx" || arg === "--loader=tsx";
|
|
59
|
+
var isNodeRuntime = (runtime) => runtime === "node";
|
|
60
|
+
var isBunRuntime = (runtime) => runtime === "bun";
|
|
61
|
+
var isDenoRuntime = (runtime) => runtime === "deno";
|
|
62
|
+
var getRuntimeSupport = (runtime) => ({
|
|
63
|
+
supportsNodeLikeImport: isNodeRuntime(runtime) || isBunRuntime(runtime),
|
|
64
|
+
supportsDenoPreload: isDenoRuntime(runtime)
|
|
65
|
+
});
|
|
66
|
+
var canHandleRuntime = (runtime) => {
|
|
67
|
+
const support = getRuntimeSupport(runtime);
|
|
68
|
+
return support.supportsNodeLikeImport || support.supportsDenoPreload;
|
|
69
|
+
};
|
|
70
|
+
var createDomSetupPathResolver = (packageTag, happyDomSetupPath, jsdomSetupPath) => {
|
|
71
|
+
return (adapter) => {
|
|
72
|
+
if (!adapter || adapter === "happy-dom") return happyDomSetupPath;
|
|
73
|
+
if (adapter === "jsdom") return jsdomSetupPath;
|
|
74
|
+
const customPath = resolve(process.cwd(), adapter.setupModule);
|
|
75
|
+
if (!existsSync(customPath)) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`[${packageTag}] Custom DOM setup module not found: "${customPath}"
|
|
78
|
+
Check the "dom.setupModule" option in your poku.config.js.`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
return customPath;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
var buildRunnerCommand = ({
|
|
85
|
+
runtime,
|
|
86
|
+
command,
|
|
87
|
+
file,
|
|
88
|
+
domSetupPath,
|
|
89
|
+
runtimeOptionArgs,
|
|
90
|
+
extensions
|
|
91
|
+
}) => {
|
|
92
|
+
const support = getRuntimeSupport(runtime);
|
|
93
|
+
if (!support.supportsNodeLikeImport && !support.supportsDenoPreload) {
|
|
94
|
+
return { shouldHandle: false, command };
|
|
95
|
+
}
|
|
96
|
+
if (!extensions.has(extname(file))) {
|
|
97
|
+
return { shouldHandle: false, command };
|
|
98
|
+
}
|
|
99
|
+
const fileIndex = command.lastIndexOf(file);
|
|
100
|
+
if (fileIndex === -1) return { shouldHandle: false, command };
|
|
101
|
+
const nodeImportFlag = `--import=${domSetupPath}`;
|
|
102
|
+
const denoPreloadFlag = `--preload=${domSetupPath}`;
|
|
103
|
+
const beforeFile = [];
|
|
104
|
+
const afterFile = [];
|
|
105
|
+
let hasTsx = false;
|
|
106
|
+
let hasNodeLikeDomSetup = false;
|
|
107
|
+
let hasDenoDomSetup = false;
|
|
108
|
+
const existingArgs = /* @__PURE__ */ new Set();
|
|
109
|
+
for (let index = 1; index < command.length; index += 1) {
|
|
110
|
+
const arg = command[index];
|
|
111
|
+
if (typeof arg !== "string") continue;
|
|
112
|
+
existingArgs.add(arg);
|
|
113
|
+
if (index < fileIndex) {
|
|
114
|
+
beforeFile.push(arg);
|
|
115
|
+
if (isTsxImport(arg)) hasTsx = true;
|
|
116
|
+
else if (arg === nodeImportFlag) hasNodeLikeDomSetup = true;
|
|
117
|
+
else if (arg === denoPreloadFlag) hasDenoDomSetup = true;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (index > fileIndex) {
|
|
121
|
+
afterFile.push(arg);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const extraImports = [];
|
|
125
|
+
if (isNodeRuntime(runtime) && !hasTsx) extraImports.push("--import=tsx");
|
|
126
|
+
if (support.supportsNodeLikeImport && !hasNodeLikeDomSetup) {
|
|
127
|
+
extraImports.push(nodeImportFlag);
|
|
128
|
+
}
|
|
129
|
+
if (support.supportsDenoPreload && !hasDenoDomSetup) {
|
|
130
|
+
extraImports.push(denoPreloadFlag);
|
|
131
|
+
}
|
|
132
|
+
const runtimeArgsToInject = [];
|
|
133
|
+
for (const runtimeOptionArg of runtimeOptionArgs) {
|
|
134
|
+
if (existingArgs.has(runtimeOptionArg)) continue;
|
|
135
|
+
runtimeArgsToInject.push(runtimeOptionArg);
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
shouldHandle: true,
|
|
139
|
+
command: [
|
|
140
|
+
runtime,
|
|
141
|
+
...beforeFile,
|
|
142
|
+
...extraImports,
|
|
143
|
+
file,
|
|
144
|
+
...runtimeArgsToInject,
|
|
145
|
+
...afterFile
|
|
146
|
+
]
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// src/plugin-setup.ts
|
|
151
|
+
import { pathToFileURL } from "url";
|
|
152
|
+
import { createRequire } from "module";
|
|
153
|
+
var TSX_LOADER_MODULE = "tsx/esm/api";
|
|
154
|
+
var appendMissingRuntimeArgs = (runtimeOptionArgs) => {
|
|
155
|
+
for (const arg of runtimeOptionArgs) {
|
|
156
|
+
if (process.argv.includes(arg)) continue;
|
|
157
|
+
process.argv.push(arg);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
var loadDomSetupModule = async (domSetupPath) => {
|
|
161
|
+
await import(pathToFileURL(domSetupPath).href);
|
|
162
|
+
};
|
|
163
|
+
var registerNodeTsxLoader = async (packageTag) => {
|
|
164
|
+
const requireFromCwd = createRequire(`${process.cwd()}/`);
|
|
165
|
+
try {
|
|
166
|
+
const resolvedModulePath = requireFromCwd.resolve(TSX_LOADER_MODULE);
|
|
167
|
+
const mod = await import(pathToFileURL(resolvedModulePath).href);
|
|
168
|
+
if (typeof mod.register !== "function") {
|
|
169
|
+
throw new Error("Missing register() export from tsx loader API");
|
|
170
|
+
}
|
|
171
|
+
return mod.register();
|
|
172
|
+
} catch (error) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
`[${packageTag}] isolation "none" in Node.js requires a working "tsx" installation to load .tsx/.jsx test files.`,
|
|
175
|
+
{ cause: error }
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
var setupInProcessEnvironment = async (options) => {
|
|
180
|
+
if (options.isolation !== "none") return void 0;
|
|
181
|
+
if (!canHandleRuntime(options.runtime)) return void 0;
|
|
182
|
+
let cleanupNodeTsxLoader;
|
|
183
|
+
if (isNodeRuntime(options.runtime)) {
|
|
184
|
+
cleanupNodeTsxLoader = await registerNodeTsxLoader(options.packageTag);
|
|
185
|
+
}
|
|
186
|
+
appendMissingRuntimeArgs(options.runtimeOptionArgs);
|
|
187
|
+
await loadDomSetupModule(options.domSetupPath);
|
|
188
|
+
return cleanupNodeTsxLoader;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// src/plugin-metrics.ts
|
|
192
|
+
var DEFAULT_TOP_N = 5;
|
|
193
|
+
var DEFAULT_MIN_DURATION_MS = 0;
|
|
194
|
+
var isRenderMetricMessage = (message, metricMessageType) => {
|
|
195
|
+
if (!message || typeof message !== "object") return false;
|
|
196
|
+
return message.type === metricMessageType;
|
|
197
|
+
};
|
|
198
|
+
var isRenderMetricBatchMessage = (message, metricBatchMessageType) => {
|
|
199
|
+
if (!message || typeof message !== "object") return false;
|
|
200
|
+
const record = message;
|
|
201
|
+
return record.type === metricBatchMessageType && Array.isArray(record.metrics);
|
|
202
|
+
};
|
|
203
|
+
var getComponentName = (componentName) => typeof componentName === "string" && componentName.length > 0 ? componentName : "AnonymousComponent";
|
|
204
|
+
var getPositiveIntegerOrDefault = (value, fallback) => {
|
|
205
|
+
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim().length > 0 ? Number(value.trim()) : NaN;
|
|
206
|
+
if (!Number.isFinite(numeric) || numeric <= 0) return fallback;
|
|
207
|
+
return Math.floor(numeric);
|
|
208
|
+
};
|
|
209
|
+
var getNonNegativeNumberOrDefault = (value, fallback) => {
|
|
210
|
+
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim().length > 0 ? Number(value.trim()) : NaN;
|
|
211
|
+
if (!Number.isFinite(numeric) || numeric < 0) return fallback;
|
|
212
|
+
return numeric;
|
|
213
|
+
};
|
|
214
|
+
var buildRuntimeOptionArgs = (options, metricsOptions, prefixes) => {
|
|
215
|
+
const args = [];
|
|
216
|
+
if (options.domUrl) {
|
|
217
|
+
args.push(`${prefixes.domUrl}${options.domUrl}`);
|
|
218
|
+
}
|
|
219
|
+
if (metricsOptions.enabled) {
|
|
220
|
+
args.push(`${prefixes.metrics}1`);
|
|
221
|
+
args.push(`${prefixes.minMetricMs}${metricsOptions.minDurationMs}`);
|
|
222
|
+
}
|
|
223
|
+
return args;
|
|
224
|
+
};
|
|
225
|
+
var normalizeMetricsOptions = (metrics) => {
|
|
226
|
+
if (metrics === true) {
|
|
227
|
+
return {
|
|
228
|
+
enabled: true,
|
|
229
|
+
topN: DEFAULT_TOP_N,
|
|
230
|
+
minDurationMs: DEFAULT_MIN_DURATION_MS
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
if (!metrics) {
|
|
234
|
+
return {
|
|
235
|
+
enabled: false,
|
|
236
|
+
topN: DEFAULT_TOP_N,
|
|
237
|
+
minDurationMs: DEFAULT_MIN_DURATION_MS
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const normalized = {
|
|
241
|
+
enabled: metrics.enabled ?? true,
|
|
242
|
+
topN: getPositiveIntegerOrDefault(metrics.topN, DEFAULT_TOP_N),
|
|
243
|
+
minDurationMs: getNonNegativeNumberOrDefault(
|
|
244
|
+
metrics.minDurationMs,
|
|
245
|
+
DEFAULT_MIN_DURATION_MS
|
|
246
|
+
)
|
|
247
|
+
};
|
|
248
|
+
if (metrics.reporter) normalized.reporter = metrics.reporter;
|
|
249
|
+
return normalized;
|
|
250
|
+
};
|
|
251
|
+
var selectTopSlowestMetrics = (metrics, options) => [...metrics].sort((a, b) => b.durationMs - a.durationMs).slice(0, options.topN);
|
|
252
|
+
var createMetricsSummary = (metrics, options) => {
|
|
253
|
+
if (!options.enabled || metrics.length === 0) return null;
|
|
254
|
+
const topSlowest = selectTopSlowestMetrics(metrics, options);
|
|
255
|
+
if (topSlowest.length === 0) return null;
|
|
256
|
+
return {
|
|
257
|
+
totalCaptured: metrics.length,
|
|
258
|
+
totalReported: topSlowest.length,
|
|
259
|
+
topSlowest
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
var printMetricsSummary = (summary, packageTag) => {
|
|
263
|
+
const lines = summary.topSlowest.map(
|
|
264
|
+
(metric) => ` - ${metric.componentName} in ${metric.file}: ${metric.durationMs.toFixed(2)}ms`
|
|
265
|
+
);
|
|
266
|
+
console.log(`
|
|
267
|
+
[${packageTag}] Slowest component renders`);
|
|
268
|
+
for (const line of lines) console.log(line);
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// src/plugin-factory.ts
|
|
272
|
+
import { definePlugin } from "poku/plugins";
|
|
273
|
+
import { dirname, resolve as resolve2 } from "path";
|
|
274
|
+
import { fileURLToPath } from "url";
|
|
275
|
+
import { existsSync as existsSync2 } from "fs";
|
|
276
|
+
var createFrameworkTestingPluginFactory = (descriptor, moduleUrl) => {
|
|
277
|
+
const currentDir = dirname(fileURLToPath(moduleUrl));
|
|
278
|
+
const resolveSetupModulePath = (baseName) => {
|
|
279
|
+
const jsPath = resolve2(currentDir, `${baseName}.js`);
|
|
280
|
+
if (existsSync2(jsPath)) return jsPath;
|
|
281
|
+
return resolve2(currentDir, `${baseName}.ts`);
|
|
282
|
+
};
|
|
283
|
+
const happyDomSetupPath = resolveSetupModulePath("dom-setup-happy");
|
|
284
|
+
const jsdomSetupPath = resolveSetupModulePath("dom-setup-jsdom");
|
|
285
|
+
const resolveDomSetupPath = createDomSetupPathResolver(
|
|
286
|
+
descriptor.packageTag,
|
|
287
|
+
happyDomSetupPath,
|
|
288
|
+
jsdomSetupPath
|
|
289
|
+
);
|
|
290
|
+
const prefixes = createRuntimeOptionArgPrefixes(descriptor.runtimeArgBase);
|
|
291
|
+
const extensions = new Set(descriptor.testFileExtensions ?? [".tsx", ".jsx"]);
|
|
292
|
+
const createTestingPlugin = (options = {}) => {
|
|
293
|
+
let metrics = [];
|
|
294
|
+
let cleanupNodeTsxLoader;
|
|
295
|
+
const domSetupPath = resolveDomSetupPath(options.dom);
|
|
296
|
+
const metricsOptions = normalizeMetricsOptions(options.metrics);
|
|
297
|
+
const runtimeOptionArgs = buildRuntimeOptionArgs(
|
|
298
|
+
options,
|
|
299
|
+
metricsOptions,
|
|
300
|
+
prefixes
|
|
301
|
+
);
|
|
302
|
+
return definePlugin({
|
|
303
|
+
name: descriptor.pluginName,
|
|
304
|
+
ipc: metricsOptions.enabled,
|
|
305
|
+
async setup(context) {
|
|
306
|
+
cleanupNodeTsxLoader = await setupInProcessEnvironment({
|
|
307
|
+
isolation: context.configs.isolation,
|
|
308
|
+
runtime: context.runtime,
|
|
309
|
+
runtimeOptionArgs,
|
|
310
|
+
domSetupPath,
|
|
311
|
+
packageTag: descriptor.packageTag
|
|
312
|
+
});
|
|
313
|
+
},
|
|
314
|
+
runner(command, file) {
|
|
315
|
+
const runtime = command[0];
|
|
316
|
+
if (!runtime) return command;
|
|
317
|
+
const result = buildRunnerCommand({
|
|
318
|
+
runtime,
|
|
319
|
+
command,
|
|
320
|
+
file,
|
|
321
|
+
domSetupPath,
|
|
322
|
+
runtimeOptionArgs,
|
|
323
|
+
extensions
|
|
324
|
+
});
|
|
325
|
+
if (!result.shouldHandle) return command;
|
|
326
|
+
return result.command;
|
|
327
|
+
},
|
|
328
|
+
onTestProcess(child, file) {
|
|
329
|
+
if (!metricsOptions.enabled) return;
|
|
330
|
+
const maybePruneMetrics = () => {
|
|
331
|
+
if (metrics.length > metricsOptions.topN * 10) {
|
|
332
|
+
metrics = selectTopSlowestMetrics(metrics, metricsOptions);
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
child.on("message", (message) => {
|
|
336
|
+
if (isRenderMetricBatchMessage(
|
|
337
|
+
message,
|
|
338
|
+
descriptor.metricBatchMessageType
|
|
339
|
+
)) {
|
|
340
|
+
for (const metric of message.metrics) {
|
|
341
|
+
const durationMs2 = Number(metric.durationMs) || 0;
|
|
342
|
+
metrics.push({
|
|
343
|
+
file,
|
|
344
|
+
componentName: getComponentName(metric.componentName),
|
|
345
|
+
durationMs: durationMs2
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
maybePruneMetrics();
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (!isRenderMetricMessage(message, descriptor.metricMessageType)) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const durationMs = Number(message.durationMs) || 0;
|
|
355
|
+
metrics.push({
|
|
356
|
+
file,
|
|
357
|
+
componentName: getComponentName(message.componentName),
|
|
358
|
+
durationMs
|
|
359
|
+
});
|
|
360
|
+
maybePruneMetrics();
|
|
361
|
+
});
|
|
362
|
+
},
|
|
363
|
+
teardown() {
|
|
364
|
+
cleanupNodeTsxLoader?.();
|
|
365
|
+
cleanupNodeTsxLoader = void 0;
|
|
366
|
+
const summary = createMetricsSummary(metrics, metricsOptions);
|
|
367
|
+
if (!summary) return;
|
|
368
|
+
if (metricsOptions.reporter) {
|
|
369
|
+
metricsOptions.reporter(summary);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
printMetricsSummary(summary, descriptor.packageTag);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
};
|
|
376
|
+
const internals = {
|
|
377
|
+
buildRunnerCommand,
|
|
378
|
+
canHandleRuntime,
|
|
379
|
+
buildRuntimeOptionArgs,
|
|
380
|
+
normalizeMetricsOptions,
|
|
381
|
+
selectTopSlowestMetrics,
|
|
382
|
+
createMetricsSummary,
|
|
383
|
+
getComponentName,
|
|
384
|
+
isRenderMetricMessage,
|
|
385
|
+
isRenderMetricBatchMessage,
|
|
386
|
+
resolveDomSetupPath
|
|
387
|
+
};
|
|
388
|
+
return {
|
|
389
|
+
createTestingPlugin,
|
|
390
|
+
resolveDomSetupPath,
|
|
391
|
+
runtimeOptionArgPrefixes: prefixes,
|
|
392
|
+
internals
|
|
393
|
+
};
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
// src/dom-env.ts
|
|
397
|
+
import { GlobalRegistrator } from "@happy-dom/global-registrator";
|
|
398
|
+
var applyReactActEnvironment = (enabled) => {
|
|
399
|
+
if (!enabled) return;
|
|
400
|
+
const reactGlobal = globalThis;
|
|
401
|
+
if (typeof reactGlobal.IS_REACT_ACT_ENVIRONMENT === "undefined") {
|
|
402
|
+
reactGlobal.IS_REACT_ACT_ENVIRONMENT = true;
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
var setupHappyDomEnvironment = async (options) => {
|
|
406
|
+
if (!globalThis.window || !globalThis.document) {
|
|
407
|
+
GlobalRegistrator.register({
|
|
408
|
+
url: options.runtimeOptions.domUrl
|
|
409
|
+
});
|
|
410
|
+
const nativeDispatchEvent = globalThis.window.dispatchEvent;
|
|
411
|
+
if (typeof nativeDispatchEvent === "function") {
|
|
412
|
+
globalThis.dispatchEvent = nativeDispatchEvent.bind(globalThis.window);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
applyReactActEnvironment(Boolean(options.enableReactActEnvironment));
|
|
416
|
+
};
|
|
417
|
+
var defineGlobal = (key, value) => {
|
|
418
|
+
Object.defineProperty(globalThis, key, {
|
|
419
|
+
configurable: true,
|
|
420
|
+
writable: true,
|
|
421
|
+
value
|
|
422
|
+
});
|
|
423
|
+
};
|
|
424
|
+
var setupJsdomEnvironment = async (options) => {
|
|
425
|
+
if (!globalThis.window || !globalThis.document) {
|
|
426
|
+
let mod;
|
|
427
|
+
try {
|
|
428
|
+
mod = await import("jsdom");
|
|
429
|
+
} catch {
|
|
430
|
+
throw new Error(
|
|
431
|
+
`[${options.packageTag}] DOM adapter "jsdom" requires the "jsdom" package. Install it with "npm install --save-dev jsdom".`
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
const { JSDOM } = mod;
|
|
435
|
+
const dom = new JSDOM("", {
|
|
436
|
+
url: options.runtimeOptions.domUrl,
|
|
437
|
+
pretendToBeVisual: true
|
|
438
|
+
});
|
|
439
|
+
defineGlobal("window", dom.window);
|
|
440
|
+
defineGlobal("document", dom.window.document);
|
|
441
|
+
defineGlobal("navigator", dom.window.navigator);
|
|
442
|
+
defineGlobal("HTMLElement", dom.window.HTMLElement);
|
|
443
|
+
defineGlobal("Element", dom.window.Element);
|
|
444
|
+
defineGlobal("Node", dom.window.Node);
|
|
445
|
+
defineGlobal("Text", dom.window.Text);
|
|
446
|
+
defineGlobal("SVGElement", dom.window.SVGElement);
|
|
447
|
+
defineGlobal("Event", dom.window.Event);
|
|
448
|
+
defineGlobal("CustomEvent", dom.window.CustomEvent);
|
|
449
|
+
defineGlobal("MutationObserver", dom.window.MutationObserver);
|
|
450
|
+
defineGlobal("requestAnimationFrame", dom.window.requestAnimationFrame);
|
|
451
|
+
defineGlobal("cancelAnimationFrame", dom.window.cancelAnimationFrame);
|
|
452
|
+
}
|
|
453
|
+
applyReactActEnvironment(Boolean(options.enableReactActEnvironment));
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
// src/testing-core.ts
|
|
457
|
+
import { getQueriesForElement } from "@testing-library/dom";
|
|
458
|
+
var getNow = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now.bind(performance) : Date.now.bind(Date);
|
|
459
|
+
var getMetricsRuntimeState = (stateKey) => {
|
|
460
|
+
const stateGlobal = globalThis;
|
|
461
|
+
if (!stateGlobal[stateKey]) {
|
|
462
|
+
stateGlobal[stateKey] = {
|
|
463
|
+
metricBuffer: [],
|
|
464
|
+
metricFlushTimer: void 0,
|
|
465
|
+
metricsChannelClosed: false,
|
|
466
|
+
listenersRegistered: false
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
return stateGlobal[stateKey];
|
|
470
|
+
};
|
|
471
|
+
var createRenderMetricsEmitter = (options) => {
|
|
472
|
+
const { runtimeOptions, metricsStateKey, metricsBatchMessageType } = options;
|
|
473
|
+
const metricsState = getMetricsRuntimeState(metricsStateKey);
|
|
474
|
+
const clearMetricFlushTimer = () => {
|
|
475
|
+
if (!metricsState.metricFlushTimer) return;
|
|
476
|
+
clearTimeout(metricsState.metricFlushTimer);
|
|
477
|
+
metricsState.metricFlushTimer = void 0;
|
|
478
|
+
};
|
|
479
|
+
const flushMetricBuffer = () => {
|
|
480
|
+
if (!runtimeOptions.metricsEnabled || typeof process.send !== "function") {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
if (process.connected === false) {
|
|
484
|
+
metricsState.metricBuffer.length = 0;
|
|
485
|
+
metricsState.metricsChannelClosed = true;
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
if (metricsState.metricsChannelClosed || metricsState.metricBuffer.length === 0) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
const payload = metricsState.metricBuffer.splice(
|
|
492
|
+
0,
|
|
493
|
+
metricsState.metricBuffer.length
|
|
494
|
+
);
|
|
495
|
+
try {
|
|
496
|
+
process.send({
|
|
497
|
+
type: metricsBatchMessageType,
|
|
498
|
+
metrics: payload
|
|
499
|
+
});
|
|
500
|
+
} catch {
|
|
501
|
+
metricsState.metricsChannelClosed = true;
|
|
502
|
+
metricsState.metricBuffer.length = 0;
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
const scheduleMetricFlush = () => {
|
|
506
|
+
if (metricsState.metricFlushTimer) return;
|
|
507
|
+
metricsState.metricFlushTimer = setTimeout(() => {
|
|
508
|
+
metricsState.metricFlushTimer = void 0;
|
|
509
|
+
flushMetricBuffer();
|
|
510
|
+
}, runtimeOptions.metricFlushMs);
|
|
511
|
+
metricsState.metricFlushTimer.unref?.();
|
|
512
|
+
};
|
|
513
|
+
if (runtimeOptions.metricsEnabled && !metricsState.listenersRegistered) {
|
|
514
|
+
metricsState.listenersRegistered = true;
|
|
515
|
+
process.on("beforeExit", () => {
|
|
516
|
+
clearMetricFlushTimer();
|
|
517
|
+
flushMetricBuffer();
|
|
518
|
+
});
|
|
519
|
+
process.on("disconnect", () => {
|
|
520
|
+
clearMetricFlushTimer();
|
|
521
|
+
metricsState.metricBuffer.length = 0;
|
|
522
|
+
metricsState.metricsChannelClosed = true;
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
const emitRenderMetric = (componentName, durationMs) => {
|
|
526
|
+
if (!runtimeOptions.metricsEnabled || typeof process.send !== "function") {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (process.connected === false || metricsState.metricsChannelClosed) {
|
|
530
|
+
metricsState.metricBuffer.length = 0;
|
|
531
|
+
metricsState.metricsChannelClosed = true;
|
|
532
|
+
clearMetricFlushTimer();
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
const safeDuration = Number.isFinite(durationMs) && durationMs >= 0 ? durationMs : 0;
|
|
536
|
+
if (safeDuration < runtimeOptions.minMetricMs) return;
|
|
537
|
+
metricsState.metricBuffer.push({
|
|
538
|
+
componentName,
|
|
539
|
+
durationMs: safeDuration
|
|
540
|
+
});
|
|
541
|
+
if (metricsState.metricBuffer.length >= runtimeOptions.metricBatchSize) {
|
|
542
|
+
clearMetricFlushTimer();
|
|
543
|
+
flushMetricBuffer();
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
scheduleMetricFlush();
|
|
547
|
+
};
|
|
548
|
+
return {
|
|
549
|
+
emitRenderMetric,
|
|
550
|
+
flushMetricBuffer,
|
|
551
|
+
clearMetricFlushTimer
|
|
552
|
+
};
|
|
553
|
+
};
|
|
554
|
+
var createScreen = () => {
|
|
555
|
+
return new Proxy({}, {
|
|
556
|
+
get(_target, prop) {
|
|
557
|
+
const baseScreenQueries = getQueriesForElement(document.body);
|
|
558
|
+
const value = Reflect.get(baseScreenQueries, prop, baseScreenQueries);
|
|
559
|
+
return typeof value === "function" ? value.bind(baseScreenQueries) : value;
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
};
|
|
563
|
+
export {
|
|
564
|
+
buildRunnerCommand,
|
|
565
|
+
buildRuntimeOptionArgs,
|
|
566
|
+
canHandleRuntime,
|
|
567
|
+
createDomSetupPathResolver,
|
|
568
|
+
createFrameworkTestingPluginFactory,
|
|
569
|
+
createMetricsSummary,
|
|
570
|
+
createRenderMetricsEmitter,
|
|
571
|
+
createRuntimeOptionArgPrefixes,
|
|
572
|
+
createScreen,
|
|
573
|
+
getComponentName,
|
|
574
|
+
getNow,
|
|
575
|
+
getRuntimeSupport,
|
|
576
|
+
isNodeRuntime,
|
|
577
|
+
isRenderMetricBatchMessage,
|
|
578
|
+
isRenderMetricMessage,
|
|
579
|
+
normalizeMetricsOptions,
|
|
580
|
+
parseRuntimeOptions,
|
|
581
|
+
printMetricsSummary,
|
|
582
|
+
selectTopSlowestMetrics,
|
|
583
|
+
setupHappyDomEnvironment,
|
|
584
|
+
setupInProcessEnvironment,
|
|
585
|
+
setupJsdomEnvironment
|
|
586
|
+
};
|
|
587
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime-options.ts","../src/plugin-command.ts","../src/plugin-setup.ts","../src/plugin-metrics.ts","../src/plugin-factory.ts","../src/dom-env.ts","../src/testing-core.ts"],"sourcesContent":["import type {\n RuntimeOptionArgPrefixes,\n RuntimeOptions,\n} from './types.ts';\n\nconst DEFAULT_DOM_URL = 'http://localhost:3000/';\nconst DEFAULT_METRIC_BATCH_SIZE = 50;\nconst DEFAULT_METRIC_FLUSH_MS = 50;\n\nconst toNumber = (value: string | undefined) => {\n if (!value) return NaN;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : NaN;\n};\n\nconst findValueByPrefix = (argv: string[], prefix: string) => {\n const match = argv.find((arg) => arg.startsWith(prefix));\n return match ? match.slice(prefix.length) : undefined;\n};\n\nconst parsePositiveInteger = (value: string | undefined, fallback: number) => {\n const numeric = toNumber(value);\n if (!Number.isFinite(numeric) || numeric <= 0) return fallback;\n return Math.floor(numeric);\n};\n\nconst parseNonNegativeNumber = (value: string | undefined, fallback: number) => {\n const numeric = toNumber(value);\n if (!Number.isFinite(numeric) || numeric < 0) return fallback;\n return numeric;\n};\n\nexport const createRuntimeOptionArgPrefixes = (\n base: string\n): RuntimeOptionArgPrefixes => ({\n metrics: `--${base}-metrics=`,\n minMetricMs: `--${base}-min-metric-ms=`,\n domUrl: `--${base}-dom-url=`,\n metricBatchSize: `--${base}-metric-batch-size=`,\n metricFlushMs: `--${base}-metric-flush-ms=`,\n});\n\nexport const parseRuntimeOptions = (\n prefixes: RuntimeOptionArgPrefixes,\n argv: string[] = process.argv\n): RuntimeOptions => {\n const metricsEnabled =\n findValueByPrefix(argv, prefixes.metrics) === '1' ||\n findValueByPrefix(argv, prefixes.metrics) === 'true';\n\n const domUrl =\n findValueByPrefix(argv, prefixes.domUrl)?.trim() || DEFAULT_DOM_URL;\n\n const minMetricMs = parseNonNegativeNumber(\n findValueByPrefix(argv, prefixes.minMetricMs),\n 0\n );\n\n const metricBatchSize = parsePositiveInteger(\n findValueByPrefix(argv, prefixes.metricBatchSize),\n DEFAULT_METRIC_BATCH_SIZE\n );\n\n const metricFlushMs = parsePositiveInteger(\n findValueByPrefix(argv, prefixes.metricFlushMs),\n DEFAULT_METRIC_FLUSH_MS\n );\n\n return {\n domUrl,\n metricsEnabled,\n minMetricMs,\n metricBatchSize,\n metricFlushMs,\n };\n};\n","import type { DomAdapter } from './types.ts';\nimport { existsSync } from 'node:fs';\nimport { extname, resolve } from 'node:path';\n\nexport type RuntimeSupport = {\n supportsNodeLikeImport: boolean;\n supportsDenoPreload: boolean;\n};\n\nexport type BuildRunnerCommandInput = {\n runtime: string;\n command: string[];\n file: string;\n domSetupPath: string;\n runtimeOptionArgs: string[];\n extensions: Set<string>;\n};\n\nexport type BuildRunnerCommandOutput = {\n shouldHandle: boolean;\n command: string[];\n};\n\nconst isTsxImport = (arg: string) =>\n arg === '--import=tsx' || arg === '--loader=tsx';\n\nexport const isNodeRuntime = (runtime: string) => runtime === 'node';\nconst isBunRuntime = (runtime: string) => runtime === 'bun';\nconst isDenoRuntime = (runtime: string) => runtime === 'deno';\n\nexport const getRuntimeSupport = (runtime: string): RuntimeSupport => ({\n supportsNodeLikeImport: isNodeRuntime(runtime) || isBunRuntime(runtime),\n supportsDenoPreload: isDenoRuntime(runtime),\n});\n\nexport const canHandleRuntime = (runtime: string) => {\n const support = getRuntimeSupport(runtime);\n return support.supportsNodeLikeImport || support.supportsDenoPreload;\n};\n\nexport const createDomSetupPathResolver = (\n packageTag: string,\n happyDomSetupPath: string,\n jsdomSetupPath: string\n) => {\n return (adapter: DomAdapter | undefined) => {\n if (!adapter || adapter === 'happy-dom') return happyDomSetupPath;\n if (adapter === 'jsdom') return jsdomSetupPath;\n\n const customPath = resolve(process.cwd(), adapter.setupModule);\n\n if (!existsSync(customPath)) {\n throw new Error(\n `[${packageTag}] Custom DOM setup module not found: \"${customPath}\"\\n` +\n 'Check the \"dom.setupModule\" option in your poku.config.js.'\n );\n }\n\n return customPath;\n };\n};\n\nexport const buildRunnerCommand = ({\n runtime,\n command,\n file,\n domSetupPath,\n runtimeOptionArgs,\n extensions,\n}: BuildRunnerCommandInput): BuildRunnerCommandOutput => {\n const support = getRuntimeSupport(runtime);\n\n if (!support.supportsNodeLikeImport && !support.supportsDenoPreload) {\n return { shouldHandle: false, command };\n }\n\n if (!extensions.has(extname(file))) {\n return { shouldHandle: false, command };\n }\n\n const fileIndex = command.lastIndexOf(file);\n if (fileIndex === -1) return { shouldHandle: false, command };\n\n const nodeImportFlag = `--import=${domSetupPath}`;\n const denoPreloadFlag = `--preload=${domSetupPath}`;\n const beforeFile: string[] = [];\n const afterFile: string[] = [];\n\n let hasTsx = false;\n let hasNodeLikeDomSetup = false;\n let hasDenoDomSetup = false;\n const existingArgs = new Set<string>();\n\n for (let index = 1; index < command.length; index += 1) {\n const arg = command[index];\n if (typeof arg !== 'string') continue;\n\n existingArgs.add(arg);\n\n if (index < fileIndex) {\n beforeFile.push(arg);\n\n if (isTsxImport(arg)) hasTsx = true;\n else if (arg === nodeImportFlag) hasNodeLikeDomSetup = true;\n else if (arg === denoPreloadFlag) hasDenoDomSetup = true;\n continue;\n }\n\n if (index > fileIndex) {\n afterFile.push(arg);\n }\n }\n\n const extraImports: string[] = [];\n if (isNodeRuntime(runtime) && !hasTsx) extraImports.push('--import=tsx');\n if (support.supportsNodeLikeImport && !hasNodeLikeDomSetup) {\n extraImports.push(nodeImportFlag);\n }\n if (support.supportsDenoPreload && !hasDenoDomSetup) {\n extraImports.push(denoPreloadFlag);\n }\n\n const runtimeArgsToInject: string[] = [];\n for (const runtimeOptionArg of runtimeOptionArgs) {\n if (existingArgs.has(runtimeOptionArg)) continue;\n runtimeArgsToInject.push(runtimeOptionArg);\n }\n\n return {\n shouldHandle: true,\n command: [\n runtime,\n ...beforeFile,\n ...extraImports,\n file,\n ...runtimeArgsToInject,\n ...afterFile,\n ],\n };\n};\n","import { pathToFileURL } from 'node:url';\nimport { createRequire } from 'node:module';\nimport { canHandleRuntime, isNodeRuntime } from './plugin-command.ts';\n\ntype TsxEsmApiModule = {\n register?: () => () => void;\n};\n\nconst TSX_LOADER_MODULE = 'tsx/esm/api';\n\nconst appendMissingRuntimeArgs = (runtimeOptionArgs: string[]) => {\n for (const arg of runtimeOptionArgs) {\n if (process.argv.includes(arg)) continue;\n process.argv.push(arg);\n }\n};\n\nconst loadDomSetupModule = async (domSetupPath: string) => {\n await import(pathToFileURL(domSetupPath).href);\n};\n\nconst registerNodeTsxLoader = async (packageTag: string) => {\n const requireFromCwd = createRequire(`${process.cwd()}/`);\n\n try {\n const resolvedModulePath = requireFromCwd.resolve(TSX_LOADER_MODULE);\n const mod = (await import(pathToFileURL(resolvedModulePath).href)) as TsxEsmApiModule;\n if (typeof mod.register !== 'function') {\n throw new Error('Missing register() export from tsx loader API');\n }\n\n return mod.register();\n } catch (error) {\n throw new Error(\n `[${packageTag}] isolation \"none\" in Node.js requires a working \"tsx\" installation to load .tsx/.jsx test files.`,\n { cause: error }\n );\n }\n};\n\nexport type InProcessSetupOptions = {\n isolation: string | undefined;\n runtime: string;\n runtimeOptionArgs: string[];\n domSetupPath: string;\n packageTag: string;\n};\n\nexport const setupInProcessEnvironment = async (\n options: InProcessSetupOptions\n): Promise<(() => void) | undefined> => {\n if (options.isolation !== 'none') return undefined;\n if (!canHandleRuntime(options.runtime)) return undefined;\n\n let cleanupNodeTsxLoader: (() => void) | undefined;\n\n if (isNodeRuntime(options.runtime)) {\n cleanupNodeTsxLoader = await registerNodeTsxLoader(options.packageTag);\n }\n\n appendMissingRuntimeArgs(options.runtimeOptionArgs);\n await loadDomSetupModule(options.domSetupPath);\n\n return cleanupNodeTsxLoader;\n};\n","import type {\n MetricsOptions,\n MetricsSummary,\n NormalizedMetricsOptions,\n RenderMetric,\n RuntimeOptionArgPrefixes,\n TestingPluginOptions,\n} from './types.ts';\n\nconst DEFAULT_TOP_N = 5;\nconst DEFAULT_MIN_DURATION_MS = 0;\n\nexport const isRenderMetricMessage = (\n message: unknown,\n metricMessageType: string\n): message is { type: string; componentName?: string; durationMs?: number } => {\n if (!message || typeof message !== 'object') return false;\n return (message as Record<string, unknown>).type === metricMessageType;\n};\n\nexport const isRenderMetricBatchMessage = (\n message: unknown,\n metricBatchMessageType: string\n): message is {\n type: string;\n metrics: Array<{ componentName?: string; durationMs?: number }>;\n} => {\n if (!message || typeof message !== 'object') return false;\n\n const record = message as Record<string, unknown>;\n return (\n record.type === metricBatchMessageType && Array.isArray(record.metrics)\n );\n};\n\nexport const getComponentName = (componentName: unknown) =>\n typeof componentName === 'string' && componentName.length > 0\n ? componentName\n : 'AnonymousComponent';\n\nconst getPositiveIntegerOrDefault = (value: unknown, fallback: number) => {\n const numeric =\n typeof value === 'number'\n ? value\n : typeof value === 'string' && value.trim().length > 0\n ? Number(value.trim())\n : NaN;\n\n if (!Number.isFinite(numeric) || numeric <= 0) return fallback;\n return Math.floor(numeric);\n};\n\nconst getNonNegativeNumberOrDefault = (value: unknown, fallback: number) => {\n const numeric =\n typeof value === 'number'\n ? value\n : typeof value === 'string' && value.trim().length > 0\n ? Number(value.trim())\n : NaN;\n\n if (!Number.isFinite(numeric) || numeric < 0) return fallback;\n return numeric;\n};\n\nexport const buildRuntimeOptionArgs = (\n options: TestingPluginOptions,\n metricsOptions: NormalizedMetricsOptions,\n prefixes: RuntimeOptionArgPrefixes\n) => {\n const args: string[] = [];\n\n if (options.domUrl) {\n args.push(`${prefixes.domUrl}${options.domUrl}`);\n }\n\n if (metricsOptions.enabled) {\n args.push(`${prefixes.metrics}1`);\n args.push(`${prefixes.minMetricMs}${metricsOptions.minDurationMs}`);\n }\n\n return args;\n};\n\nexport const normalizeMetricsOptions = (\n metrics: boolean | MetricsOptions | undefined\n): NormalizedMetricsOptions => {\n if (metrics === true) {\n return {\n enabled: true,\n topN: DEFAULT_TOP_N,\n minDurationMs: DEFAULT_MIN_DURATION_MS,\n };\n }\n\n if (!metrics) {\n return {\n enabled: false,\n topN: DEFAULT_TOP_N,\n minDurationMs: DEFAULT_MIN_DURATION_MS,\n };\n }\n\n const normalized: NormalizedMetricsOptions = {\n enabled: metrics.enabled ?? true,\n topN: getPositiveIntegerOrDefault(metrics.topN, DEFAULT_TOP_N),\n minDurationMs: getNonNegativeNumberOrDefault(\n metrics.minDurationMs,\n DEFAULT_MIN_DURATION_MS\n ),\n };\n\n if (metrics.reporter) normalized.reporter = metrics.reporter;\n\n return normalized;\n};\n\nexport const selectTopSlowestMetrics = (\n metrics: RenderMetric[],\n options: NormalizedMetricsOptions\n) =>\n [...metrics]\n .sort((a, b) => b.durationMs - a.durationMs)\n .slice(0, options.topN);\n\nexport const createMetricsSummary = (\n metrics: RenderMetric[],\n options: NormalizedMetricsOptions\n): MetricsSummary | null => {\n if (!options.enabled || metrics.length === 0) return null;\n\n const topSlowest = selectTopSlowestMetrics(metrics, options);\n if (topSlowest.length === 0) return null;\n\n return {\n totalCaptured: metrics.length,\n totalReported: topSlowest.length,\n topSlowest,\n };\n};\n\nexport const printMetricsSummary = (\n summary: MetricsSummary,\n packageTag: string\n) => {\n const lines = summary.topSlowest.map(\n (metric) =>\n ` - ${metric.componentName} in ${metric.file}: ${metric.durationMs.toFixed(2)}ms`\n );\n\n console.log(`\\n[${packageTag}] Slowest component renders`);\n for (const line of lines) console.log(line);\n};\n","import type {\n DomAdapter,\n FrameworkDescriptor,\n MetricsSummary,\n RenderMetric,\n TestingPluginOptions,\n} from './types.ts';\nimport { definePlugin } from 'poku/plugins';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { existsSync } from 'node:fs';\nimport {\n buildRunnerCommand,\n canHandleRuntime,\n createDomSetupPathResolver,\n} from './plugin-command.ts';\nimport {\n buildRuntimeOptionArgs,\n createMetricsSummary,\n getComponentName,\n isRenderMetricBatchMessage,\n isRenderMetricMessage,\n normalizeMetricsOptions,\n printMetricsSummary,\n selectTopSlowestMetrics,\n} from './plugin-metrics.ts';\nimport { setupInProcessEnvironment } from './plugin-setup.ts';\nimport { createRuntimeOptionArgPrefixes } from './runtime-options.ts';\n\nexport type FrameworkPluginInternals = {\n buildRunnerCommand: typeof buildRunnerCommand;\n canHandleRuntime: typeof canHandleRuntime;\n buildRuntimeOptionArgs: typeof buildRuntimeOptionArgs;\n normalizeMetricsOptions: typeof normalizeMetricsOptions;\n selectTopSlowestMetrics: typeof selectTopSlowestMetrics;\n createMetricsSummary: typeof createMetricsSummary;\n getComponentName: typeof getComponentName;\n isRenderMetricMessage: typeof isRenderMetricMessage;\n isRenderMetricBatchMessage: typeof isRenderMetricBatchMessage;\n resolveDomSetupPath: (adapter: DomAdapter | undefined) => string;\n};\n\nexport const createFrameworkTestingPluginFactory = (\n descriptor: FrameworkDescriptor,\n moduleUrl: string\n) => {\n const currentDir = dirname(fileURLToPath(moduleUrl));\n const resolveSetupModulePath = (baseName: string) => {\n const jsPath = resolve(currentDir, `${baseName}.js`);\n if (existsSync(jsPath)) return jsPath;\n return resolve(currentDir, `${baseName}.ts`);\n };\n\n const happyDomSetupPath = resolveSetupModulePath('dom-setup-happy');\n const jsdomSetupPath = resolveSetupModulePath('dom-setup-jsdom');\n const resolveDomSetupPath = createDomSetupPathResolver(\n descriptor.packageTag,\n happyDomSetupPath,\n jsdomSetupPath\n );\n\n const prefixes = createRuntimeOptionArgPrefixes(descriptor.runtimeArgBase);\n const extensions = new Set(descriptor.testFileExtensions ?? ['.tsx', '.jsx']);\n\n const createTestingPlugin = (options: TestingPluginOptions = {}) => {\n let metrics: RenderMetric[] = [];\n let cleanupNodeTsxLoader: (() => void) | undefined;\n const domSetupPath = resolveDomSetupPath(options.dom);\n const metricsOptions = normalizeMetricsOptions(options.metrics);\n const runtimeOptionArgs = buildRuntimeOptionArgs(\n options,\n metricsOptions,\n prefixes\n );\n\n return definePlugin({\n name: descriptor.pluginName,\n ipc: metricsOptions.enabled,\n\n async setup(context: any) {\n cleanupNodeTsxLoader = await setupInProcessEnvironment({\n isolation: context.configs.isolation,\n runtime: context.runtime,\n runtimeOptionArgs,\n domSetupPath,\n packageTag: descriptor.packageTag,\n });\n },\n\n runner(command: string[], file: string) {\n const runtime = command[0];\n if (!runtime) return command;\n\n const result = buildRunnerCommand({\n runtime,\n command,\n file,\n domSetupPath,\n runtimeOptionArgs,\n extensions,\n });\n\n if (!result.shouldHandle) return command;\n return result.command;\n },\n\n onTestProcess(child: any, file: string) {\n if (!metricsOptions.enabled) return;\n\n const maybePruneMetrics = () => {\n if (metrics.length > metricsOptions.topN * 10) {\n metrics = selectTopSlowestMetrics(metrics, metricsOptions);\n }\n };\n\n child.on('message', (message: unknown) => {\n if (\n isRenderMetricBatchMessage(\n message,\n descriptor.metricBatchMessageType\n )\n ) {\n for (const metric of message.metrics) {\n const durationMs = Number(metric.durationMs) || 0;\n\n metrics.push({\n file,\n componentName: getComponentName(metric.componentName),\n durationMs,\n });\n }\n\n maybePruneMetrics();\n return;\n }\n\n if (!isRenderMetricMessage(message, descriptor.metricMessageType)) {\n return;\n }\n\n const durationMs = Number(message.durationMs) || 0;\n\n metrics.push({\n file,\n componentName: getComponentName(message.componentName),\n durationMs,\n });\n\n maybePruneMetrics();\n });\n },\n\n teardown() {\n cleanupNodeTsxLoader?.();\n cleanupNodeTsxLoader = undefined;\n\n const summary = createMetricsSummary(metrics, metricsOptions);\n if (!summary) return;\n\n if (metricsOptions.reporter) {\n metricsOptions.reporter(summary as MetricsSummary);\n return;\n }\n\n printMetricsSummary(summary, descriptor.packageTag);\n },\n });\n };\n\n const internals: FrameworkPluginInternals = {\n buildRunnerCommand,\n canHandleRuntime,\n buildRuntimeOptionArgs,\n normalizeMetricsOptions,\n selectTopSlowestMetrics,\n createMetricsSummary,\n getComponentName,\n isRenderMetricMessage,\n isRenderMetricBatchMessage,\n resolveDomSetupPath,\n };\n\n return {\n createTestingPlugin,\n resolveDomSetupPath,\n runtimeOptionArgPrefixes: prefixes,\n internals,\n };\n};\n","import { GlobalRegistrator } from '@happy-dom/global-registrator';\nimport type { RuntimeOptions } from './types.ts';\n\ntype SetupDomEnvironmentOptions = {\n runtimeOptions: RuntimeOptions;\n packageTag: string;\n enableReactActEnvironment?: boolean;\n};\n\ntype ReactActGlobal = typeof globalThis & {\n IS_REACT_ACT_ENVIRONMENT?: boolean;\n};\n\nconst applyReactActEnvironment = (enabled: boolean) => {\n if (!enabled) return;\n const reactGlobal = globalThis as ReactActGlobal;\n if (typeof reactGlobal.IS_REACT_ACT_ENVIRONMENT === 'undefined') {\n reactGlobal.IS_REACT_ACT_ENVIRONMENT = true;\n }\n};\n\nexport const setupHappyDomEnvironment = async (\n options: SetupDomEnvironmentOptions\n) => {\n if (!globalThis.window || !globalThis.document) {\n GlobalRegistrator.register({\n url: options.runtimeOptions.domUrl,\n });\n\n const nativeDispatchEvent = globalThis.window.dispatchEvent;\n if (typeof nativeDispatchEvent === 'function') {\n globalThis.dispatchEvent = nativeDispatchEvent.bind(globalThis.window);\n }\n }\n\n applyReactActEnvironment(Boolean(options.enableReactActEnvironment));\n};\n\nconst defineGlobal = (key: keyof typeof globalThis, value: unknown) => {\n Object.defineProperty(globalThis, key, {\n configurable: true,\n writable: true,\n value,\n });\n};\n\nexport const setupJsdomEnvironment = async (\n options: SetupDomEnvironmentOptions\n) => {\n if (!globalThis.window || !globalThis.document) {\n let mod: typeof import('jsdom');\n\n try {\n mod = await import('jsdom');\n } catch {\n throw new Error(\n `[${options.packageTag}] DOM adapter \"jsdom\" requires the \"jsdom\" package. Install it with \"npm install --save-dev jsdom\".`\n );\n }\n\n const { JSDOM } = mod;\n const dom = new JSDOM('', {\n url: options.runtimeOptions.domUrl,\n pretendToBeVisual: true,\n });\n\n defineGlobal('window', dom.window as unknown as Window & typeof globalThis);\n defineGlobal('document', dom.window.document);\n defineGlobal('navigator', dom.window.navigator);\n defineGlobal('HTMLElement', dom.window.HTMLElement);\n defineGlobal('Element', dom.window.Element);\n defineGlobal('Node', dom.window.Node);\n defineGlobal('Text', dom.window.Text);\n defineGlobal('SVGElement', dom.window.SVGElement);\n defineGlobal('Event', dom.window.Event);\n defineGlobal('CustomEvent', dom.window.CustomEvent);\n defineGlobal('MutationObserver', dom.window.MutationObserver);\n defineGlobal('requestAnimationFrame', dom.window.requestAnimationFrame);\n defineGlobal('cancelAnimationFrame', dom.window.cancelAnimationFrame);\n }\n\n applyReactActEnvironment(Boolean(options.enableReactActEnvironment));\n};\n","import type { Screen } from '@testing-library/dom';\nimport { getQueriesForElement } from '@testing-library/dom';\nimport type { RuntimeOptions } from './types.ts';\n\nexport const getNow: () => number =\n typeof performance !== 'undefined' && typeof performance.now === 'function'\n ? performance.now.bind(performance)\n : Date.now.bind(Date);\n\ntype QueuedRenderMetric = {\n componentName: string;\n durationMs: number;\n};\n\ntype MetricsRuntimeState = {\n metricBuffer: QueuedRenderMetric[];\n metricFlushTimer: ReturnType<typeof setTimeout> | undefined;\n metricsChannelClosed: boolean;\n listenersRegistered: boolean;\n};\n\ntype RuntimeStateGlobal = typeof globalThis & {\n [symbol: symbol]: MetricsRuntimeState | undefined;\n};\n\nexport type RenderMetricsEmitterOptions = {\n runtimeOptions: RuntimeOptions;\n metricsStateKey: symbol;\n metricsBatchMessageType: string;\n};\n\nconst getMetricsRuntimeState = (\n stateKey: symbol\n): MetricsRuntimeState => {\n const stateGlobal = globalThis as RuntimeStateGlobal;\n\n if (!stateGlobal[stateKey]) {\n stateGlobal[stateKey] = {\n metricBuffer: [],\n metricFlushTimer: undefined,\n metricsChannelClosed: false,\n listenersRegistered: false,\n };\n }\n\n return stateGlobal[stateKey]!;\n};\n\nexport const createRenderMetricsEmitter = (\n options: RenderMetricsEmitterOptions\n) => {\n const { runtimeOptions, metricsStateKey, metricsBatchMessageType } = options;\n const metricsState = getMetricsRuntimeState(metricsStateKey);\n\n const clearMetricFlushTimer = () => {\n if (!metricsState.metricFlushTimer) return;\n clearTimeout(metricsState.metricFlushTimer);\n metricsState.metricFlushTimer = undefined;\n };\n\n const flushMetricBuffer = () => {\n if (!runtimeOptions.metricsEnabled || typeof process.send !== 'function') {\n return;\n }\n\n if (process.connected === false) {\n metricsState.metricBuffer.length = 0;\n metricsState.metricsChannelClosed = true;\n return;\n }\n\n if (\n metricsState.metricsChannelClosed ||\n metricsState.metricBuffer.length === 0\n ) {\n return;\n }\n\n const payload = metricsState.metricBuffer.splice(\n 0,\n metricsState.metricBuffer.length\n );\n\n try {\n process.send({\n type: metricsBatchMessageType,\n metrics: payload,\n });\n } catch {\n metricsState.metricsChannelClosed = true;\n metricsState.metricBuffer.length = 0;\n }\n };\n\n const scheduleMetricFlush = () => {\n if (metricsState.metricFlushTimer) return;\n\n metricsState.metricFlushTimer = setTimeout(() => {\n metricsState.metricFlushTimer = undefined;\n flushMetricBuffer();\n }, runtimeOptions.metricFlushMs);\n\n metricsState.metricFlushTimer.unref?.();\n };\n\n if (runtimeOptions.metricsEnabled && !metricsState.listenersRegistered) {\n metricsState.listenersRegistered = true;\n\n process.on('beforeExit', () => {\n clearMetricFlushTimer();\n flushMetricBuffer();\n });\n\n process.on('disconnect', () => {\n clearMetricFlushTimer();\n metricsState.metricBuffer.length = 0;\n metricsState.metricsChannelClosed = true;\n });\n }\n\n const emitRenderMetric = (componentName: string, durationMs: number) => {\n if (\n !runtimeOptions.metricsEnabled ||\n typeof process.send !== 'function'\n ) {\n return;\n }\n\n if (process.connected === false || metricsState.metricsChannelClosed) {\n metricsState.metricBuffer.length = 0;\n metricsState.metricsChannelClosed = true;\n clearMetricFlushTimer();\n return;\n }\n\n const safeDuration =\n Number.isFinite(durationMs) && durationMs >= 0 ? durationMs : 0;\n\n if (safeDuration < runtimeOptions.minMetricMs) return;\n\n metricsState.metricBuffer.push({\n componentName,\n durationMs: safeDuration,\n });\n\n if (metricsState.metricBuffer.length >= runtimeOptions.metricBatchSize) {\n clearMetricFlushTimer();\n flushMetricBuffer();\n return;\n }\n\n scheduleMetricFlush();\n };\n\n return {\n emitRenderMetric,\n flushMetricBuffer,\n clearMetricFlushTimer,\n };\n};\n\nexport const createScreen = (): Screen => {\n return new Proxy({} as Screen, {\n get(_target, prop) {\n const baseScreenQueries = getQueriesForElement(document.body);\n const value = Reflect.get(baseScreenQueries, prop, baseScreenQueries);\n return typeof value === 'function'\n ? value.bind(baseScreenQueries)\n : value;\n },\n }) as Screen;\n};\n"],"mappings":"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","names":["resolve","existsSync","resolve","existsSync","durationMs"]}
|
package/package.json
CHANGED
|
@@ -1,11 +1,91 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pokujs/dom",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"private": false,
|
|
4
5
|
"description": "Shared DOM testing core for Poku framework adapters.",
|
|
5
6
|
"type": "module",
|
|
6
|
-
"main": "./index.js",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=20.x.x",
|
|
21
|
+
"bun": ">=1.x.x",
|
|
22
|
+
"deno": ">=2.x.x",
|
|
23
|
+
"typescript": ">=6.x.x"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"test": "npm run test:node",
|
|
27
|
+
"test:node": "node --import=tsx ./node_modules/poku/lib/bin/index.js tests --showLogs",
|
|
28
|
+
"test:bun": "bun ./node_modules/poku/lib/bin/index.js tests --showLogs",
|
|
29
|
+
"test:deno": "deno run -A npm:poku tests --showLogs",
|
|
30
|
+
"clean": "rimraf dist",
|
|
31
|
+
"build": "tsup src/index.ts --format esm --dts --target node20 --sourcemap --clean --tsconfig tsconfig.tsup.json",
|
|
32
|
+
"typecheck": "tsc -p tsconfig.build.json --noEmit",
|
|
33
|
+
"lint": "npm run typecheck",
|
|
34
|
+
"check": "npm run typecheck && npm test",
|
|
35
|
+
"prepack": "npm run build"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"poku",
|
|
39
|
+
"dom",
|
|
40
|
+
"testing",
|
|
41
|
+
"happy-dom",
|
|
42
|
+
"jsdom"
|
|
43
|
+
],
|
|
44
|
+
"author": "Lojhan",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/pokujs/dom.git"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/pokujs/dom#readme",
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/pokujs/dom/issues"
|
|
52
|
+
},
|
|
7
53
|
"license": "MIT",
|
|
8
54
|
"publishConfig": {
|
|
9
|
-
"access": "public"
|
|
55
|
+
"access": "public",
|
|
56
|
+
"provenance": true
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"@happy-dom/global-registrator": ">=20",
|
|
60
|
+
"happy-dom": ">=20",
|
|
61
|
+
"jsdom": ">=22",
|
|
62
|
+
"poku": ">=4.1.0"
|
|
63
|
+
},
|
|
64
|
+
"peerDependenciesMeta": {
|
|
65
|
+
"@happy-dom/global-registrator": {
|
|
66
|
+
"optional": true
|
|
67
|
+
},
|
|
68
|
+
"happy-dom": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"jsdom": {
|
|
72
|
+
"optional": true
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"@testing-library/dom": "^10.4.1"
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@happy-dom/global-registrator": "^20.8.9",
|
|
80
|
+
"@types/jsdom": "^28.0.1",
|
|
81
|
+
"@types/node": "^25.5.0",
|
|
82
|
+
"cross-env": "^10.1.0",
|
|
83
|
+
"happy-dom": "^20.8.9",
|
|
84
|
+
"jsdom": "^26.1.0",
|
|
85
|
+
"poku": "4.2.0",
|
|
86
|
+
"rimraf": "^6.0.1",
|
|
87
|
+
"tsup": "^8.5.0",
|
|
88
|
+
"tsx": "^4.21.0",
|
|
89
|
+
"typescript": "^6.0.2"
|
|
10
90
|
}
|
|
11
91
|
}
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
// placeholder
|