@oslokommune/punkt-testing-utils
Version:
Test-utilities for Punkt
294 lines (293 loc) • 14.1 kB
JavaScript
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