UNPKG

@oslokommune/punkt-testing-utils

Version:
294 lines (293 loc) 14.1 kB
import { getElementError } from "@testing-library/dom"; const PKT_CUSTOM_FORMFIELDS = [ "pkt-backlink", "pkt-checkbox", "pkt-combobox", "pkt-datepicker", "pkt-progressbar", "pkt-radiobutton", "pkt-select", "pkt-textarea", "pkt-textinput" ]; const PKT_CUSTOM_ELEMENTS = [ "pkt-input-wrapper", "pkt-icon", "pkt-helptext", "pkt-loader", "pkt-backlink", ...PKT_CUSTOM_FORMFIELDS ]; const setupPktTestingLibrary = (options) => { const tools = { fireEvent: options.fireEvent, findAllByLabelText: options.findAllByLabelText, findAllByDisplayValue: options.findAllByDisplayValue, findAllByTestId: options.findAllByTestId, findAllByText: options.findAllByText, getAllByLabelText: options.getAllByLabelText, getAllByDisplayValue: options.getAllByDisplayValue, getAllByTestId: options.getAllByTestId, getAllByText: options.getAllByText }; return { findPktElementByLabelText: withTestingLibrary(tools, findPktElementByLabel), waitForPktElementsToBeDefined, getPktElementByLabelText: withTestingLibrary(tools, getPktElementByLabel), setPktElementChecked: withTestingLibrary(tools, setPktElementChecked), isPktElementChecked: withTestingLibrary(tools, isPktElementChecked), getPktSelectOptions: withTestingLibrary(tools, getPktSelectOptions), getAllPktElementsByLabelText: withTestingLibrary(tools, getAllPktElementsByLabelText), setPktElementValue: withTestingLibrary(tools, setPktElementValue), pktClickButton: withTestingLibrary(tools, pktClickButton), setPktSelectedOptionsByLabel: withTestingLibrary(tools, setPktSelectedOptionsByLabel), getPktElementByDisplayValue: withTestingLibrary(tools, getPktElementByDisplayValue), findPktElementByDisplayValue: withTestingLibrary(tools, findPktElementByDisplayValue) }; }; const syncQueryFunctionResolver = (testingLibrary) => (queryType) => { const queries = { text: testingLibrary.getAllByText, label: testingLibrary.getAllByLabelText, testid: testingLibrary.getAllByTestId, displayValue: testingLibrary.getAllByDisplayValue }; const query = queries[queryType]; if (query) { return query; } else { throw new Error("Unsupported query type: " + queryType); } }; const asyncQueryFunctionResolver = (testingLibrary) => (queryType) => { const queries = { text: testingLibrary.findAllByText, label: testingLibrary.findAllByLabelText, testid: testingLibrary.findAllByTestId, displayValue: testingLibrary.findAllByDisplayValue }; const query = queries[queryType]; if (query) { return query; } else { throw new Error("Unsupported query type: " + queryType); } }; const syncResolveIdentifierOrElement = (testingLibrary) => (elementOrIdentifier, query) => typeof elementOrIdentifier === "string" || elementOrIdentifier instanceof RegExp ? getPktElementBy(testingLibrary)(query, elementOrIdentifier) : elementOrIdentifier; const asyncResolveIdentifierOrElement = (testingLibrary) => async (elementOrIdentifier, query) => typeof elementOrIdentifier === "string" || elementOrIdentifier instanceof RegExp ? await findPktElementBy(testingLibrary)(query, elementOrIdentifier) : elementOrIdentifier; const asyncFuncWithStringIdentifierOrElement = (testingLibrary) => (func, query = "label") => { return async (labelOrPktElement, ...restArgs) => { const element = await asyncResolveIdentifierOrElement(testingLibrary)(labelOrPktElement, query); return await func(element, ...restArgs); }; }; const syncFuncWithStringIdentifierOrElement = (testingLibrary) => (func, query = "label") => { return (labelOrPktElement, ...restArgs) => { const element = syncResolveIdentifierOrElement(testingLibrary)(labelOrPktElement, query); return func(element, ...restArgs); }; }; const withTestingLibrary = (testingLibrary, fn) => fn(testingLibrary); const waitForPktElementsToBeDefined = async () => await Promise.all( PKT_CUSTOM_ELEMENTS.map((elementName) => { if (document.querySelector(elementName) !== null) { return window.customElements.whenDefined(elementName); } }).filter((promise) => promise) ); const findPossibleWrappingPktCustomElement = (testingLibrary) => (innerElement, isByRole, args) => { if (!innerElement === null) { throw getElementError(`Finner ikke noe element med ${isByRole ? "role" : "label"} "${args[0]}"`, document.body); } const pktElement = innerElement == null ? void 0 : innerElement.closest(PKT_CUSTOM_FORMFIELDS.join(", ")); if (pktElement) { return pktElement; } else { return innerElement; } }; const getPktElementBy = (testingLibrary) => (query = "label", identifier, container) => { try { const queryFunc = syncQueryFunctionResolver(testingLibrary)(query); const innerElement = doElementSearch(identifier, query, queryFunc, container)[0]; return findPossibleWrappingPktCustomElement(testingLibrary)(innerElement, false, []); } catch (e) { return fallbackSearchForPktSelectByLabel()(identifier, query, container); } }; const removeElementBySelector = (ancestor, selector) => { const elements = Array.from(ancestor.querySelectorAll(selector)); elements.forEach((element) => { var _a; (_a = element.parentNode) == null ? void 0 : _a.removeChild(element); }); }; function getPureLabelText(label) { var _a; const clonedLabel = label.cloneNode(true); removeElementBySelector(clonedLabel, "pkt-helptext"); removeElementBySelector(clonedLabel, ".pkt-input-suffix"); removeElementBySelector(clonedLabel, ".pkt-input-prefix"); removeElementBySelector(clonedLabel, ".pkt-input-icon"); removeElementBySelector(clonedLabel, ".pkt-input__counter"); removeElementBySelector(clonedLabel, "option"); removeElementBySelector(clonedLabel, "pkt-listbox"); removeElementBySelector(clonedLabel, ".pkt-alert--error"); removeElementBySelector(clonedLabel, ".pkt-tag"); removeElementBySelector(clonedLabel, ".pkt-input-check__input-helptext"); removeElementBySelector(clonedLabel, ".pkt-inputwrapper__helptext"); return ((_a = clonedLabel.textContent) == null ? void 0 : _a.trim()) || null; } const getPureLabelTextForLabelOwner = (labelOwner) => { const label = "labels" in labelOwner && labelOwner.labels instanceof NodeList && labelOwner.labels.length > 0 && labelOwner.labels[0] || ["input", ...PKT_CUSTOM_FORMFIELDS].includes(labelOwner.tagName.toLowerCase()) && labelOwner.querySelector("label") || null; if (label) { return getPureLabelText(label); } else { return null; } }; const labelMatcher = (labelTextToMatch) => (nodeContent, element) => { if (element instanceof HTMLElement) { const labelWithoutHelptext = getPureLabelTextForLabelOwner(element); return labelWithoutHelptext === labelTextToMatch; } else { return false; } }; const fallbackSearchForPktSelectByLabel = (testingLibrary) => (identifier, query, container = document.body) => { if (typeof identifier === "string" && query === "label") { const matchingLabel = Array.from(container.querySelectorAll("label")).find( (labelElement) => getPureLabelText(labelElement) === identifier ); const labelOwner = (matchingLabel == null ? void 0 : matchingLabel.control) || (matchingLabel == null ? void 0 : matchingLabel.closest("pkt-select")) || (matchingLabel == null ? void 0 : matchingLabel.closest("pkt-combobox")); if (!labelOwner) { throw getElementError(`Fant ikke noe element med label "${identifier}"`, container); } return labelOwner; } throw getElementError(`Fant ikke noe element med ${query} "${identifier}"`, container); }; const doElementSearch = (identifier, query, queryFunc, container = document.body) => { return typeof identifier === "string" && query === "label" ? queryFunc(container, labelMatcher(identifier.trim())) : queryFunc(container, identifier); }; const findPktElementBy = (testingLibrary) => async (query = "label", identifier, container) => { try { const queryFunc = asyncQueryFunctionResolver(testingLibrary)(query); const innerElement = (await doElementSearch(identifier, query, queryFunc, container))[0]; return Promise.resolve(findPossibleWrappingPktCustomElement(testingLibrary)(innerElement, false, [])); } catch (e) { return fallbackSearchForPktSelectByLabel()(identifier, query, container); } }; const getPktElementByLabel = (testingLibrary) => (label, container) => getPktElementBy(testingLibrary)("label", label, container); const findPktElementByLabel = (testingLibrary) => async (label, container) => findPktElementBy(testingLibrary)("label", label, container); const setPktElementChecked = (testingLibrary) => asyncFuncWithStringIdentifierOrElement(testingLibrary)(async (element, checked) => { if (!("checked" in element)) { throw new Error('Bare elementer som har en "checked"-attributt støttes'); } let returnValue = false; if (element.tagName === "INPUT") { const htmlInputElement = element; if (htmlInputElement.type === "radio") { if (!checked) { throw new Error("Kan ikke av-velge en <input type='radio'> - prøv å velge en annen radioknapp i samme gruppe"); } else { returnValue = testingLibrary.fireEvent.click(element); await new Promise((resolve) => setTimeout(resolve, 0)); } } else if (htmlInputElement.type === "checkbox") { if (htmlInputElement.checked !== checked) { returnValue = testingLibrary.fireEvent.click(element); } } } else { returnValue = testingLibrary.fireEvent.change(element, { target: { checked } }); } return Promise.resolve(returnValue); }); const isPktElementChecked = (testingLibrary) => (target) => { const element = syncResolveIdentifierOrElement(testingLibrary)(target, "label"); if ("checked" in element) return !!element.checked; const descendant = element.querySelector('input[type="radio"]:checked, input[type="checkbox"]:checked'); return descendant !== null; }; const getPktElementByDisplayValue = (testingLibrary) => (text, container) => getPktElementBy(testingLibrary)("displayValue", text, container); const findPktElementByDisplayValue = (testingLibrary) => (text, container) => findPktElementBy(testingLibrary)("displayValue", text, container); const getPktSelectOptions = (testingLibrary) => syncFuncWithStringIdentifierOrElement(testingLibrary)( (selectElement, onlySelected) => { const optionElements = Array.from( selectElement.querySelectorAll("option:not(.pkt-hide), data:not(.pkt.hide)") ); const filter = onlySelected ? ([, , selected]) => selected : (_) => true; const currentValue = selectElement.value; return optionElements.map( (optionElement) => { var _a; return [optionElement.value, (_a = optionElement.textContent) == null ? void 0 : _a.trim(), optionElement.value === currentValue]; } ).filter(filter); } ); const getAllPktElementsByLabelText = (testingLibrary) => (label, container) => { const innerElements = testingLibrary.getAllByLabelText(container || document.body, label); return innerElements.map((element) => findPossibleWrappingPktCustomElement()(element, false, [])); }; const setPktElementValue = (testingLibrary) => asyncFuncWithStringIdentifierOrElement(testingLibrary)( async (element, valueOrValues, useInputEvent = false) => { if (Array.isArray(valueOrValues) && valueOrValues.length > 1 && element.tagName === "PKT-SELECT") { throw new Error("Multi-verdi <pkt-select> støttes ikke. Bruk <pkt-combobox> i stedet."); } if (element.tagName === "PKT-SELECT" || element.tagName === "PKT-COMBOBOX") { const pktSelect = element; const multiple = pktSelect.multiple; const valueAsArray = Array.isArray(valueOrValues) ? valueOrValues : [valueOrValues]; const newValue = multiple && valueAsArray || valueAsArray.length == 0 && "" || valueAsArray[0]; testingLibrary.fireEvent.change(element, { target: { value: newValue } }); return Promise.resolve(true); } else { if ("value" in element) { element.value = valueOrValues.toString(); } testingLibrary.fireEvent.input(element, { target: { value: valueOrValues } }); testingLibrary.fireEvent.change(element, { target: { value: valueOrValues } }); return Promise.resolve(true); } } ); const containsRadioInput = (element) => { var _a; if (element.tagName === "INPUT" && element.type === "radio") return true; if ((_a = element.querySelector) == null ? void 0 : _a.call(element, 'input[type="radio"]')) return true; if (element.tagName === "LABEL") { const label = element; const control = label.control || (label.htmlFor ? document.getElementById(label.htmlFor) : null); return !!(control && control.type === "radio"); } return false; }; const pktClickButton = (testingLibrary) => asyncFuncWithStringIdentifierOrElement(testingLibrary)(async (element) => { if (containsRadioInput(element)) { throw new Error("Klikk på <pkt-radiobutton> støttes ikke - bruk setPktElementChecked i stedet"); } return testingLibrary.fireEvent.click(element); }, "text"); const setPktSelectedOptionsByLabel = (testingLibrary) => asyncFuncWithStringIdentifierOrElement(testingLibrary)( async (pktSelect, ...desiredOptionLabels) => { const availableOptions = getPktSelectOptions(testingLibrary)(pktSelect); const selectedOptions = desiredOptionLabels.map((optionLabel) => availableOptions.find(([_, label]) => label === optionLabel)).filter((possibleOption) => possibleOption); if (selectedOptions.length !== desiredOptionLabels.length) { throw new Error( "Noen av option'ene finnes ikke i denne komponenten. Du valgte " + JSON.stringify(desiredOptionLabels) + ", mens valgmulighetene er " + JSON.stringify(availableOptions.map(([, label]) => label)) ); } return await setPktElementValue(testingLibrary)( pktSelect, selectedOptions.map(([value]) => value) ); } ); export { PKT_CUSTOM_FORMFIELDS, setupPktTestingLibrary }; //# sourceMappingURL=punkt-testing-utils.es.js.map