@vmz/test 0.1.0 → 0.1.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.
@@ -11,9 +11,7 @@ export const BROWSER_LOCATOR_KINDS = Object.freeze(['role', 'label', 'text', 'te
11
11
  */
12
12
  export function parseActionLocator(action) {
13
13
  const warnings = [];
14
- const loc = action && typeof action.locator === 'object' && action.locator
15
- ? action.locator
16
- : null;
14
+ const loc = action && typeof action.locator === 'object' && action.locator ? action.locator : null;
17
15
  if (loc) {
18
16
  const kind = String(loc.kind || '');
19
17
  if (kind === 'role') {
@@ -75,18 +73,21 @@ export function resolveLocatorInPage(locator, opts = {}) {
75
73
  const root = document.getElementById('app') || document.body;
76
74
  if (!root)
77
75
  return { ok: false, count: 0, actionable: false, reason: '#app missing', index: -1 };
78
- const normalize = (s) => String(s || '').replace(/\s+/g, ' ').trim();
76
+ const normalize = (s) => String(s || '')
77
+ .replace(/\s+/g, ' ')
78
+ .trim();
79
79
  const nameOf = (el) => {
80
80
  const labelled = el.getAttribute('aria-label');
81
81
  if (labelled)
82
82
  return normalize(labelled);
83
+ const id = el.id;
84
+ if (id) {
85
+ const lab = root.querySelector(`label[for="${CSS.escape(id)}"]`);
86
+ if (lab)
87
+ return normalize(lab.textContent || '');
88
+ }
83
89
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
84
- const id = el.id;
85
- if (id) {
86
- const lab = root.querySelector(`label[for="${CSS.escape(id)}"]`);
87
- if (lab)
88
- return normalize(lab.textContent || '');
89
- }
90
+ // already covered by id+for above; keep text fallback below
90
91
  }
91
92
  return normalize(el.innerText || el.textContent || '');
92
93
  };
@@ -165,9 +166,7 @@ export function resolveLocatorInPage(locator, opts = {}) {
165
166
  found = pool.filter((el) => {
166
167
  const n = nameOf(el);
167
168
  const optVal = el.getAttribute('data-vmz-option') || (el instanceof HTMLOptionElement ? el.value : '');
168
- return locator.exact
169
- ? n === want || optVal === want
170
- : n.includes(want) || String(optVal).includes(want);
169
+ return locator.exact ? n === want || optVal === want : n.includes(want) || String(optVal).includes(want);
171
170
  });
172
171
  }
173
172
  else {
@@ -8,7 +8,7 @@ export type ServeHostHandle = {
8
8
  };
9
9
  /**
10
10
  * Resolve author RouteId / pathPattern to a pathname for page.goto.
11
- * Prefers explicit path; then cdn-policy-manifest; then route-realization.
11
+ * Prefers explicit path; then `vmz-deployment.json` pathPattern; then CDN / realization.
12
12
  */
13
13
  export declare function resolveRoutePath(outDir: string, opts: {
14
14
  routeId?: string;
@@ -23,7 +23,7 @@ function freePort() {
23
23
  }
24
24
  /**
25
25
  * Resolve author RouteId / pathPattern to a pathname for page.goto.
26
- * Prefers explicit path; then cdn-policy-manifest; then route-realization.
26
+ * Prefers explicit path; then `vmz-deployment.json` pathPattern; then CDN / realization.
27
27
  */
28
28
  export function resolveRoutePath(outDir, opts) {
29
29
  if (typeof opts.path === 'string' && opts.path.trim()) {
@@ -32,6 +32,24 @@ export function resolveRoutePath(outDir, opts) {
32
32
  const routeId = String(opts.routeId || '').trim();
33
33
  if (!routeId)
34
34
  throw new Error('open/navigate: routeId or path required');
35
+ const depPath = path.join(outDir, 'vmz-deployment.json');
36
+ if (fs.existsSync(depPath)) {
37
+ try {
38
+ const dep = JSON.parse(fs.readFileSync(depPath, 'utf8'));
39
+ const units = Array.isArray(dep.units) ? dep.units : [];
40
+ const pages = units.filter((u) => u.kind === 'page');
41
+ const hit = pages.find((u) => u.routeId === routeId) ||
42
+ pages.find((u) => u.chunkId === routeId) ||
43
+ pages.find((u) => u.chunkId === `pages/${routeId}`) ||
44
+ pages.find((u) => String(u.chunkId || '').endsWith(`/${routeId}`));
45
+ const pattern = String(hit?.pathPattern || '').trim();
46
+ if (pattern)
47
+ return applyParams(pattern, opts.params);
48
+ }
49
+ catch {
50
+ /* fall through */
51
+ }
52
+ }
35
53
  const cdnPath = path.join(outDir, '_vmz', 'cdn-policy-manifest.json');
36
54
  if (fs.existsSync(cdnPath)) {
37
55
  try {
package/dist/browser.js CHANGED
@@ -17,7 +17,7 @@ import net from 'node:net';
17
17
  import os from 'node:os';
18
18
  import path from 'node:path';
19
19
  import { resolveChunkArtifacts } from './compile.js';
20
- import { createArtifactsDir, writeFailureEvidence, writeTimingOnly, } from './browser-evidence.js';
20
+ import { createArtifactsDir, writeFailureEvidence, writeTimingOnly } from './browser-evidence.js';
21
21
  import { isServeHostManifest, resolveRoutePath, startServeHost } from './browser-serve.js';
22
22
  import { defaultClickLocator, parseActionLocator, resolveLocatorInPage, sleep, } from './browser-protocol.js';
23
23
  const MIME = {
@@ -977,8 +977,7 @@ async function fillTarget(page, value) {
977
977
  }
978
978
  async function pressTarget(page, key) {
979
979
  const ok = await page.evaluate((k) => {
980
- const el = document.querySelector('[data-vmz-bh-target="1"]') ||
981
- document.activeElement;
980
+ const el = document.querySelector('[data-vmz-bh-target="1"]') || document.activeElement;
982
981
  if (!el)
983
982
  return false;
984
983
  el.dispatchEvent(new KeyboardEvent('keydown', { key: String(k), bubbles: true }));
@@ -998,6 +997,7 @@ async function pageText(page) {
998
997
  }
999
998
  /**
1000
999
  * Native <select> or listbox/combobox (data-vmz-option / role=option).
1000
+ * Prefer option value (data-vmz-option) then accessible name/label.
1001
1001
  */
1002
1002
  async function selectTarget(page, value, opts = {}) {
1003
1003
  const want = String(value ?? '');
@@ -1015,7 +1015,9 @@ async function selectTarget(page, value, opts = {}) {
1015
1015
  return { ok: true, kind: 'native' };
1016
1016
  }
1017
1017
  // Custom combobox/listbox: open if needed, then click option.
1018
- el.click();
1018
+ const expanded = el.getAttribute('aria-expanded');
1019
+ if (expanded !== 'true')
1020
+ el.click();
1019
1021
  return { ok: true, kind: 'custom' };
1020
1022
  }, want);
1021
1023
  if (!native || !native.ok) {
@@ -1023,6 +1025,16 @@ async function selectTarget(page, value, opts = {}) {
1023
1025
  }
1024
1026
  if (native.kind === 'native')
1025
1027
  return;
1028
+ // Prefer stable option value contract, then accessible name.
1029
+ const byValue = { kind: 'css', selector: `[data-vmz-option="${want.replace(/"/g, '\\"')}"]` };
1030
+ try {
1031
+ await waitForLocator(page, byValue, opts);
1032
+ await clickTarget(page);
1033
+ return;
1034
+ }
1035
+ catch {
1036
+ /* fall through to role=option name */
1037
+ }
1026
1038
  const optionLocator = { kind: 'role', role: 'option', name: want };
1027
1039
  await waitForLocator(page, optionLocator, opts);
1028
1040
  await clickTarget(page);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vmz/test",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "VMZ native test protocol + Compile/Logic/Browser/SSR/Resume/Deployment hosts",
6
6
  "main": "./dist/index.js",
@@ -27,8 +27,8 @@
27
27
  "build": "tsc -p tsconfig.json"
28
28
  },
29
29
  "dependencies": {
30
- "@vmz/core": "0.1.0",
31
- "@vmz/protocol": "0.1.0",
30
+ "@vmz/core": "0.1.1",
31
+ "@vmz/protocol": "0.1.1",
32
32
  "linkedom": "^0.18.13",
33
33
  "puppeteer-core": "^24.11.2"
34
34
  },