import { assert } from "chai"; import getCssSelector from "../src/index.js"; import { getAttributeSelectors } from "../src/selector-attribute.js"; import { CssSelectorGenerated } from "../src/types"; describe("selector - attribute", function () { let root: Element; beforeEach(function () { root = document.body.appendChild(document.createElement("div")); }); afterEach(function () { root.parentNode.removeChild(root); }); it("should generate attribute selectors", function () { root.innerHTML = '
'; const result = getAttributeSelectors([root.firstElementChild]); assert.sameMembers(result, [ "[aaa]", "[aaa='bbb']", "[ccc]", "[ccc='ddd']", ]); }); it("should put simplified selector before full selector", () => { root.innerHTML = ''; const result = getAttributeSelectors([root.firstElementChild]); assert.sameOrderedMembers(result, ["[aaa]", "[aaa='bbb']"]); }); it("should ignore ID attribute", function () { root.innerHTML = ''; const result = getAttributeSelectors([root.firstElementChild]); assert.lengthOf(result, 0); }); it("should ignore class attribute", function () { root.innerHTML = ''; const result = getAttributeSelectors([root.firstElementChild]); assert.lengthOf(result, 0); }); it("should sanitize attribute values", function () { root.innerHTML = ''; const result = getAttributeSelectors([root.firstElementChild]); const expectation = "[aaa='bbb\\ ccc']" as CssSelectorGenerated; assert.include(result, expectation); }); it("should quote attribute values", function () { root.innerHTML = ''; const result = getAttributeSelectors([root.firstElementChild]); const expectation = "[aaa='bbb\\:ccc']" as CssSelectorGenerated; assert.include(result, expectation); }); it("should ignore Angular attributes", function () { root.innerHTML = ''; const result = getAttributeSelectors([root.firstElementChild]); assert.equal(result.length, 0); }); it("should generate attribute selectors for multiple elements", function () { root.innerHTML = ` `; const elements = root.querySelectorAll("div"); const withIntersection = getAttributeSelectors([elements[0], elements[1]]); const noIntersection = getAttributeSelectors([elements[0], elements[2]]); assert.sameMembers(withIntersection, ["[aaa]", "[aaa='bbb']"]); assert.sameMembers(noIntersection, []); }); it("should prefer simplified selector if possible", () => { root.innerHTML = ` `; const result = getCssSelector(root.firstElementChild); assert.equal(result, "[aaa]"); }); it("should use full selector", () => { root.innerHTML = ` `; const result = getCssSelector(root.firstElementChild); assert.equal(result, "[aaa='bbb']"); }); it("should use simplified selector for multiple elements", () => { root.innerHTML = ` `; const result = getCssSelector(Array.from(root.children)); assert.equal(result, "[aaa]"); }); it("should escape attribute values", () => { root.innerHTML = ''; const result = getCssSelector(root.firstElementChild); assert.deepEqual(result, "[width='\\33 0\\%']"); }); it("should ignore `value` attribute on INPUT elements", () => { root.innerHTML = ''; const result = getCssSelector(root.firstElementChild); assert.notMatch(result, /^\[/); }); it("should ignore `value` attribute on OPTION elements", () => { root.innerHTML = ''; const result = getCssSelector(root.firstElementChild); assert.notMatch(result, /^\[/); }); it("should escape attributes containing colons and semicolons", () => { root.innerHTML = ''; const element = root.firstElementChild; const selectors = getAttributeSelectors([element]); selectors.forEach((selector) => { assert.doesNotThrow(() => document.querySelector(selector), selector); assert.equal(document.querySelector(selector), element); }); }); it("should ignore base64 encoded 'src' attribute values", () => { root.innerHTML = '