@ggui-ai/ui-visual-tester 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fixture-runtime.d.ts.map +1 -1
- package/dist/fixture-runtime.js +1 -100
- package/dist/prime-inputs.d.ts +12 -0
- package/dist/prime-inputs.d.ts.map +1 -0
- package/dist/prime-inputs.js +159 -0
- package/fixture/fixture-runtime.js +141 -87
- package/package.json +9 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixture-runtime.d.ts","sourceRoot":"","sources":["../src/fixture-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,cAAc,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,gBAAgB,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,gBAAgB,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,kBAAkB,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,cAAc,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"fixture-runtime.d.ts","sourceRoot":"","sources":["../src/fixture-runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/fixture-runtime.js
CHANGED
|
@@ -26,6 +26,7 @@ import * as Wire from '@ggui-ai/wire';
|
|
|
26
26
|
// in-repo consumers of the compile pipeline use.
|
|
27
27
|
import { hoistImports, loadModule } from '@ggui-ai/design/module-loader';
|
|
28
28
|
import { rewriteImports, stripMarkers, } from '@ggui-ai/design/rendering';
|
|
29
|
+
import { primeInputs } from './prime-inputs.js';
|
|
29
30
|
function parseActionEntry(raw) {
|
|
30
31
|
if (raw === null || typeof raw !== 'object')
|
|
31
32
|
return null;
|
|
@@ -150,106 +151,6 @@ function collectActionCandidates(root, name, label) {
|
|
|
150
151
|
}
|
|
151
152
|
return { named: [...byValue, ...byText], fallback: rest, disabled, namedDisabled, allClickables: every };
|
|
152
153
|
}
|
|
153
|
-
// #1040: date-like types too — a wizard's first step is routinely gated on one ("preferred follow-up
|
|
154
|
-
// date"), and a Next that never enables hides every step behind it from the probe.
|
|
155
|
-
const TEXT_LIKE_INPUT_TYPES = new Set([
|
|
156
|
-
'text', 'email', 'search', 'url', 'tel', 'number', 'password', '',
|
|
157
|
-
'date', 'time', 'month', 'week', 'datetime-local',
|
|
158
|
-
]);
|
|
159
|
-
function sampleValueFor(el) {
|
|
160
|
-
const type = el instanceof HTMLInputElement ? el.type : 'textarea';
|
|
161
|
-
switch (type) {
|
|
162
|
-
case 'number':
|
|
163
|
-
return '1';
|
|
164
|
-
case 'email':
|
|
165
|
-
return 'probe@example.com';
|
|
166
|
-
case 'url':
|
|
167
|
-
return 'https://example.com';
|
|
168
|
-
case 'tel':
|
|
169
|
-
return '5550100';
|
|
170
|
-
case 'date':
|
|
171
|
-
return '2026-01-15';
|
|
172
|
-
case 'time':
|
|
173
|
-
return '09:30';
|
|
174
|
-
case 'month':
|
|
175
|
-
return '2026-01';
|
|
176
|
-
case 'week':
|
|
177
|
-
return '2026-W03';
|
|
178
|
-
case 'datetime-local':
|
|
179
|
-
return '2026-01-15T09:30';
|
|
180
|
-
default:
|
|
181
|
-
return 'probe';
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
/** Set a value the way React notices it: through the prototype's native setter, then an input + change event. */
|
|
185
|
-
function setNativeValue(el, value) {
|
|
186
|
-
const proto = el instanceof HTMLTextAreaElement
|
|
187
|
-
? HTMLTextAreaElement.prototype
|
|
188
|
-
: el instanceof HTMLSelectElement
|
|
189
|
-
? HTMLSelectElement.prototype
|
|
190
|
-
: HTMLInputElement.prototype;
|
|
191
|
-
const setter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
|
|
192
|
-
if (setter)
|
|
193
|
-
setter.call(el, value);
|
|
194
|
-
else
|
|
195
|
-
el.value = value;
|
|
196
|
-
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
197
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* The generic minimum a user does before pressing Send / Submit / Complete
|
|
201
|
-
* (#1021): give every empty text-like input and textarea a value, every
|
|
202
|
-
* unset select its first real option, every unchecked radio group its first
|
|
203
|
-
* radio. Checkboxes are never touched — they are often the action itself.
|
|
204
|
-
* Returns how many controls were primed.
|
|
205
|
-
*/
|
|
206
|
-
function primeInputs(root) {
|
|
207
|
-
let primed = 0;
|
|
208
|
-
const editable = (el) => !el.disabled && !(el instanceof HTMLSelectElement) ? !el.readOnly : !el.disabled;
|
|
209
|
-
for (const el of Array.from(root.querySelectorAll('input, textarea'))) {
|
|
210
|
-
if (!(el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) || !editable(el))
|
|
211
|
-
continue;
|
|
212
|
-
if (el instanceof HTMLInputElement && !TEXT_LIKE_INPUT_TYPES.has(el.type))
|
|
213
|
-
continue;
|
|
214
|
-
if (el.value.trim().length > 0)
|
|
215
|
-
continue;
|
|
216
|
-
setNativeValue(el, sampleValueFor(el));
|
|
217
|
-
if (el.value.trim().length > 0)
|
|
218
|
-
primed += 1;
|
|
219
|
-
}
|
|
220
|
-
for (const el of Array.from(root.querySelectorAll('select'))) {
|
|
221
|
-
if (!(el instanceof HTMLSelectElement) || el.disabled)
|
|
222
|
-
continue;
|
|
223
|
-
const current = el.options[el.selectedIndex];
|
|
224
|
-
if (current !== undefined && current.value.trim().length > 0)
|
|
225
|
-
continue;
|
|
226
|
-
const first = Array.from(el.options).find((o) => o.value.trim().length > 0 && !o.disabled);
|
|
227
|
-
if (first === undefined)
|
|
228
|
-
continue;
|
|
229
|
-
setNativeValue(el, first.value);
|
|
230
|
-
primed += 1;
|
|
231
|
-
}
|
|
232
|
-
const seenGroups = new Set();
|
|
233
|
-
for (const el of Array.from(root.querySelectorAll('input[type="radio"]'))) {
|
|
234
|
-
if (!(el instanceof HTMLInputElement) || el.disabled)
|
|
235
|
-
continue;
|
|
236
|
-
const group = el.name || '';
|
|
237
|
-
if (seenGroups.has(group))
|
|
238
|
-
continue;
|
|
239
|
-
seenGroups.add(group);
|
|
240
|
-
const members = group
|
|
241
|
-
? Array.from(root.querySelectorAll(`input[type="radio"][name="${CSS.escape(group)}"]`)).filter((m) => m instanceof HTMLInputElement)
|
|
242
|
-
: [el];
|
|
243
|
-
if (members.some((m) => m.checked))
|
|
244
|
-
continue;
|
|
245
|
-
const first = members.find((m) => !m.disabled);
|
|
246
|
-
if (first === undefined)
|
|
247
|
-
continue;
|
|
248
|
-
first.click();
|
|
249
|
-
primed += 1;
|
|
250
|
-
}
|
|
251
|
-
return primed;
|
|
252
|
-
}
|
|
253
154
|
function countNamedDispatches(actionName) {
|
|
254
155
|
return (window.__ggui_test_dispatches__ ?? []).filter((d) => d.actionName === actionName).length;
|
|
255
156
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function sampleValueFor(el: HTMLInputElement | HTMLTextAreaElement): string;
|
|
2
|
+
/** Set a value the way React notices it: through the prototype's native setter, then an input + change event. */
|
|
3
|
+
export declare function setNativeValue(el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, value: string): void;
|
|
4
|
+
/**
|
|
5
|
+
* The generic minimum a user does before pressing Send / Submit / Complete
|
|
6
|
+
* (#1021): give every empty text-like input and textarea a value, every
|
|
7
|
+
* unset select its first real option, every unchecked radio group its first
|
|
8
|
+
* radio. Checkboxes are never touched — they are often the action itself.
|
|
9
|
+
* Returns how many controls were primed.
|
|
10
|
+
*/
|
|
11
|
+
export declare function primeInputs(root: HTMLElement): number;
|
|
12
|
+
//# sourceMappingURL=prime-inputs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prime-inputs.d.ts","sourceRoot":"","sources":["../src/prime-inputs.ts"],"names":[],"mappings":"AAsDA,wBAAgB,cAAc,CAAC,EAAE,EAAE,gBAAgB,GAAG,mBAAmB,GAAG,MAAM,CAmCjF;AAED,iHAAiH;AACjH,wBAAgB,cAAc,CAAC,EAAE,EAAE,gBAAgB,GAAG,mBAAmB,GAAG,iBAAiB,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAWlH;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAoCrD"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// oss/packages/ui-visual-tester/src/prime-inputs.ts
|
|
2
|
+
//
|
|
3
|
+
// Shared input-priming for the "generic minimum a user does before pressing
|
|
4
|
+
// Send / Submit / Complete" (#1021), incl. the #1040 relative-to-now date rule.
|
|
5
|
+
// Extracted from fixture-runtime.ts so BOTH the Playwright fixture runtime and
|
|
6
|
+
// the ui-gen runtime-render action-wiring probe (ggui#1187) prime inputs the
|
|
7
|
+
// SAME way — one typed-values table, no divergence. Pure DOM + Date only (no
|
|
8
|
+
// Playwright), so it is safe to import into the lightweight happy-dom check
|
|
9
|
+
// worker via the `@ggui-ai/ui-visual-tester/prime-inputs` subpath.
|
|
10
|
+
// #1040: date-like types too — a wizard's first step is routinely gated on one ("preferred follow-up
|
|
11
|
+
// date"), and a Next that never enables hides every step behind it from the probe.
|
|
12
|
+
const TEXT_LIKE_INPUT_TYPES = new Set([
|
|
13
|
+
'text', 'email', 'search', 'url', 'tel', 'number', 'password', '',
|
|
14
|
+
'date', 'time', 'month', 'week', 'datetime-local',
|
|
15
|
+
]);
|
|
16
|
+
/**
|
|
17
|
+
* #1040: dates are primed RELATIVE TO NOW — a form routinely refuses a date in the past
|
|
18
|
+
* ("preferred follow-up date"), and a constant sample date is a past date. Thirty days out,
|
|
19
|
+
* clamped into the input's own [min, max] when those are set in the input's format (ISO
|
|
20
|
+
* strings compare lexically). Time-of-day values have no past to fall into.
|
|
21
|
+
*/
|
|
22
|
+
const pad2 = (n) => String(n).padStart(2, '0');
|
|
23
|
+
const partsOf = (at) => ({ y: at.getFullYear(), m: at.getMonth() + 1, d: at.getDate() });
|
|
24
|
+
/** Today and thirty days out, as the parts a date-like value is built from. */
|
|
25
|
+
function todayAndThirtyDaysOut() {
|
|
26
|
+
const now = new Date();
|
|
27
|
+
const at = new Date(now);
|
|
28
|
+
at.setDate(at.getDate() + 30);
|
|
29
|
+
return { today: partsOf(now), later: partsOf(at) };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Clamp a value into the input's [min, max] when each is set in the input's own format
|
|
33
|
+
* (ISO-shaped strings compare lexically). `min` later than the value wins; `max` earlier than
|
|
34
|
+
* the value wins only while it is not before `floor` (today in the same format) — a max in
|
|
35
|
+
* the past is a form that can never be satisfied, and the probe reports that as it is.
|
|
36
|
+
*/
|
|
37
|
+
function clampToRange(el, value, floor, format) {
|
|
38
|
+
let out = value;
|
|
39
|
+
if (format.test(el.min) && el.min > out)
|
|
40
|
+
out = el.min;
|
|
41
|
+
if (format.test(el.max) && el.max < out && el.max >= floor)
|
|
42
|
+
out = el.max;
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
function isoWeekOf(y, m, d) {
|
|
46
|
+
// ISO-8601 week: Thursday of the same week decides the year.
|
|
47
|
+
const date = new Date(Date.UTC(y, m - 1, d));
|
|
48
|
+
const day = date.getUTCDay() || 7;
|
|
49
|
+
date.setUTCDate(date.getUTCDate() + 4 - day);
|
|
50
|
+
const jan1 = new Date(Date.UTC(date.getUTCFullYear(), 0, 1));
|
|
51
|
+
const week = Math.ceil(((date.getTime() - jan1.getTime()) / 86_400_000 + 1) / 7);
|
|
52
|
+
return `${date.getUTCFullYear()}-W${pad2(week)}`;
|
|
53
|
+
}
|
|
54
|
+
export function sampleValueFor(el) {
|
|
55
|
+
if (!(el instanceof HTMLInputElement))
|
|
56
|
+
return 'probe'; // a textarea has no type to switch on
|
|
57
|
+
switch (el.type) {
|
|
58
|
+
case 'number':
|
|
59
|
+
return '1';
|
|
60
|
+
case 'email':
|
|
61
|
+
return 'probe@example.com';
|
|
62
|
+
case 'url':
|
|
63
|
+
return 'https://example.com';
|
|
64
|
+
case 'tel':
|
|
65
|
+
return '5550100';
|
|
66
|
+
case 'date': {
|
|
67
|
+
const { today, later } = todayAndThirtyDaysOut();
|
|
68
|
+
const iso = (p) => `${p.y}-${pad2(p.m)}-${pad2(p.d)}`;
|
|
69
|
+
return clampToRange(el, iso(later), iso(today), /^\d{4}-\d{2}-\d{2}$/);
|
|
70
|
+
}
|
|
71
|
+
case 'time':
|
|
72
|
+
return '09:30';
|
|
73
|
+
case 'month': {
|
|
74
|
+
const { today, later } = todayAndThirtyDaysOut();
|
|
75
|
+
const ym = (p) => `${p.y}-${pad2(p.m)}`;
|
|
76
|
+
return clampToRange(el, ym(later), ym(today), /^\d{4}-\d{2}$/);
|
|
77
|
+
}
|
|
78
|
+
case 'week': {
|
|
79
|
+
const { today, later } = todayAndThirtyDaysOut();
|
|
80
|
+
return clampToRange(el, isoWeekOf(later.y, later.m, later.d), isoWeekOf(today.y, today.m, today.d), /^\d{4}-W\d{2}$/);
|
|
81
|
+
}
|
|
82
|
+
case 'datetime-local': {
|
|
83
|
+
const { today, later } = todayAndThirtyDaysOut();
|
|
84
|
+
const at = (p, hm) => `${p.y}-${pad2(p.m)}-${pad2(p.d)}T${hm}`;
|
|
85
|
+
return clampToRange(el, at(later, '09:30'), at(today, '00:00'), /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/);
|
|
86
|
+
}
|
|
87
|
+
default:
|
|
88
|
+
return 'probe';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** Set a value the way React notices it: through the prototype's native setter, then an input + change event. */
|
|
92
|
+
export function setNativeValue(el, value) {
|
|
93
|
+
const proto = el instanceof HTMLTextAreaElement
|
|
94
|
+
? HTMLTextAreaElement.prototype
|
|
95
|
+
: el instanceof HTMLSelectElement
|
|
96
|
+
? HTMLSelectElement.prototype
|
|
97
|
+
: HTMLInputElement.prototype;
|
|
98
|
+
const setter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
|
|
99
|
+
if (setter)
|
|
100
|
+
setter.call(el, value);
|
|
101
|
+
else
|
|
102
|
+
el.value = value;
|
|
103
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
104
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The generic minimum a user does before pressing Send / Submit / Complete
|
|
108
|
+
* (#1021): give every empty text-like input and textarea a value, every
|
|
109
|
+
* unset select its first real option, every unchecked radio group its first
|
|
110
|
+
* radio. Checkboxes are never touched — they are often the action itself.
|
|
111
|
+
* Returns how many controls were primed.
|
|
112
|
+
*/
|
|
113
|
+
export function primeInputs(root) {
|
|
114
|
+
let primed = 0;
|
|
115
|
+
const editable = (el) => !el.disabled && !(el instanceof HTMLSelectElement) ? !el.readOnly : !el.disabled;
|
|
116
|
+
for (const el of Array.from(root.querySelectorAll('input, textarea'))) {
|
|
117
|
+
if (!(el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) || !editable(el))
|
|
118
|
+
continue;
|
|
119
|
+
if (el instanceof HTMLInputElement && !TEXT_LIKE_INPUT_TYPES.has(el.type))
|
|
120
|
+
continue;
|
|
121
|
+
if (el.value.trim().length > 0)
|
|
122
|
+
continue;
|
|
123
|
+
setNativeValue(el, sampleValueFor(el));
|
|
124
|
+
if (el.value.trim().length > 0)
|
|
125
|
+
primed += 1;
|
|
126
|
+
}
|
|
127
|
+
for (const el of Array.from(root.querySelectorAll('select'))) {
|
|
128
|
+
if (!(el instanceof HTMLSelectElement) || el.disabled)
|
|
129
|
+
continue;
|
|
130
|
+
const current = el.options[el.selectedIndex];
|
|
131
|
+
if (current !== undefined && current.value.trim().length > 0)
|
|
132
|
+
continue;
|
|
133
|
+
const first = Array.from(el.options).find((o) => o.value.trim().length > 0 && !o.disabled);
|
|
134
|
+
if (first === undefined)
|
|
135
|
+
continue;
|
|
136
|
+
setNativeValue(el, first.value);
|
|
137
|
+
primed += 1;
|
|
138
|
+
}
|
|
139
|
+
const seenGroups = new Set();
|
|
140
|
+
for (const el of Array.from(root.querySelectorAll('input[type="radio"]'))) {
|
|
141
|
+
if (!(el instanceof HTMLInputElement) || el.disabled)
|
|
142
|
+
continue;
|
|
143
|
+
const group = el.name || '';
|
|
144
|
+
if (seenGroups.has(group))
|
|
145
|
+
continue;
|
|
146
|
+
seenGroups.add(group);
|
|
147
|
+
const members = group
|
|
148
|
+
? Array.from(root.querySelectorAll(`input[type="radio"][name="${CSS.escape(group)}"]`)).filter((m) => m instanceof HTMLInputElement)
|
|
149
|
+
: [el];
|
|
150
|
+
if (members.some((m) => m.checked))
|
|
151
|
+
continue;
|
|
152
|
+
const first = members.find((m) => !m.disabled);
|
|
153
|
+
if (first === undefined)
|
|
154
|
+
continue;
|
|
155
|
+
first.click();
|
|
156
|
+
primed += 1;
|
|
157
|
+
}
|
|
158
|
+
return primed;
|
|
159
|
+
}
|
|
@@ -20658,7 +20658,7 @@
|
|
|
20658
20658
|
alignItems: "center",
|
|
20659
20659
|
justifyContent: "center",
|
|
20660
20660
|
gap: "var(--ggui-spacing-2, 8px)",
|
|
20661
|
-
borderRadius: "var(--ggui-shape-radius-md, 8px)",
|
|
20661
|
+
borderRadius: "var(--ggui-shape-radius-control, var(--ggui-shape-radius-md, 8px))",
|
|
20662
20662
|
fontWeight: "var(--ggui-font-weight-medium, 500)",
|
|
20663
20663
|
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
20664
20664
|
opacity: isDisabled ? 0.5 : 1,
|
|
@@ -20704,7 +20704,7 @@
|
|
|
20704
20704
|
}, children: [label, required && (0, import_jsx_runtime9.jsx)("span", { "aria-hidden": "true", style: { color: "var(--ggui-color-error-500, #ef4444)", marginLeft: "2px" }, children: "*" })] }), (0, import_jsx_runtime9.jsx)("input", { id: inputId, type, placeholder, value, onChange: (e) => onChange?.(e.target.value), disabled, required, "aria-invalid": hasError || void 0, "aria-describedby": hasMessage ? messageId : void 0, style: {
|
|
20705
20705
|
...sizeStyles2[size],
|
|
20706
20706
|
width: "100%",
|
|
20707
|
-
borderRadius: "var(--ggui-shape-radius-md, 8px)",
|
|
20707
|
+
borderRadius: "var(--ggui-shape-radius-control, var(--ggui-shape-radius-md, 8px))",
|
|
20708
20708
|
border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-outline, #d4d4d8)"}`,
|
|
20709
20709
|
backgroundColor: disabled ? "var(--ggui-color-sunken, #fafafa)" : "var(--ggui-color-sunken, #ffffff)",
|
|
20710
20710
|
color: "var(--ggui-color-onContainer, #18181b)",
|
|
@@ -20738,7 +20738,7 @@
|
|
|
20738
20738
|
}, children: [label, required && (0, import_jsx_runtime10.jsx)("span", { "aria-hidden": "true", style: { color: "var(--ggui-color-error-500, #ef4444)", marginLeft: "2px" }, children: "*" })] }), (0, import_jsx_runtime10.jsx)("textarea", { id: textareaId, placeholder, value, onChange: (e) => onChange?.(e.target.value), rows, disabled, required, maxLength, "aria-invalid": hasError || void 0, "aria-describedby": hasMessage ? messageId : void 0, style: {
|
|
20739
20739
|
padding: "10px 12px",
|
|
20740
20740
|
width: "100%",
|
|
20741
|
-
borderRadius: "var(--ggui-shape-radius-md, 8px)",
|
|
20741
|
+
borderRadius: "var(--ggui-shape-radius-control, var(--ggui-shape-radius-md, 8px))",
|
|
20742
20742
|
border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-outline, #d4d4d8)"}`,
|
|
20743
20743
|
backgroundColor: disabled ? "var(--ggui-color-sunken, #fafafa)" : "var(--ggui-color-sunken, #ffffff)",
|
|
20744
20744
|
color: "var(--ggui-color-onContainer, #18181b)",
|
|
@@ -20778,7 +20778,7 @@
|
|
|
20778
20778
|
}, children: [label, required && (0, import_jsx_runtime11.jsx)("span", { "aria-hidden": "true", style: { color: "var(--ggui-color-error-500, #ef4444)", marginLeft: "2px" }, children: "*" })] }), (0, import_jsx_runtime11.jsxs)("select", { id: selectId, value, onChange: (e) => onChange?.(e.target.value), disabled, required, "aria-invalid": hasError || void 0, "aria-describedby": hasMessage ? messageId : void 0, style: {
|
|
20779
20779
|
...sizeStyles3[size],
|
|
20780
20780
|
width: "100%",
|
|
20781
|
-
borderRadius: "var(--ggui-shape-radius-md, 8px)",
|
|
20781
|
+
borderRadius: "var(--ggui-shape-radius-control, var(--ggui-shape-radius-md, 8px))",
|
|
20782
20782
|
border: `1px solid ${hasError ? "var(--ggui-color-error-500, #ef4444)" : "var(--ggui-color-outline, #d4d4d8)"}`,
|
|
20783
20783
|
backgroundColor: disabled ? "var(--ggui-color-sunken, #fafafa)" : "var(--ggui-color-sunken, #ffffff)",
|
|
20784
20784
|
color: value ? "var(--ggui-color-onContainer, #18181b)" : "var(--ggui-color-onSunken, #52525b)",
|
|
@@ -28618,6 +28618,7 @@
|
|
|
28618
28618
|
if (onDispatchSuppressed) {
|
|
28619
28619
|
try {
|
|
28620
28620
|
onDispatchSuppressed({
|
|
28621
|
+
reason: "duplicate-dispatch",
|
|
28621
28622
|
actionName,
|
|
28622
28623
|
payloadSignature: decision.signature,
|
|
28623
28624
|
payload: data,
|
|
@@ -29372,11 +29373,26 @@
|
|
|
29372
29373
|
return internalSeq;
|
|
29373
29374
|
});
|
|
29374
29375
|
const validateEnvelope = opts.validateEnvelope ?? validateOutboundActionEnvelope;
|
|
29376
|
+
const spentOneShots = /* @__PURE__ */ new Set();
|
|
29375
29377
|
return {
|
|
29376
29378
|
app: opts.app,
|
|
29377
29379
|
render: opts.render,
|
|
29378
29380
|
auth: opts.auth,
|
|
29381
|
+
...opts.onDispatchSuppressed !== void 0 ? { onDispatchSuppressed: opts.onDispatchSuppressed } : {},
|
|
29379
29382
|
dispatch: (actionName, data) => {
|
|
29383
|
+
const actionSpec = opts.getActiveActionSpec();
|
|
29384
|
+
const isOneShot = actionSpec?.[actionName]?.oneShot === true;
|
|
29385
|
+
if (isOneShot && spentOneShots.has(actionName)) {
|
|
29386
|
+
console.warn(`[ggui] dispatch('${actionName}') suppressed: this action is declared oneShot and already fired for this render. A second gesture is NOT sent to the agent \u2014 the submitted control is spent for the render's lifetime. If the action is meant to repeat, remove \`oneShot\` from its contract entry.`);
|
|
29387
|
+
opts.onDispatchSuppressed?.({
|
|
29388
|
+
reason: "one-shot-spent",
|
|
29389
|
+
actionName,
|
|
29390
|
+
payloadSignature: payloadSignature(actionName, data),
|
|
29391
|
+
payload: data,
|
|
29392
|
+
suppressedAt: Date.now()
|
|
29393
|
+
});
|
|
29394
|
+
return;
|
|
29395
|
+
}
|
|
29380
29396
|
const envelope = buildActionEnvelope({
|
|
29381
29397
|
sessionId: opts.render.sessionId,
|
|
29382
29398
|
type: "data:submit",
|
|
@@ -29386,11 +29402,13 @@
|
|
|
29386
29402
|
},
|
|
29387
29403
|
clientSeq: nextClientSeq()
|
|
29388
29404
|
});
|
|
29389
|
-
const result = validateEnvelope(
|
|
29405
|
+
const result = validateEnvelope(actionSpec, envelope);
|
|
29390
29406
|
if (!result.valid) {
|
|
29391
29407
|
opts.onViolation(new ClientContractViolationError("outbound-action", result.violations));
|
|
29392
29408
|
return;
|
|
29393
29409
|
}
|
|
29410
|
+
if (isOneShot)
|
|
29411
|
+
spentOneShots.add(actionName);
|
|
29394
29412
|
opts.emitEnvelope(envelope);
|
|
29395
29413
|
},
|
|
29396
29414
|
subscribe: (channelName, handler) => {
|
|
@@ -29543,6 +29561,7 @@
|
|
|
29543
29561
|
"--ggui-font-weight-normal",
|
|
29544
29562
|
"--ggui-font-weight-semibold",
|
|
29545
29563
|
"--ggui-letter-spacing-heading",
|
|
29564
|
+
"--ggui-shape-radius-control",
|
|
29546
29565
|
"--ggui-shape-radius-full",
|
|
29547
29566
|
"--ggui-shape-radius-lg",
|
|
29548
29567
|
"--ggui-shape-radius-md",
|
|
@@ -32775,6 +32794,123 @@
|
|
|
32775
32794
|
}
|
|
32776
32795
|
}
|
|
32777
32796
|
|
|
32797
|
+
// src/prime-inputs.ts
|
|
32798
|
+
var TEXT_LIKE_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
32799
|
+
"text",
|
|
32800
|
+
"email",
|
|
32801
|
+
"search",
|
|
32802
|
+
"url",
|
|
32803
|
+
"tel",
|
|
32804
|
+
"number",
|
|
32805
|
+
"password",
|
|
32806
|
+
"",
|
|
32807
|
+
"date",
|
|
32808
|
+
"time",
|
|
32809
|
+
"month",
|
|
32810
|
+
"week",
|
|
32811
|
+
"datetime-local"
|
|
32812
|
+
]);
|
|
32813
|
+
var pad2 = (n) => String(n).padStart(2, "0");
|
|
32814
|
+
var partsOf = (at) => ({ y: at.getFullYear(), m: at.getMonth() + 1, d: at.getDate() });
|
|
32815
|
+
function todayAndThirtyDaysOut() {
|
|
32816
|
+
const now = /* @__PURE__ */ new Date();
|
|
32817
|
+
const at = new Date(now);
|
|
32818
|
+
at.setDate(at.getDate() + 30);
|
|
32819
|
+
return { today: partsOf(now), later: partsOf(at) };
|
|
32820
|
+
}
|
|
32821
|
+
function clampToRange(el, value, floor, format) {
|
|
32822
|
+
let out = value;
|
|
32823
|
+
if (format.test(el.min) && el.min > out) out = el.min;
|
|
32824
|
+
if (format.test(el.max) && el.max < out && el.max >= floor) out = el.max;
|
|
32825
|
+
return out;
|
|
32826
|
+
}
|
|
32827
|
+
function isoWeekOf(y, m, d) {
|
|
32828
|
+
const date = new Date(Date.UTC(y, m - 1, d));
|
|
32829
|
+
const day = date.getUTCDay() || 7;
|
|
32830
|
+
date.setUTCDate(date.getUTCDate() + 4 - day);
|
|
32831
|
+
const jan1 = new Date(Date.UTC(date.getUTCFullYear(), 0, 1));
|
|
32832
|
+
const week = Math.ceil(((date.getTime() - jan1.getTime()) / 864e5 + 1) / 7);
|
|
32833
|
+
return `${date.getUTCFullYear()}-W${pad2(week)}`;
|
|
32834
|
+
}
|
|
32835
|
+
function sampleValueFor(el) {
|
|
32836
|
+
if (!(el instanceof HTMLInputElement)) return "probe";
|
|
32837
|
+
switch (el.type) {
|
|
32838
|
+
case "number":
|
|
32839
|
+
return "1";
|
|
32840
|
+
case "email":
|
|
32841
|
+
return "probe@example.com";
|
|
32842
|
+
case "url":
|
|
32843
|
+
return "https://example.com";
|
|
32844
|
+
case "tel":
|
|
32845
|
+
return "5550100";
|
|
32846
|
+
case "date": {
|
|
32847
|
+
const { today, later } = todayAndThirtyDaysOut();
|
|
32848
|
+
const iso = (p) => `${p.y}-${pad2(p.m)}-${pad2(p.d)}`;
|
|
32849
|
+
return clampToRange(el, iso(later), iso(today), /^\d{4}-\d{2}-\d{2}$/);
|
|
32850
|
+
}
|
|
32851
|
+
case "time":
|
|
32852
|
+
return "09:30";
|
|
32853
|
+
case "month": {
|
|
32854
|
+
const { today, later } = todayAndThirtyDaysOut();
|
|
32855
|
+
const ym = (p) => `${p.y}-${pad2(p.m)}`;
|
|
32856
|
+
return clampToRange(el, ym(later), ym(today), /^\d{4}-\d{2}$/);
|
|
32857
|
+
}
|
|
32858
|
+
case "week": {
|
|
32859
|
+
const { today, later } = todayAndThirtyDaysOut();
|
|
32860
|
+
return clampToRange(el, isoWeekOf(later.y, later.m, later.d), isoWeekOf(today.y, today.m, today.d), /^\d{4}-W\d{2}$/);
|
|
32861
|
+
}
|
|
32862
|
+
case "datetime-local": {
|
|
32863
|
+
const { today, later } = todayAndThirtyDaysOut();
|
|
32864
|
+
const at = (p, hm) => `${p.y}-${pad2(p.m)}-${pad2(p.d)}T${hm}`;
|
|
32865
|
+
return clampToRange(el, at(later, "09:30"), at(today, "00:00"), /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/);
|
|
32866
|
+
}
|
|
32867
|
+
default:
|
|
32868
|
+
return "probe";
|
|
32869
|
+
}
|
|
32870
|
+
}
|
|
32871
|
+
function setNativeValue(el, value) {
|
|
32872
|
+
const proto = el instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : el instanceof HTMLSelectElement ? HTMLSelectElement.prototype : HTMLInputElement.prototype;
|
|
32873
|
+
const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;
|
|
32874
|
+
if (setter) setter.call(el, value);
|
|
32875
|
+
else el.value = value;
|
|
32876
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
32877
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
32878
|
+
}
|
|
32879
|
+
function primeInputs(root) {
|
|
32880
|
+
let primed = 0;
|
|
32881
|
+
const editable = (el) => !el.disabled && !(el instanceof HTMLSelectElement) ? !el.readOnly : !el.disabled;
|
|
32882
|
+
for (const el of Array.from(root.querySelectorAll("input, textarea"))) {
|
|
32883
|
+
if (!(el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) || !editable(el)) continue;
|
|
32884
|
+
if (el instanceof HTMLInputElement && !TEXT_LIKE_INPUT_TYPES.has(el.type)) continue;
|
|
32885
|
+
if (el.value.trim().length > 0) continue;
|
|
32886
|
+
setNativeValue(el, sampleValueFor(el));
|
|
32887
|
+
if (el.value.trim().length > 0) primed += 1;
|
|
32888
|
+
}
|
|
32889
|
+
for (const el of Array.from(root.querySelectorAll("select"))) {
|
|
32890
|
+
if (!(el instanceof HTMLSelectElement) || el.disabled) continue;
|
|
32891
|
+
const current = el.options[el.selectedIndex];
|
|
32892
|
+
if (current !== void 0 && current.value.trim().length > 0) continue;
|
|
32893
|
+
const first = Array.from(el.options).find((o) => o.value.trim().length > 0 && !o.disabled);
|
|
32894
|
+
if (first === void 0) continue;
|
|
32895
|
+
setNativeValue(el, first.value);
|
|
32896
|
+
primed += 1;
|
|
32897
|
+
}
|
|
32898
|
+
const seenGroups = /* @__PURE__ */ new Set();
|
|
32899
|
+
for (const el of Array.from(root.querySelectorAll('input[type="radio"]'))) {
|
|
32900
|
+
if (!(el instanceof HTMLInputElement) || el.disabled) continue;
|
|
32901
|
+
const group = el.name || "";
|
|
32902
|
+
if (seenGroups.has(group)) continue;
|
|
32903
|
+
seenGroups.add(group);
|
|
32904
|
+
const members = group ? Array.from(root.querySelectorAll(`input[type="radio"][name="${CSS.escape(group)}"]`)).filter((m) => m instanceof HTMLInputElement) : [el];
|
|
32905
|
+
if (members.some((m) => m.checked)) continue;
|
|
32906
|
+
const first = members.find((m) => !m.disabled);
|
|
32907
|
+
if (first === void 0) continue;
|
|
32908
|
+
first.click();
|
|
32909
|
+
primed += 1;
|
|
32910
|
+
}
|
|
32911
|
+
return primed;
|
|
32912
|
+
}
|
|
32913
|
+
|
|
32778
32914
|
// src/fixture-runtime.ts
|
|
32779
32915
|
function parseActionEntry(raw) {
|
|
32780
32916
|
if (raw === null || typeof raw !== "object") return null;
|
|
@@ -32864,88 +33000,6 @@
|
|
|
32864
33000
|
}
|
|
32865
33001
|
return { named: [...byValue, ...byText], fallback: rest, disabled, namedDisabled, allClickables: every };
|
|
32866
33002
|
}
|
|
32867
|
-
var TEXT_LIKE_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
32868
|
-
"text",
|
|
32869
|
-
"email",
|
|
32870
|
-
"search",
|
|
32871
|
-
"url",
|
|
32872
|
-
"tel",
|
|
32873
|
-
"number",
|
|
32874
|
-
"password",
|
|
32875
|
-
"",
|
|
32876
|
-
"date",
|
|
32877
|
-
"time",
|
|
32878
|
-
"month",
|
|
32879
|
-
"week",
|
|
32880
|
-
"datetime-local"
|
|
32881
|
-
]);
|
|
32882
|
-
function sampleValueFor(el) {
|
|
32883
|
-
const type = el instanceof HTMLInputElement ? el.type : "textarea";
|
|
32884
|
-
switch (type) {
|
|
32885
|
-
case "number":
|
|
32886
|
-
return "1";
|
|
32887
|
-
case "email":
|
|
32888
|
-
return "probe@example.com";
|
|
32889
|
-
case "url":
|
|
32890
|
-
return "https://example.com";
|
|
32891
|
-
case "tel":
|
|
32892
|
-
return "5550100";
|
|
32893
|
-
case "date":
|
|
32894
|
-
return "2026-01-15";
|
|
32895
|
-
case "time":
|
|
32896
|
-
return "09:30";
|
|
32897
|
-
case "month":
|
|
32898
|
-
return "2026-01";
|
|
32899
|
-
case "week":
|
|
32900
|
-
return "2026-W03";
|
|
32901
|
-
case "datetime-local":
|
|
32902
|
-
return "2026-01-15T09:30";
|
|
32903
|
-
default:
|
|
32904
|
-
return "probe";
|
|
32905
|
-
}
|
|
32906
|
-
}
|
|
32907
|
-
function setNativeValue(el, value) {
|
|
32908
|
-
const proto = el instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : el instanceof HTMLSelectElement ? HTMLSelectElement.prototype : HTMLInputElement.prototype;
|
|
32909
|
-
const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;
|
|
32910
|
-
if (setter) setter.call(el, value);
|
|
32911
|
-
else el.value = value;
|
|
32912
|
-
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
32913
|
-
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
32914
|
-
}
|
|
32915
|
-
function primeInputs(root) {
|
|
32916
|
-
let primed = 0;
|
|
32917
|
-
const editable = (el) => !el.disabled && !(el instanceof HTMLSelectElement) ? !el.readOnly : !el.disabled;
|
|
32918
|
-
for (const el of Array.from(root.querySelectorAll("input, textarea"))) {
|
|
32919
|
-
if (!(el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) || !editable(el)) continue;
|
|
32920
|
-
if (el instanceof HTMLInputElement && !TEXT_LIKE_INPUT_TYPES.has(el.type)) continue;
|
|
32921
|
-
if (el.value.trim().length > 0) continue;
|
|
32922
|
-
setNativeValue(el, sampleValueFor(el));
|
|
32923
|
-
if (el.value.trim().length > 0) primed += 1;
|
|
32924
|
-
}
|
|
32925
|
-
for (const el of Array.from(root.querySelectorAll("select"))) {
|
|
32926
|
-
if (!(el instanceof HTMLSelectElement) || el.disabled) continue;
|
|
32927
|
-
const current = el.options[el.selectedIndex];
|
|
32928
|
-
if (current !== void 0 && current.value.trim().length > 0) continue;
|
|
32929
|
-
const first = Array.from(el.options).find((o) => o.value.trim().length > 0 && !o.disabled);
|
|
32930
|
-
if (first === void 0) continue;
|
|
32931
|
-
setNativeValue(el, first.value);
|
|
32932
|
-
primed += 1;
|
|
32933
|
-
}
|
|
32934
|
-
const seenGroups = /* @__PURE__ */ new Set();
|
|
32935
|
-
for (const el of Array.from(root.querySelectorAll('input[type="radio"]'))) {
|
|
32936
|
-
if (!(el instanceof HTMLInputElement) || el.disabled) continue;
|
|
32937
|
-
const group = el.name || "";
|
|
32938
|
-
if (seenGroups.has(group)) continue;
|
|
32939
|
-
seenGroups.add(group);
|
|
32940
|
-
const members = group ? Array.from(root.querySelectorAll(`input[type="radio"][name="${CSS.escape(group)}"]`)).filter((m) => m instanceof HTMLInputElement) : [el];
|
|
32941
|
-
if (members.some((m) => m.checked)) continue;
|
|
32942
|
-
const first = members.find((m) => !m.disabled);
|
|
32943
|
-
if (first === void 0) continue;
|
|
32944
|
-
first.click();
|
|
32945
|
-
primed += 1;
|
|
32946
|
-
}
|
|
32947
|
-
return primed;
|
|
32948
|
-
}
|
|
32949
33003
|
function countNamedDispatches(actionName) {
|
|
32950
33004
|
return (window.__ggui_test_dispatches__ ?? []).filter((d) => d.actionName === actionName).length;
|
|
32951
33005
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ggui-ai/ui-visual-tester",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Behavioral validator for ggui-generated component code. Renders a component in real Chromium via Playwright, clicks each action declared in its contract, and verifies the action actually does something — mutating bound state or dispatching to the agent. Catches buttons wired to no-op handlers that structural validators miss. Playwright is an optional peer dependency, injected by the caller.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ggui",
|
|
@@ -35,6 +35,11 @@
|
|
|
35
35
|
"types": "./dist/index.d.ts",
|
|
36
36
|
"import": "./dist/index.js",
|
|
37
37
|
"default": "./dist/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./prime-inputs": {
|
|
40
|
+
"types": "./dist/prime-inputs.d.ts",
|
|
41
|
+
"import": "./dist/prime-inputs.js",
|
|
42
|
+
"default": "./dist/prime-inputs.js"
|
|
38
43
|
}
|
|
39
44
|
},
|
|
40
45
|
"peerDependencies": {
|
|
@@ -48,9 +53,9 @@
|
|
|
48
53
|
"dependencies": {
|
|
49
54
|
"react": "^19.0.0",
|
|
50
55
|
"react-dom": "^19.0.0",
|
|
51
|
-
"@ggui-ai/design": "0.
|
|
52
|
-
"@ggui-ai/
|
|
53
|
-
"@ggui-ai/
|
|
56
|
+
"@ggui-ai/design": "0.20.0",
|
|
57
|
+
"@ggui-ai/protocol": "0.20.0",
|
|
58
|
+
"@ggui-ai/wire": "0.20.0"
|
|
54
59
|
},
|
|
55
60
|
"devDependencies": {
|
|
56
61
|
"@playwright/test": "^1.60.0",
|