dsh-agent-team-ui-testing 0.1.1 → 0.1.3
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 +172 -36
- package/annotation-capture.js +111 -0
- package/browser-safety.js +45 -1
- package/client.js +3721 -132
- package/context-menu.js +134 -0
- package/core/clock.js +88 -0
- package/core/config-store.js +566 -0
- package/core/config.js +892 -0
- package/core/contracts.js +240 -0
- package/core/errors.js +156 -0
- package/core/evidence.js +141 -0
- package/core/geometry.js +217 -0
- package/core/identity.js +180 -0
- package/core/index.js +24 -0
- package/core/p0.js +1101 -0
- package/core/readiness.js +175 -0
- package/core/registry.js +239 -0
- package/core/tab-access.js +1350 -0
- package/docs/adapters.md +160 -0
- package/docs/architecture.md +44 -9
- package/docs/capability-tiers.md +20 -4
- package/docs/capture-p0.md +39 -2
- package/docs/diagnose-reason-codes.md +157 -0
- package/docs/element-picker.md +24 -6
- package/docs/getting-started.md +47 -1
- package/docs/images/tutorial-01-overview.png +0 -0
- package/docs/images/tutorial-02-history-dropdown.png +0 -0
- package/docs/images/tutorial-03-pick-element.png +0 -0
- package/docs/images/tutorial-04-annotate.png +0 -0
- package/docs/index.md +11 -7
- package/docs/multi-tab.md +55 -20
- package/docs/network-p0.md +2 -2
- package/docs/panel-context-menu.md +41 -0
- package/docs/t2-structured-gui.md +2 -2
- package/docs/t3-vision.md +6 -1
- package/docs/t4-dynamic-behavior.md +1 -0
- package/docs/tools.md +65 -7
- package/docs/tutorial.md +125 -0
- package/docs/verification.md +26 -2
- package/docs/viewport-control.md +21 -8
- package/index.js +3263 -251
- package/integration/cross-environment.js +1535 -0
- package/integration/diagnose.js +945 -0
- package/integration/tab-principal.js +493 -0
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/network-p0.js +24 -7
- package/package.json +4 -3
- package/select-control.js +298 -0
- package/skills/ui-testing-common/SKILL.md +54 -0
- package/skills/ui-testing-fake/SKILL.md +43 -0
- package/skills/{ui-testing → ui-testing-web}/SKILL.md +64 -24
- package/skills/{ui-testing → ui-testing-web}/manifests/declared-capabilities.json +9 -2
- package/skills/{ui-testing → ui-testing-web}/references/contracts/capability.md +3 -3
- package/skills/{ui-testing → ui-testing-web}/references/contracts/evidence.md +1 -1
- package/skills/{ui-testing → ui-testing-web}/references/contracts/operation.md +1 -1
- package/skills/{ui-testing → ui-testing-web}/references/core/fallback.md +1 -1
- package/skills/{ui-testing → ui-testing-web}/references/core/page-identity.md +3 -2
- package/skills/{ui-testing → ui-testing-web}/references/core/selection.md +1 -1
- package/skills/{ui-testing → ui-testing-web}/references/core/tool-routing-and-limits.md +6 -3
- package/skills/{ui-testing → ui-testing-web}/references/platforms/index.md +1 -1
- package/skills/{ui-testing → ui-testing-web}/references/platforms/web.md +2 -2
- package/skills/ui-testing-web/references/scenarios/annotations.md +104 -0
- package/skills/{ui-testing → ui-testing-web}/references/scenarios/functional.md +1 -1
- package/skills/{ui-testing → ui-testing-web}/references/scenarios/visual-motion.md +1 -1
- package/skills/{ui-testing → ui-testing-web}/references/tiers/t1-programmatic.md +2 -2
- package/skills/{ui-testing → ui-testing-web}/references/tiers/t3-static-visual.md +2 -2
- package/skills/{ui-testing → ui-testing-web}/references/tiers/t4-dynamic-visual.md +2 -1
- package/targets/fake/adapter.js +1163 -0
- package/targets/fake/fixtures.js +177 -0
- package/targets/web/adapter.js +1231 -0
- package/targets/web/provider.js +415 -0
- package/skills/ui-testing/references/scenarios/annotations.md +0 -87
- /package/skills/{ui-testing → ui-testing-web}/references/core/dynamic-visual.md +0 -0
- /package/skills/{ui-testing → ui-testing-web}/references/core/network-and-state.md +0 -0
- /package/skills/{ui-testing → ui-testing-web}/references/core/report-template.md +0 -0
- /package/skills/{ui-testing → ui-testing-web}/references/core/reporting.md +0 -0
- /package/skills/{ui-testing → ui-testing-web}/references/core/safety.md +0 -0
- /package/skills/{ui-testing → ui-testing-web}/references/core/verdict-examples.md +0 -0
- /package/skills/{ui-testing → ui-testing-web}/references/core/verdict-gate.md +0 -0
- /package/skills/{ui-testing → ui-testing-web}/references/core/verification.md +0 -0
- /package/skills/{ui-testing → ui-testing-web}/references/core/workflow.md +0 -0
- /package/skills/{ui-testing → ui-testing-web}/references/tiers/t2-structured.md +0 -0
- /package/skills/{ui-testing → ui-testing-web}/templates/capability-note.md +0 -0
- /package/skills/{ui-testing → ui-testing-web}/templates/platform.md +0 -0
- /package/skills/{ui-testing → ui-testing-web}/templates/regression-case.md +0 -0
- /package/skills/{ui-testing → ui-testing-web}/templates/scenario.md +0 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic, non-Web fixtures for the test-only fake environment.
|
|
3
|
+
*
|
|
4
|
+
* Everything here is pure and dependency-free (node: builtins only):
|
|
5
|
+
* - stable JSON (sorted keys) and sha256 helpers,
|
|
6
|
+
* - a minimal PNG encoder built by hand (zlib deflate for IDAT, own CRC32),
|
|
7
|
+
* - deterministic id helpers.
|
|
8
|
+
*
|
|
9
|
+
* No browser, no network, no repository writes.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { createHash } from 'node:crypto';
|
|
13
|
+
import { deflateSync } from 'node:zlib';
|
|
14
|
+
|
|
15
|
+
/** Lowercase sha256 hex digest of bytes/strings. */
|
|
16
|
+
export function sha256Hex(value) {
|
|
17
|
+
return createHash('sha256').update(value).digest('hex');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Stable JSON: object keys are sorted, undefined-valued keys are skipped.
|
|
22
|
+
* Same input -> same string, independent of property insertion order.
|
|
23
|
+
*/
|
|
24
|
+
export function canonicalJson(value) {
|
|
25
|
+
if (value === null || typeof value !== 'object') {
|
|
26
|
+
const encoded = JSON.stringify(value);
|
|
27
|
+
return encoded === undefined ? 'null' : encoded;
|
|
28
|
+
}
|
|
29
|
+
if (Array.isArray(value)) {
|
|
30
|
+
return `[${value.map((item) => canonicalJson(item)).join(',')}]`;
|
|
31
|
+
}
|
|
32
|
+
const parts = [];
|
|
33
|
+
for (const key of Object.keys(value).sort()) {
|
|
34
|
+
if (value[key] === undefined) continue;
|
|
35
|
+
parts.push(`${JSON.stringify(key)}:${canonicalJson(value[key])}`);
|
|
36
|
+
}
|
|
37
|
+
return `{${parts.join(',')}}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Deterministic id: `${seed}:${parts.join(':')}`. */
|
|
41
|
+
export function fakeId(seed, ...parts) {
|
|
42
|
+
return [seed, ...parts.map((part) => String(part))].join(':');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Deterministic short digest id (16 hex chars) derived from seed + parts. */
|
|
46
|
+
export function fakeDigest(seed, ...parts) {
|
|
47
|
+
return sha256Hex(Buffer.from([seed, ...parts.map((part) => String(part))].join('\u0000'), 'utf8')).slice(0, 16);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Minimal PNG encoder (RGBA, bit depth 8, no interlace)
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
const CRC_TABLE = (() => {
|
|
55
|
+
const table = new Uint32Array(256);
|
|
56
|
+
for (let n = 0; n < 256; n += 1) {
|
|
57
|
+
let c = n;
|
|
58
|
+
for (let k = 0; k < 8; k += 1) {
|
|
59
|
+
c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1);
|
|
60
|
+
}
|
|
61
|
+
table[n] = c >>> 0;
|
|
62
|
+
}
|
|
63
|
+
return table;
|
|
64
|
+
})();
|
|
65
|
+
|
|
66
|
+
/** CRC32 over a byte buffer (PNG chunk CRC, IEEE polynomial). */
|
|
67
|
+
export function crc32(bytes) {
|
|
68
|
+
let crc = 0xffffffff;
|
|
69
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
70
|
+
crc = CRC_TABLE[(crc ^ bytes[index]) & 0xff] ^ (crc >>> 8);
|
|
71
|
+
}
|
|
72
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** One PNG chunk: length, type, data, CRC(type + data). */
|
|
76
|
+
export function pngChunk(type, data) {
|
|
77
|
+
const typeBytes = Buffer.from(type, 'ascii');
|
|
78
|
+
const body = Buffer.concat([typeBytes, data]);
|
|
79
|
+
const chunk = Buffer.alloc(12 + data.length);
|
|
80
|
+
chunk.writeUInt32BE(data.length, 0);
|
|
81
|
+
typeBytes.copy(chunk, 4);
|
|
82
|
+
data.copy(chunk, 8);
|
|
83
|
+
chunk.writeUInt32BE(crc32(body), 8 + data.length);
|
|
84
|
+
return chunk;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Default 2x2 RGBA pixels (opaque red / green / blue / white). */
|
|
88
|
+
export const DEFAULT_PNG_PIXELS = Object.freeze([
|
|
89
|
+
255, 0, 0, 255,
|
|
90
|
+
0, 255, 0, 255,
|
|
91
|
+
0, 0, 255, 255,
|
|
92
|
+
255, 255, 255, 255,
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Build a deterministic, valid PNG.
|
|
97
|
+
* @param {{width?:number, height?:number, pixels?:number[]|Uint8Array}} [options]
|
|
98
|
+
* @returns {{bytes:Buffer, width:number, height:number, rgba:Buffer, sha256:string}}
|
|
99
|
+
*/
|
|
100
|
+
export function createDeterministicPng({ width = 2, height = 2, pixels = DEFAULT_PNG_PIXELS } = {}) {
|
|
101
|
+
if (!Number.isInteger(width) || width < 1) throw new TypeError('createDeterministicPng: width must be an integer >= 1');
|
|
102
|
+
if (!Number.isInteger(height) || height < 1) throw new TypeError('createDeterministicPng: height must be an integer >= 1');
|
|
103
|
+
const pixelBytes = width * height * 4;
|
|
104
|
+
const rgba = Buffer.alloc(pixelBytes);
|
|
105
|
+
if (pixels instanceof Uint8Array) {
|
|
106
|
+
rgba.set(pixels.subarray(0, Math.min(pixels.length, pixelBytes)));
|
|
107
|
+
} else {
|
|
108
|
+
for (let index = 0; index < Math.min(pixels.length, pixelBytes); index += 1) rgba[index] = pixels[index] & 0xff;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const signature = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
112
|
+
const ihdr = Buffer.alloc(13);
|
|
113
|
+
ihdr.writeUInt32BE(width, 0);
|
|
114
|
+
ihdr.writeUInt32BE(height, 4);
|
|
115
|
+
ihdr[8] = 8; // bit depth
|
|
116
|
+
ihdr[9] = 6; // color type: RGBA
|
|
117
|
+
ihdr[10] = 0; // compression
|
|
118
|
+
ihdr[11] = 0; // filter
|
|
119
|
+
ihdr[12] = 0; // interlace
|
|
120
|
+
|
|
121
|
+
const stride = width * 4;
|
|
122
|
+
const raw = Buffer.alloc((stride + 1) * height);
|
|
123
|
+
for (let row = 0; row < height; row += 1) {
|
|
124
|
+
const offset = row * (stride + 1);
|
|
125
|
+
raw[offset] = 0; // filter type: none
|
|
126
|
+
rgba.copy(raw, offset + 1, row * stride, (row + 1) * stride);
|
|
127
|
+
}
|
|
128
|
+
const idat = deflateSync(raw, { level: 9 });
|
|
129
|
+
|
|
130
|
+
const bytes = Buffer.concat([
|
|
131
|
+
signature,
|
|
132
|
+
pngChunk('IHDR', ihdr),
|
|
133
|
+
pngChunk('IDAT', idat),
|
|
134
|
+
pngChunk('IEND', Buffer.alloc(0)),
|
|
135
|
+
]);
|
|
136
|
+
return { bytes, width, height, rgba, sha256: sha256Hex(bytes) };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Parse enough of a PNG to verify structure: signature, IHDR dimensions and
|
|
141
|
+
* every chunk CRC. Used by tests as a minimal structural check.
|
|
142
|
+
* @param {Buffer|Uint8Array} bytes
|
|
143
|
+
*/
|
|
144
|
+
export function inspectPng(bytes) {
|
|
145
|
+
const buffer = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes);
|
|
146
|
+
const signature = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
147
|
+
const validSignature = buffer.length >= 8 && buffer.subarray(0, 8).equals(signature);
|
|
148
|
+
const chunks = [];
|
|
149
|
+
let offset = 8;
|
|
150
|
+
while (offset + 12 <= buffer.length) {
|
|
151
|
+
const length = buffer.readUInt32BE(offset);
|
|
152
|
+
const type = buffer.subarray(offset + 4, offset + 8).toString('ascii');
|
|
153
|
+
const dataStart = offset + 8;
|
|
154
|
+
const dataEnd = dataStart + length;
|
|
155
|
+
if (dataEnd + 4 > buffer.length) break;
|
|
156
|
+
const expected = buffer.readUInt32BE(dataEnd);
|
|
157
|
+
const actual = crc32(buffer.subarray(offset + 4, dataEnd));
|
|
158
|
+
chunks.push({ type, length, crcOk: expected === actual, data: buffer.subarray(dataStart, dataEnd) });
|
|
159
|
+
offset = dataEnd + 4;
|
|
160
|
+
if (type === 'IEND') break;
|
|
161
|
+
}
|
|
162
|
+
const ihdr = chunks.find((chunk) => chunk.type === 'IHDR');
|
|
163
|
+
const width = ihdr ? ihdr.data.readUInt32BE(0) : 0;
|
|
164
|
+
const height = ihdr ? ihdr.data.readUInt32BE(4) : 0;
|
|
165
|
+
const bitDepth = ihdr ? ihdr.data[8] : 0;
|
|
166
|
+
const colorType = ihdr ? ihdr.data[9] : 0;
|
|
167
|
+
return {
|
|
168
|
+
validSignature,
|
|
169
|
+
chunks,
|
|
170
|
+
width,
|
|
171
|
+
height,
|
|
172
|
+
bitDepth,
|
|
173
|
+
colorType,
|
|
174
|
+
crcOk: chunks.length > 0 && chunks.every((chunk) => chunk.crcOk),
|
|
175
|
+
idat: chunks.filter((chunk) => chunk.type === 'IDAT').map((chunk) => chunk.data),
|
|
176
|
+
};
|
|
177
|
+
}
|