UNPKG

@uuv/assistant

Version:

UUV Helper used to improve the life of testers and developers by generating cucumber phrases from the GUI.

251 lines (250 loc) 10.8 kB
"use strict"; /** * Software Name : UUV * * SPDX-License-Identifier: MIT * * This software is distributed under the MIT License, * see the "LICENSE" file for more details * * Authors: NJAKO MOLOM Louis Fredice & SERVICAL Stanley * Software description: Make test writing fast, understandable by any human * understanding English or French. */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.InformativeNodesHelper = void 0; class InformativeNodesHelper { constructor() { this.TAGS_WITH_NATIVE_ACCESSIBILITY_DATA = [ "article", "aside", "button", "details", "dialog", "fieldset", "figure", "form", "footer", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hr", "img", "input", "label", "li", "main", "menu", "nav", "ol", "ul", "option", "progress", "section", "select", "summary", "table", "textarea", "tbody", "thead", "tfoot", "td", "th", "tr", ]; // fIXME SSE transformer en role this.INTERESTING_TAGS = ["P", "H1", "H2", "H3", "H4", "H5", "H6", "BUTTON", "A", "LABEL", "STRONG", "DIV", "SPANz"]; this.candidatesWithNativeAccessibleData = document.querySelectorAll(this.TAGS_WITH_NATIVE_ACCESSIBILITY_DATA.join(",")); this.candidatesWithCustomAccessibleData = this.findInformativeElements(document); } /** * Trouve l'élément parent le plus pertinent pour l'analyse d'accessibilité * @param {HTMLElement} element - L'élément de départ * @returns {HTMLElement|null} - L'élément parent pertinent ou null si non trouvé */ // eslint-disable-next-line @typescript-eslint/no-explicit-any findRelevantAccessibilityParent(element) { if (!element || !(element instanceof HTMLElement)) { return null; } // Éléments considérés comme pertinents pour l'accessibilité (par ordre de priorité) const relevantElements = [ // Éléments sectionnels et structurels "section", "article", "aside", "nav", "main", "header", "footer", // Éléments de contenu "p", "div", "span", // Éléments de liste "ul", "ol", "li", "dl", "dt", "dd", // Éléments de formulaire "form", "fieldset", "legend", // Éléments de tableau "table", "thead", "tbody", "tfoot", "tr", "td", "th", // Autres éléments structurels "blockquote", "figure", "figcaption", "details", "summary" ]; // Éléments avec des rôles ARIA pertinents const relevantAriaRoles = [ "banner", "navigation", "main", "complementary", "contentinfo", "region", "article", "section", "group", "list", "listitem", "table", "row", "cell", "columnheader", "rowheader", "form", "search", "dialog", "alertdialog", "alert" ]; let currentElement = element.parentElement; let bestMatch = null; let bestPriority = Infinity; while (currentElement) { const tagName = currentElement.tagName.toLowerCase(); const role = currentElement.getAttribute("role"); // Vérifier si l'élément a un rôle ARIA pertinent if (role && relevantAriaRoles.includes(role)) { const priority = relevantAriaRoles.indexOf(role); if (priority < bestPriority) { bestMatch = currentElement; bestPriority = priority; } } // Vérifier si l'élément est dans notre liste d'éléments pertinents const elementIndex = relevantElements.indexOf(tagName); if (elementIndex !== -1 && elementIndex < bestPriority) { bestMatch = currentElement; bestPriority = elementIndex; } // Vérifier les attributs d'accessibilité importants if (currentElement.hasAttribute("aria-label") || currentElement.hasAttribute("aria-labelledby") || currentElement.hasAttribute("aria-describedby")) { // Donner une priorité plus élevée aux éléments avec des labels d'accessibilité const accessibilityPriority = bestPriority - 0.5; if (accessibilityPriority < bestPriority) { bestMatch = currentElement; bestPriority = accessibilityPriority; } } // Arrêter la recherche si on trouve un élément très pertinent if (["section", "article", "main", "nav", "form"].includes(tagName) || (role && ["main", "navigation", "banner", "contentinfo"].includes(role))) { break; } currentElement = currentElement.parentElement; } return bestMatch; } extractContextForElement(element) { var _a, _b; const siblingText = Array.from(((_a = element.parentElement) === null || _a === void 0 ? void 0 : _a.childNodes) || []) .filter((n) => { var _a; const isTextNode = n.nodeType === 3; return isTextNode && ((_a = n.textContent) === null || _a === void 0 ? void 0 : _a.trim()); }) .map((n) => { var _a; return (_a = n.textContent) === null || _a === void 0 ? void 0 : _a.trim(); }) .join(" "); let fragment = this.getSiblingsFragment(element, 1); const prev = (_b = element.parentElement) === null || _b === void 0 ? void 0 : _b.previousElementSibling; if (prev && this.INTERESTING_TAGS.includes(prev.tagName)) { fragment = prev.outerHTML + "\n" + fragment; } return { parentElement: element.parentElement, htmlContext: element.parentElement.outerHTML, siblingText, }; } getSiblingsFragment(element, maxSiblings = 1) { const parent = element.parentElement; if (!parent) { return element.outerHTML; } const children = Array.from(parent.children); const index = children.indexOf(element); const beforeEls = children.slice(Math.max(0, index - maxSiblings), index); const afterEls = children.slice(index + 1, index + 1 + maxSiblings); function processElement(informativeNodesHelper, el) { if (informativeNodesHelper.INTERESTING_TAGS.includes(el.tagName)) { return el.outerHTML; } if (informativeNodesHelper.isStructuralOnly(el)) { return informativeNodesHelper.extractInterestingChildren(el).join("\n"); } return ""; } const before = beforeEls.map(value => processElement(this, value)).filter(Boolean); const after = afterEls.map(value => processElement(this, value)).filter(Boolean); return [...before, element.outerHTML, ...after].join("\n"); } extractInterestingChildren(el) { const collected = []; for (const child of Array.from(el.children)) { if (this.INTERESTING_TAGS.includes(child.tagName)) { collected.push(child.outerHTML); } else if (this.isStructuralOnly(child)) { collected.push(...this.extractInterestingChildren(child)); } } return collected; } isStructuralOnly(el) { const tag = el.tagName; const isStructuralTag = ["DIV", "SPAN", "SECTION", "MAIN", "BODY"].includes(tag); return isStructuralTag && !this.hasInformativeAttributes(el); } findInformativeElements(root) { const query = `*${this.TAGS_WITH_NATIVE_ACCESSIBILITY_DATA.map(tag => `:not(${tag})`).join("")}`; const nodes = root.querySelectorAll(query); const informative = [...nodes].filter(el => this.hasInformativeAttributes(el)); const children = [...nodes].flatMap(el => Array.from(el.children)); return [...informative, ...children]; } getAvailableNodes() { return [...this.candidatesWithNativeAccessibleData, ...this.candidatesWithCustomAccessibleData]; } getAvailableChildren(node) { return __awaiter(this, void 0, void 0, function* () { if (node.children.length === 1) { return this.getAvailableChildren(node.children[0]); } return this.findInformativeElements(node); }); } hasInformativeAttributes(node) { for (const attr of Array.from(node.attributes)) { const name = attr.name.toLowerCase(); const isAccessibilityAttr = name === "role" || name === "alt" || name === "title" || name === "tabindex" || name === "lang" || name === "scope" || name === "for" || name.startsWith("aria-"); const isTechnicalAttr = name === "data-testid"; if (isAccessibilityAttr || isTechnicalAttr) { return true; } } return false; } getDialogName(node) { var _a, _b, _c; const idAccessibleName = node.getAttribute("aria-labelledby"); if (idAccessibleName) { return (_b = (_a = document.getElementById(idAccessibleName)) === null || _a === void 0 ? void 0 : _a.textContent) !== null && _b !== void 0 ? _b : null; } return (_c = node.getAttribute("aria-label")) !== null && _c !== void 0 ? _c : node.getAttribute("title"); } } exports.InformativeNodesHelper = InformativeNodesHelper;