Files
action-topology-engine/tests/locator.test.ts
2026-06-16 19:51:23 +08:00

58 lines
1.9 KiB
TypeScript

import assert from "node:assert/strict";
import { test } from "node:test";
import { generateLocators } from "../src/locator.js";
import type { RawActionableElement } from "../src/types.js";
function raw(overrides: Partial<RawActionableElement>): RawActionableElement {
return {
uid: "button_0_Open settings",
candidateIndex: 0,
tag: "button",
role: "button",
accessibleName: "Open settings",
identityText: "Open settings",
text: "Open settings",
label: null,
placeholder: null,
attributes: { "data-testid": "open-settings", id: "openSettings" },
bbox: { x: 0, y: 0, width: 100, height: 30 },
visibility: { visible: true, enabled: true, editable: false, receivesEvents: true },
context: {
dialog: null,
form: null,
section: null,
row: null,
landmark: null,
listitem: null,
},
parameterSurface: null,
evidence: [],
...overrides,
};
}
test("locator generation prioritizes user-facing Playwright locators before CSS fallbacks", () => {
const candidates = generateLocators("el_test", raw({}));
assert.equal(candidates[0].strategy, "role");
assert.equal(candidates[0].framework, "playwright");
assert.equal(candidates.some((candidate) => candidate.strategy === "testId"), true);
assert.equal(candidates.at(-1)?.strategy, "css");
});
test("locator generation keeps CSS as fallback for id and name", () => {
const candidates = generateLocators(
"el_input",
raw({
tag: "input",
role: "textbox",
accessibleName: "Query",
text: null,
label: "Query",
placeholder: "Search customers",
attributes: { id: "queryInput", name: "q" },
}),
);
assert.equal(candidates.some((candidate) => candidate.expression.includes("#queryInput")), true);
assert.equal(candidates.some((candidate) => candidate.expression.includes("[name=")), true);
});