@oslokommune/punkt-testing-utils
Version:
Test-utilities for Punkt
256 lines (255 loc) • 15 kB
JavaScript
(function(global, factory) {
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@testing-library/dom")) : typeof define === "function" && define.amd ? define(["exports", "@testing-library/dom"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.punktTestingUtils = {}, global["dom-testing-library"]));
})(this, function(exports, _testing_library_dom) {
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
//#region src/index.ts
var PKT_CUSTOM_FORMFIELDS = [
"pkt-backlink",
"pkt-checkbox",
"pkt-combobox",
"pkt-progressbar",
"pkt-radiobutton",
"pkt-select",
"pkt-textarea",
"pkt-textinput"
];
var PKT_CUSTOM_ELEMENTS = [
"pkt-input-wrapper",
"pkt-icon",
"pkt-helptext",
"pkt-loader",
"pkt-backlink",
...PKT_CUSTOM_FORMFIELDS
];
var 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)
};
};
var syncQueryFunctionResolver = (testingLibrary) => (queryType) => {
const query = {
text: testingLibrary.getAllByText,
label: testingLibrary.getAllByLabelText,
testid: testingLibrary.getAllByTestId,
displayValue: testingLibrary.getAllByDisplayValue
}[queryType];
if (query) return query;
else throw new Error("Unsupported query type: " + queryType);
};
var asyncQueryFunctionResolver = (testingLibrary) => (queryType) => {
const query = {
text: testingLibrary.findAllByText,
label: testingLibrary.findAllByLabelText,
testid: testingLibrary.findAllByTestId,
displayValue: testingLibrary.findAllByDisplayValue
}[queryType];
if (query) return query;
else throw new Error("Unsupported query type: " + queryType);
};
var syncResolveIdentifierOrElement = (testingLibrary) => (elementOrIdentifier, query) => typeof elementOrIdentifier === "string" || elementOrIdentifier instanceof RegExp ? getPktElementBy(testingLibrary)(query, elementOrIdentifier) : elementOrIdentifier;
var getByLabelElement = (possibleLabelElement) => {
const control = possibleLabelElement instanceof HTMLLabelElement && possibleLabelElement.control;
if (control) return control;
else throw new Error("Fant ikke noe skjema-element for labelen: " + possibleLabelElement.outerHTML);
};
var asyncResolveIdentifierOrElement = (testingLibrary) => async (elementOrIdentifier, query) => elementOrIdentifier instanceof HTMLLabelElement && getByLabelElement(elementOrIdentifier) || (typeof elementOrIdentifier === "string" || elementOrIdentifier instanceof RegExp ? await findPktElementBy(testingLibrary)(query, elementOrIdentifier) : elementOrIdentifier);
var asyncFuncWithStringIdentifierOrElement = (testingLibrary) => (func, query = "label") => {
return async (labelOrPktElement, ...restArgs) => {
return await func(await asyncResolveIdentifierOrElement(testingLibrary)(labelOrPktElement, query), ...restArgs);
};
};
var syncFuncWithStringIdentifierOrElement = (testingLibrary) => (func, query = "label") => {
return (labelOrPktElement, ...restArgs) => {
return func(syncResolveIdentifierOrElement(testingLibrary)(labelOrPktElement, query), ...restArgs);
};
};
var withTestingLibrary = (testingLibrary, fn) => fn(testingLibrary);
/**
* Sørger for at alle brukte custom elements fra Punkt er definerte og erstattet med implementasjoner.
* Bør kalles etter `render`, før noen operasjoner og antakelser gjøres.
*
* F.eks.:
* <code>
* render(<div><PktTextinput id="textinput" name={'textInput'} label="My text input" aria-label="My text input" /></div>);
* await waitForPktElementsToBeDefined();
*
* await setPktElementValue("My text input", "new value");
* </code>
*/
var waitForPktElementsToBeDefined = async () => await Promise.all(PKT_CUSTOM_ELEMENTS.map((elementName) => {
if (document.querySelector(elementName) !== null) return window.customElements.whenDefined(elementName);
}).filter((promise) => promise));
var findPossibleWrappingPktCustomElement = (testingLibrary) => (innerElement, isByRole, args) => {
const pktElement = innerElement?.closest(PKT_CUSTOM_FORMFIELDS.join(", "));
if (pktElement) return pktElement;
else return innerElement;
};
var getPktElementBy = (testingLibrary) => (query = "label", identifier, container) => {
try {
const innerElement = doElementSearch(identifier, query, syncQueryFunctionResolver(testingLibrary)(query), container)[0];
return findPossibleWrappingPktCustomElement(testingLibrary)(innerElement, false, []);
} catch (e) {
return fallbackSearchForPktSelectByLabel(testingLibrary)(identifier, query, container);
}
};
var removeElementBySelector = (ancestor, selector) => {
Array.from(ancestor.querySelectorAll(selector)).forEach((element) => {
element.parentNode?.removeChild(element);
});
};
function getPureLabelText(label) {
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 clonedLabel.textContent?.trim() || null;
}
var 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;
};
var labelMatcher = (labelTextToMatch) => (nodeContent, element) => {
if (element instanceof HTMLElement) return getPureLabelTextForLabelOwner(element) === labelTextToMatch;
else return false;
};
var 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?.control || matchingLabel?.closest("pkt-select") || matchingLabel?.closest("pkt-combobox");
if (!labelOwner) throw (0, _testing_library_dom.getElementError)(`Fant ikke noe element med label "${identifier}"`, container);
return labelOwner;
}
throw (0, _testing_library_dom.getElementError)(`Fant ikke noe element med ${query} "${identifier}"`, container);
};
var doElementSearch = (identifier, query, queryFunc, container = document.body) => {
return identifier instanceof HTMLLabelElement ? getByLabelElement(identifier) : typeof identifier === "string" && query === "label" ? queryFunc(container, labelMatcher(identifier.trim())) : queryFunc(container, identifier);
};
var findPktElementBy = (testingLibrary) => async (query = "label", identifier, container) => {
try {
if (identifier instanceof HTMLLabelElement) return Promise.resolve(getByLabelElement(identifier));
const innerElement = (await doElementSearch(identifier, query, asyncQueryFunctionResolver(testingLibrary)(query), container))[0];
return Promise.resolve(findPossibleWrappingPktCustomElement(testingLibrary)(innerElement, false, []));
} catch (e) {
return fallbackSearchForPktSelectByLabel(testingLibrary)(identifier, query, container);
}
};
var getPktElementByLabel = (testingLibrary) => (label, container) => label instanceof HTMLLabelElement ? getByLabelElement(label) : getPktElementBy(testingLibrary)("label", label, container);
var findPktElementByLabel = (testingLibrary) => async (label, container) => label instanceof HTMLLabelElement ? getByLabelElement(label) : findPktElementBy(testingLibrary)("label", label, container);
var 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);
});
var isPktElementChecked = (testingLibrary) => (target) => {
const element = syncResolveIdentifierOrElement(testingLibrary)(target, "label");
if ("checked" in element) return !!element.checked;
return element.querySelector("input[type=\"radio\"]:checked, input[type=\"checkbox\"]:checked") !== null;
};
var getPktElementByDisplayValue = (testingLibrary) => (text, container) => getPktElementBy(testingLibrary)("displayValue", text, container);
var findPktElementByDisplayValue = (testingLibrary) => (text, container) => findPktElementBy(testingLibrary)("displayValue", text, container);
var 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) => [
optionElement.value,
optionElement.textContent?.trim(),
optionElement.value === currentValue
]).filter(filter);
});
var getAllPktElementsByLabelText = (testingLibrary) => (label, container) => {
return testingLibrary.getAllByLabelText(container || document.body, label).map((element) => findPossibleWrappingPktCustomElement(testingLibrary)(element, false, []));
};
var setPktElementValue = (testingLibrary) => asyncFuncWithStringIdentifierOrElement(testingLibrary)(async (element, valueOrValues, optionsOrUseInputEvent = false) => {
const options = typeof optionsOrUseInputEvent === "object" ? optionsOrUseInputEvent : optionsOrUseInputEvent && {
useInputEventForNative: true,
useChangeEventForNative: true,
setValuePropForNative: true
} || {
useInputEventForNative: false,
useChangeEventForNative: true,
setValuePropForNative: 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 multiple = element.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 (options.setValuePropForNative) {
if ("value" in element) element.value = valueOrValues.toString();
}
if (options.useInputEventForNative) testingLibrary.fireEvent.input(element, { target: { value: valueOrValues } });
if (options.useChangeEventForNative) testingLibrary.fireEvent.change(element, { target: { value: valueOrValues } });
return Promise.resolve(true);
}
});
var containsRadioInput = (element) => {
if (element.tagName === "INPUT" && element.type === "radio") return true;
if (element.querySelector?.("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;
};
var 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");
var 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));
});
//#endregion
exports.PKT_CUSTOM_FORMFIELDS = PKT_CUSTOM_FORMFIELDS;
exports.setupPktTestingLibrary = setupPktTestingLibrary;
});
//# sourceMappingURL=punkt-testing-utils.umd.js.map