@rettangoli/vt 1.0.1 → 1.0.2

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/README.md CHANGED
@@ -126,6 +126,7 @@ Step action reference:
126
126
 
127
127
  - `docs/step-actions.md`
128
128
  - canonical format is structured action objects (`- action: ...`); legacy one-line string steps are not supported.
129
+ - `action: select` accepts exactly one of `testId` or `selector` for interaction targeting.
129
130
  - `assert` supports `js` deep-equal checks for object/array values.
130
131
 
131
132
  Screenshot naming:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rettangoli/vt",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Rettangoli Visual Testing",
5
5
  "type": "module",
6
6
  "repository": {
@@ -180,6 +180,27 @@ function requireStructuredString(stepObject, key, actionName) {
180
180
  return value;
181
181
  }
182
182
 
183
+ function resolveStructuredSelectTarget(stepObject, actionName) {
184
+ const hasTestId = Object.prototype.hasOwnProperty.call(stepObject, "testId");
185
+ const hasSelector = Object.prototype.hasOwnProperty.call(stepObject, "selector");
186
+
187
+ if (hasTestId === hasSelector) {
188
+ throw new Error(`Structured action "${actionName}" requires exactly one of \`testId\` or \`selector\`.`);
189
+ }
190
+
191
+ if (hasTestId) {
192
+ return {
193
+ type: "testId",
194
+ value: requireStructuredString(stepObject, "testId", actionName),
195
+ };
196
+ }
197
+
198
+ return {
199
+ type: "selector",
200
+ value: requireStructuredString(stepObject, "selector", actionName),
201
+ };
202
+ }
203
+
183
204
  function requireStructuredNumber(stepObject, key, actionName) {
184
205
  const value = stepObject[key];
185
206
  if (typeof value !== "number" || !Number.isFinite(value)) {
@@ -224,13 +245,13 @@ function normalizeStructuredActionStep(stepObject) {
224
245
  }
225
246
 
226
247
  if (action === "select") {
227
- assertStructuredKeys(stepObject, new Set(["action", "testId", "steps"]), action);
228
- const testId = requireStructuredString(stepObject, "testId", action);
248
+ assertStructuredKeys(stepObject, new Set(["action", "testId", "selector", "steps"]), action);
249
+ const target = resolveStructuredSelectTarget(stepObject, action);
229
250
  if (!Array.isArray(stepObject.steps)) {
230
251
  throw new Error('Structured action "select" requires array `steps`.');
231
252
  }
232
253
  const nestedSteps = stepObject.steps.map((nestedStep) => normalizeStepValue(nestedStep));
233
- return { kind: "block", command: "select", args: [testId], nestedSteps };
254
+ return { kind: "block", command: "select", args: [`${target.type}=${target.value}`], nestedSteps };
234
255
  }
235
256
 
236
257
  if (action === "click" || action === "dblclick" || action === "hover" || action === "rclick") {
@@ -828,22 +849,32 @@ async function assertStructured(page, assertionConfig, selectedElement) {
828
849
  }
829
850
 
830
851
  async function select(page, args) {
831
- const testId = args[0];
832
- if (!testId) {
833
- throw new Error("`select` requires a test id.");
834
- }
835
- const hostElementLocator = page.getByTestId(testId);
836
-
837
- const interactiveElementLocator = hostElementLocator.locator(
838
- 'input, textarea, button, select, a'
839
- ).first();
840
-
852
+ const { named, positional } = parseNamedArgs(args);
853
+ const testId = typeof named.testId === "string" && named.testId.length > 0
854
+ ? named.testId
855
+ : positional[0];
856
+ const selector = typeof named.selector === "string" && named.selector.length > 0
857
+ ? named.selector
858
+ : undefined;
859
+
860
+ if ((testId ? 1 : 0) + (selector ? 1 : 0) !== 1) {
861
+ throw new Error("`select` requires exactly one target: `testId` or `selector`.");
862
+ }
863
+
864
+ const hostElementLocator = selector
865
+ ? page.locator(selector)
866
+ : page.getByTestId(testId);
867
+
868
+ const interactiveElementLocator = hostElementLocator
869
+ .locator('input, textarea, button, select, a')
870
+ .first();
871
+
841
872
  const count = await interactiveElementLocator.count();
842
-
873
+
843
874
  if (count > 0) {
844
875
  return interactiveElementLocator;
845
876
  }
846
-
877
+
847
878
  return hostElementLocator;
848
879
  }
849
880
 
package/src/validation.js CHANGED
@@ -273,11 +273,18 @@ function validateStructuredActionStep(step, stepPath) {
273
273
  }
274
274
 
275
275
  if (action === "select") {
276
- assertNoUnknownStepKeys(step, stepPath, new Set(["action", "testId", "steps"]));
276
+ assertNoUnknownStepKeys(step, stepPath, new Set(["action", "testId", "selector", "steps"]));
277
277
  validateOptionalString(step.testId, `${stepPath}.testId`);
278
+ validateOptionalString(step.selector, `${stepPath}.selector`);
278
279
  assert(
279
- typeof step.testId === "string" && step.testId.trim().length > 0,
280
- `"${stepPath}.testId" is required for action=select.`,
280
+ (
281
+ typeof step.testId === "string"
282
+ && step.testId.trim().length > 0
283
+ ) !== (
284
+ typeof step.selector === "string"
285
+ && step.selector.trim().length > 0
286
+ ),
287
+ `"${stepPath}" for action=select requires exactly one of "testId" or "selector".`,
281
288
  );
282
289
  assert(Array.isArray(step.steps), `"${stepPath}.steps" must be an array for action=select.`);
283
290
  step.steps.forEach((nestedStep, nestedIndex) => {