@tooluminati/testing 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/LICENSE +21 -0
  2. package/dist/browser-helpers-RSQLRHHZ.js +8 -0
  3. package/dist/browser-helpers.d.ts +6 -0
  4. package/dist/browser-helpers.d.ts.map +1 -0
  5. package/dist/browser-helpers.js +23 -0
  6. package/dist/chunk-6YAIOYYZ.js +30 -0
  7. package/dist/chunk-EG3BG65X.js +146 -0
  8. package/dist/comparative-proof-QUN7C3KM.js +11 -0
  9. package/dist/comparative-proof.d.ts +22 -0
  10. package/dist/comparative-proof.d.ts.map +1 -0
  11. package/dist/comparative-proof.js +29 -0
  12. package/dist/devtools-mcp-helper.d.ts +17 -0
  13. package/dist/devtools-mcp-helper.d.ts.map +1 -0
  14. package/dist/devtools-mcp-helper.js +34 -0
  15. package/dist/dom-only-inspection.d.ts +13 -0
  16. package/dist/dom-only-inspection.d.ts.map +1 -0
  17. package/dist/dom-only-inspection.js +86 -0
  18. package/dist/dom-only-inspection.test.d.ts +2 -0
  19. package/dist/dom-only-inspection.test.d.ts.map +1 -0
  20. package/dist/dom-only-inspection.test.js +25 -0
  21. package/dist/index.cjs +473 -0
  22. package/dist/index.d.ts +12 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +245 -0
  25. package/dist/install-model-context-mock.d.ts +3 -0
  26. package/dist/install-model-context-mock.d.ts.map +1 -0
  27. package/dist/install-model-context-mock.js +10 -0
  28. package/dist/mock-model-context.d.ts +14 -0
  29. package/dist/mock-model-context.d.ts.map +1 -0
  30. package/dist/mock-model-context.js +45 -0
  31. package/dist/model-context-mock-script.d.ts +2 -0
  32. package/dist/model-context-mock-script.d.ts.map +1 -0
  33. package/dist/model-context-mock-script.js +40 -0
  34. package/dist/webmcp-evals.d.ts +32 -0
  35. package/dist/webmcp-evals.d.ts.map +1 -0
  36. package/dist/webmcp-evals.js +81 -0
  37. package/package.json +50 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,473 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __esm = (fn, res) => function __init() {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ };
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
+
23
+ // src/browser-helpers.ts
24
+ var browser_helpers_exports = {};
25
+ __export(browser_helpers_exports, {
26
+ expectWebMcpTool: () => expectWebMcpTool,
27
+ invokeWebMcpTool: () => invokeWebMcpTool
28
+ });
29
+ async function expectWebMcpTool(page, name) {
30
+ const exists = await page.evaluate(async (toolName) => {
31
+ const context = document.modelContext;
32
+ const tools = await context?.getTools?.() ?? [];
33
+ return tools.some((tool) => tool.name === toolName);
34
+ }, name);
35
+ if (!exists) {
36
+ throw new Error(`Expected WebMCP tool "${name}" to be registered.`);
37
+ }
38
+ }
39
+ async function invokeWebMcpTool(page, name, args) {
40
+ return page.evaluate(
41
+ async ({ toolName, input }) => {
42
+ const context = document.modelContext;
43
+ const tools = await context?.getTools?.() ?? [];
44
+ const tool = tools.find((candidate) => candidate.name === toolName);
45
+ if (!tool || !context?.executeTool) {
46
+ throw new Error(`WebMCP tool "${toolName}" cannot be invoked.`);
47
+ }
48
+ return context.executeTool(tool, JSON.stringify(input));
49
+ },
50
+ { toolName: name, input: args }
51
+ );
52
+ }
53
+ var init_browser_helpers = __esm({
54
+ "src/browser-helpers.ts"() {
55
+ "use strict";
56
+ }
57
+ });
58
+
59
+ // src/dom-only-inspection.ts
60
+ function pushUnique(target, value) {
61
+ const trimmed = value?.trim();
62
+ if (!trimmed || target.includes(trimmed)) {
63
+ return;
64
+ }
65
+ target.push(trimmed);
66
+ }
67
+ function collectDisabledActionReasonsFromDom(root, buttonLabel) {
68
+ const buttons = [...root.querySelectorAll("button")];
69
+ const button = buttons.find(
70
+ (candidate) => candidate.textContent?.includes(buttonLabel)
71
+ );
72
+ if (!button) {
73
+ return {
74
+ label: buttonLabel,
75
+ disabled: false,
76
+ visibleBlockerReasons: []
77
+ };
78
+ }
79
+ const visibleBlockerReasons = [];
80
+ pushUnique(visibleBlockerReasons, button.getAttribute("title"));
81
+ pushUnique(
82
+ visibleBlockerReasons,
83
+ button.getAttribute("aria-label")?.includes("because") ? button.getAttribute("aria-label") : null
84
+ );
85
+ const describedBy = button.getAttribute("aria-describedby");
86
+ if (describedBy) {
87
+ for (const id of describedBy.split(/\s+/)) {
88
+ pushUnique(
89
+ visibleBlockerReasons,
90
+ root.getElementById(id)?.textContent
91
+ );
92
+ }
93
+ }
94
+ const fieldset = button.closest("fieldset");
95
+ if (fieldset) {
96
+ pushUnique(
97
+ visibleBlockerReasons,
98
+ fieldset.querySelector("legend")?.textContent
99
+ );
100
+ }
101
+ const row = button.closest('[data-testid="checkout-row"]');
102
+ if (row) {
103
+ pushUnique(
104
+ visibleBlockerReasons,
105
+ row.querySelector("[data-dom-blocker-hint]")?.textContent
106
+ );
107
+ }
108
+ return {
109
+ label: buttonLabel,
110
+ disabled: button.disabled,
111
+ visibleBlockerReasons
112
+ };
113
+ }
114
+ async function inspectDisabledActionFromDom(page, buttonLabel) {
115
+ return page.evaluate((label) => {
116
+ const buttons = [...document.querySelectorAll("button")];
117
+ const button = buttons.find(
118
+ (candidate) => candidate.textContent?.includes(label)
119
+ );
120
+ if (!button) {
121
+ return {
122
+ label,
123
+ disabled: false,
124
+ visibleBlockerReasons: []
125
+ };
126
+ }
127
+ const visibleBlockerReasons = [];
128
+ const push = (value) => {
129
+ const trimmed = value?.trim();
130
+ if (trimmed && !visibleBlockerReasons.includes(trimmed)) {
131
+ visibleBlockerReasons.push(trimmed);
132
+ }
133
+ };
134
+ push(button.getAttribute("title"));
135
+ const ariaLabel = button.getAttribute("aria-label");
136
+ if (ariaLabel?.includes("because")) {
137
+ push(ariaLabel);
138
+ }
139
+ const describedBy = button.getAttribute("aria-describedby");
140
+ if (describedBy) {
141
+ for (const id of describedBy.split(/\s+/)) {
142
+ push(document.getElementById(id)?.textContent);
143
+ }
144
+ }
145
+ const fieldset = button.closest("fieldset");
146
+ if (fieldset) {
147
+ push(fieldset.querySelector("legend")?.textContent);
148
+ }
149
+ const row = button.closest('[data-testid="checkout-row"]');
150
+ if (row) {
151
+ push(row.querySelector("[data-dom-blocker-hint]")?.textContent);
152
+ }
153
+ return {
154
+ label,
155
+ disabled: button.disabled,
156
+ visibleBlockerReasons
157
+ };
158
+ }, buttonLabel);
159
+ }
160
+ var init_dom_only_inspection = __esm({
161
+ "src/dom-only-inspection.ts"() {
162
+ "use strict";
163
+ }
164
+ });
165
+
166
+ // src/comparative-proof.ts
167
+ var comparative_proof_exports = {};
168
+ __export(comparative_proof_exports, {
169
+ diagnoseCheckoutBlockerFromDomOnly: () => diagnoseCheckoutBlockerFromDomOnly,
170
+ diagnoseCheckoutBlockerFromWebMcp: () => diagnoseCheckoutBlockerFromWebMcp,
171
+ runComparativeProof: () => runComparativeProof
172
+ });
173
+ async function diagnoseCheckoutBlockerFromDomOnly(page, buttonLabel) {
174
+ return inspectDisabledActionFromDom(page, buttonLabel);
175
+ }
176
+ async function diagnoseCheckoutBlockerFromWebMcp(page, actionId, toolName = "why_is_action_unavailable") {
177
+ const result = await invokeWebMcpTool(page, toolName, { actionId });
178
+ return {
179
+ actionId: result.actionId,
180
+ available: result.available,
181
+ reasons: result.reasons ?? []
182
+ };
183
+ }
184
+ async function runComparativeProof(page, options) {
185
+ const domOnly = await diagnoseCheckoutBlockerFromDomOnly(
186
+ page,
187
+ options.buttonLabel
188
+ );
189
+ const webMcp = await diagnoseCheckoutBlockerFromWebMcp(
190
+ page,
191
+ options.actionId,
192
+ options.toolName
193
+ );
194
+ const domBlockerCount = domOnly.visibleBlockerReasons.length;
195
+ const webMcpBlockerCount = webMcp.reasons.length;
196
+ return {
197
+ domOnly,
198
+ webMcp,
199
+ domBlockerCount,
200
+ webMcpBlockerCount,
201
+ webMcpIsStrictlyMoreInformative: domOnly.disabled && domBlockerCount === 0 && webMcpBlockerCount > 0 && !webMcp.available
202
+ };
203
+ }
204
+ var init_comparative_proof = __esm({
205
+ "src/comparative-proof.ts"() {
206
+ "use strict";
207
+ init_browser_helpers();
208
+ init_dom_only_inspection();
209
+ }
210
+ });
211
+
212
+ // src/index.ts
213
+ var index_exports = {};
214
+ __export(index_exports, {
215
+ MODEL_CONTEXT_MOCK_INIT_SCRIPT: () => MODEL_CONTEXT_MOCK_INIT_SCRIPT,
216
+ MockModelContext: () => MockModelContext,
217
+ assertToolSchemaBudgets: () => assertToolSchemaBudgets,
218
+ collectDisabledActionReasonsFromDom: () => collectDisabledActionReasonsFromDom,
219
+ createToolCallEvalFixture: () => createToolCallEvalFixture,
220
+ diagnoseCheckoutBlockerFromDomOnly: () => diagnoseCheckoutBlockerFromDomOnly,
221
+ diagnoseCheckoutBlockerFromWebMcp: () => diagnoseCheckoutBlockerFromWebMcp,
222
+ executeWebMcpTool: () => executeWebMcpTool,
223
+ expectWebMcpTool: () => expectWebMcpTool,
224
+ inspectDisabledActionFromDom: () => inspectDisabledActionFromDom,
225
+ installModelContextMock: () => installModelContextMock,
226
+ invokeWebMcpTool: () => invokeWebMcpTool,
227
+ listWebMcpTools: () => listWebMcpTools,
228
+ runComparativeProof: () => runComparativeProof,
229
+ runTimelineComparativeProof: () => runTimelineComparativeProof,
230
+ runToolSelectionSmokeTest: () => runToolSelectionSmokeTest,
231
+ snapshotRegisteredTools: () => snapshotRegisteredTools
232
+ });
233
+ module.exports = __toCommonJS(index_exports);
234
+ init_browser_helpers();
235
+ init_comparative_proof();
236
+
237
+ // src/devtools-mcp-helper.ts
238
+ function getModelContext(host = document) {
239
+ return host.modelContext;
240
+ }
241
+ async function listWebMcpTools(host = document) {
242
+ const context = getModelContext(host);
243
+ if (!context?.getTools) {
244
+ return [];
245
+ }
246
+ const tools = await context.getTools();
247
+ return tools.map((tool) => ({
248
+ name: tool.name,
249
+ description: tool.description
250
+ }));
251
+ }
252
+ async function executeWebMcpTool(name, args = {}, host = document) {
253
+ const context = getModelContext(host);
254
+ if (!context?.getTools || !context.executeTool) {
255
+ throw new Error(
256
+ "WebMCP model context is unavailable. Use installModelContextMock() in tests."
257
+ );
258
+ }
259
+ const tools = await context.getTools();
260
+ const tool = tools.find((candidate) => candidate.name === name);
261
+ if (!tool) {
262
+ throw new Error(`WebMCP tool not found: ${name}`);
263
+ }
264
+ return context.executeTool(tool, JSON.stringify(args));
265
+ }
266
+
267
+ // src/index.ts
268
+ init_dom_only_inspection();
269
+
270
+ // src/mock-model-context.ts
271
+ var MockModelContext = class extends EventTarget {
272
+ tools = /* @__PURE__ */ new Map();
273
+ invocations = [];
274
+ registerTool(tool, options = {}) {
275
+ if (this.tools.has(tool.name)) {
276
+ throw new Error(`Tool already registered: ${tool.name}`);
277
+ }
278
+ if (options.signal?.aborted) {
279
+ return;
280
+ }
281
+ this.tools.set(tool.name, tool);
282
+ this.dispatchEvent(new Event("toolchange"));
283
+ options.signal?.addEventListener(
284
+ "abort",
285
+ () => {
286
+ this.tools.delete(tool.name);
287
+ this.dispatchEvent(new Event("toolchange"));
288
+ },
289
+ { once: true }
290
+ );
291
+ }
292
+ async getTools() {
293
+ return [...this.tools.values()];
294
+ }
295
+ async executeTool(toolOrName, argsJson = "{}") {
296
+ const name = typeof toolOrName === "string" ? toolOrName : toolOrName.name;
297
+ if (!name) {
298
+ throw new Error("Tool name is required.");
299
+ }
300
+ const tool = this.tools.get(name);
301
+ if (!tool) {
302
+ throw new Error(`Tool not found: ${name}`);
303
+ }
304
+ const args = JSON.parse(argsJson);
305
+ try {
306
+ const result = await tool.execute(args, {
307
+ signal: new AbortController().signal
308
+ });
309
+ this.invocations.push({ name, args, result });
310
+ return result;
311
+ } catch (error) {
312
+ this.invocations.push({ name, args, error });
313
+ throw error;
314
+ }
315
+ }
316
+ };
317
+
318
+ // src/install-model-context-mock.ts
319
+ function installModelContextMock(target = "document") {
320
+ const mock = new MockModelContext();
321
+ const host = target === "document" ? document : navigator;
322
+ Object.defineProperty(host, "modelContext", {
323
+ configurable: true,
324
+ value: mock
325
+ });
326
+ return mock;
327
+ }
328
+
329
+ // src/model-context-mock-script.ts
330
+ var MODEL_CONTEXT_MOCK_INIT_SCRIPT = `
331
+ (() => {
332
+ const tools = new Map();
333
+
334
+ Object.defineProperty(document, 'modelContext', {
335
+ configurable: true,
336
+ value: {
337
+ registerTool(tool, options = {}) {
338
+ if (options.signal?.aborted) {
339
+ return;
340
+ }
341
+
342
+ tools.set(tool.name, tool);
343
+ options.signal?.addEventListener(
344
+ 'abort',
345
+ () => {
346
+ tools.delete(tool.name);
347
+ },
348
+ { once: true },
349
+ );
350
+ },
351
+ async getTools() {
352
+ return [...tools.values()];
353
+ },
354
+ async executeTool(toolOrName, argsJson = '{}') {
355
+ const name =
356
+ typeof toolOrName === 'string'
357
+ ? toolOrName
358
+ : toolOrName?.name;
359
+ const tool = name ? tools.get(name) : undefined;
360
+ if (!tool) {
361
+ throw new Error('Tool not found: ' + name);
362
+ }
363
+
364
+ return tool.execute(JSON.parse(argsJson));
365
+ },
366
+ },
367
+ });
368
+ })();
369
+ `;
370
+
371
+ // src/webmcp-evals.ts
372
+ var BUDGETS = {
373
+ name: 30,
374
+ paramName: 30,
375
+ paramDescription: 150,
376
+ description: 500,
377
+ output: 1500
378
+ };
379
+ function walkSchemaProperties(schema, visit) {
380
+ if (!schema || typeof schema !== "object") {
381
+ return;
382
+ }
383
+ const node = schema;
384
+ const properties = node.properties;
385
+ if (properties && typeof properties === "object") {
386
+ for (const [name, child] of Object.entries(properties)) {
387
+ if (child && typeof child === "object") {
388
+ visit(name, child);
389
+ }
390
+ }
391
+ }
392
+ }
393
+ async function snapshotRegisteredTools(page) {
394
+ return page.evaluate(() => {
395
+ const context = document.modelContext;
396
+ if (!context?.getTools) {
397
+ return [];
398
+ }
399
+ return context.getTools().then(
400
+ (tools) => tools.map((tool) => ({
401
+ name: String(tool.name ?? ""),
402
+ description: String(tool.description ?? ""),
403
+ inputSchema: tool.inputSchema,
404
+ ...tool.annotations ? { annotations: tool.annotations } : {}
405
+ }))
406
+ );
407
+ });
408
+ }
409
+ function createToolCallEvalFixture(tools, cases) {
410
+ return { tools, cases };
411
+ }
412
+ function assertToolSchemaBudgets(tools) {
413
+ const violations = [];
414
+ for (const tool of tools) {
415
+ if (tool.name.length > BUDGETS.name) {
416
+ violations.push(`Tool name too long: ${tool.name}`);
417
+ }
418
+ if (tool.description.length > BUDGETS.description) {
419
+ violations.push(`Description too long: ${tool.name}`);
420
+ }
421
+ walkSchemaProperties(tool.inputSchema, (name, node) => {
422
+ if (name.length > BUDGETS.paramName) {
423
+ violations.push(`Param name too long on ${tool.name}: ${name}`);
424
+ }
425
+ const description = node.description;
426
+ if (typeof description === "string" && description.length > BUDGETS.paramDescription) {
427
+ violations.push(`Param description too long on ${tool.name}.${name}`);
428
+ }
429
+ });
430
+ }
431
+ return violations;
432
+ }
433
+ function runToolSelectionSmokeTest(tools, prompt, expected) {
434
+ const haystack = `${prompt} ${tools.map((t) => t.description).join(" ")}`.toLowerCase();
435
+ return haystack.includes(expected.toLowerCase()) || tools.some((t) => t.name === expected);
436
+ }
437
+ async function runTimelineComparativeProof(page, options) {
438
+ const { runComparativeProof: runComparativeProof2 } = await Promise.resolve().then(() => (init_comparative_proof(), comparative_proof_exports));
439
+ const { invokeWebMcpTool: invokeWebMcpTool2 } = await Promise.resolve().then(() => (init_browser_helpers(), browser_helpers_exports));
440
+ const proof = await runComparativeProof2(page, {
441
+ buttonLabel: options.buttonLabel,
442
+ actionId: options.actionId
443
+ });
444
+ const timeline = await invokeWebMcpTool2(
445
+ page,
446
+ options.timelineTool ?? "get_troubleshooting_timeline",
447
+ {}
448
+ );
449
+ return {
450
+ domBlockerCount: proof.domBlockerCount,
451
+ timelineEventCount: timeline.events?.length ?? 0
452
+ };
453
+ }
454
+ // Annotate the CommonJS export names for ESM import in node:
455
+ 0 && (module.exports = {
456
+ MODEL_CONTEXT_MOCK_INIT_SCRIPT,
457
+ MockModelContext,
458
+ assertToolSchemaBudgets,
459
+ collectDisabledActionReasonsFromDom,
460
+ createToolCallEvalFixture,
461
+ diagnoseCheckoutBlockerFromDomOnly,
462
+ diagnoseCheckoutBlockerFromWebMcp,
463
+ executeWebMcpTool,
464
+ expectWebMcpTool,
465
+ inspectDisabledActionFromDom,
466
+ installModelContextMock,
467
+ invokeWebMcpTool,
468
+ listWebMcpTools,
469
+ runComparativeProof,
470
+ runTimelineComparativeProof,
471
+ runToolSelectionSmokeTest,
472
+ snapshotRegisteredTools
473
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @experimental Tooluminati testing utilities — public APIs may change.
3
+ */
4
+ export * from './browser-helpers';
5
+ export * from './comparative-proof';
6
+ export * from './devtools-mcp-helper';
7
+ export * from './dom-only-inspection';
8
+ export * from './install-model-context-mock';
9
+ export * from './mock-model-context';
10
+ export * from './model-context-mock-script';
11
+ export * from './webmcp-evals';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gBAAgB,CAAC"}