Bug 38201: Add component tests for FormElement

Signed-off-by: Michaela Sieber <michaela.sieber@kit.edu>
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
Matt Blenkinsop 2025-07-30 15:14:03 +01:00 committed by Lucas Gass
parent 761ff5725f
commit 37076455ff
No known key found for this signature in database
GPG Key ID: 60B447398B0D36CA
5 changed files with 244 additions and 5 deletions

View File

@ -1,14 +1,29 @@
import { setError, submitting, submitted } from "../messages";
function _ifDocumentAvailable(callback) {
if (typeof document !== "undefined" && document.getElementById) {
callback();
}
}
class HttpClient {
constructor(options = {}) {
this._baseURL = options.baseURL || "";
this._headers = options.headers || {
"Content-Type": "application/json;charset=utf-8",
};
this.csrf_token = document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content");
this.csrf_token = this._getCsrfToken(options);
}
_getCsrfToken(options) {
let token = null;
_ifDocumentAvailable(() => {
const metaTag = document.querySelector('meta[name="csrf-token"]');
if (metaTag) {
token = metaTag.getAttribute("content");
}
});
return token !== null ? token : options.csrfToken || null;
}
async _fetchJSON(

View File

@ -1,6 +1,6 @@
const methods = ["__", "__x", "__n", "__nx", "__p", "__px", "__np", "__npx"];
if (window.Cypress.testingType === "component") {
if (window?.Cypress?.testingType === "component") {
methods.forEach(method => {
window[method] = string => string;
});

View File

@ -0,0 +1,222 @@
import FormElement from "@koha-vue/components/FormElement.vue";
const resource = {
id: 1,
name: "test",
correct: true,
errorField: "",
};
//form error message
// events
describe("FormElement", () => {
it("should render a label if required, with the correct 'for' attribute", () => {
cy.mount(FormElement, {
props: {
attr: {
name: "name",
label: "Name",
type: "text",
},
resource,
},
});
cy.get("label").should("have.attr", "for", "name");
cy.mount(FormElement, {
props: {
attr: {
name: "name",
label: "Name",
type: "text",
id: "newId",
},
resource,
},
});
cy.get("label").should("have.attr", "for", "newId");
});
it("should mark the field as required", () => {
cy.mount(FormElement, {
props: {
attr: {
name: "name",
label: "Name",
type: "text",
required: true,
},
resource,
},
});
cy.get("label").should("have.class", "required");
cy.get("input").should("have.attr", "required");
cy.get("span").should("have.class", "required");
});
it("should mark the field as disabled", () => {
cy.mount(FormElement, {
props: {
attr: {
name: "name",
label: "Name",
type: "text",
disabled: true,
},
resource,
},
});
cy.get("input").should("have.attr", "disabled");
});
it("should pass a placeholder", () => {
cy.mount(FormElement, {
props: {
attr: {
name: "name",
label: "Name",
type: "text",
placeholder: "This is a placeholder",
},
resource,
},
});
cy.get("input").should(
"have.attr",
"placeholder",
"This is a placeholder"
);
});
it("should render a text type input", () => {
cy.mount(FormElement, {
props: {
attr: {
name: "name",
label: "Name",
type: "text",
},
resource,
},
});
cy.get("input").should("have.attr", "type", "text");
});
it("should render a number type input", () => {
cy.mount(FormElement, {
props: {
attr: {
name: "name",
label: "Name",
type: "number",
},
resource,
},
});
cy.get("input").should("have.attr", "inputmode", "numeric");
});
it("should render a textarea", () => {
cy.mount(FormElement, {
props: {
attr: {
name: "name",
label: "Name",
type: "textarea",
},
resource,
},
});
cy.get("textarea").should("exist");
});
it("should render a checkbox", () => {
cy.mount(FormElement, {
props: {
attr: {
name: "name",
label: "Name",
type: "checkbox",
},
resource,
},
});
cy.get("input").should("have.attr", "type", "checkbox");
});
it("should render a radio input", () => {
cy.mount(FormElement, {
props: {
attr: {
name: "correct",
label: "Name",
type: "radio",
options: [
{ value: true, description: "Correct" },
{ value: false, description: "Incorrect" },
],
},
resource,
},
});
cy.get("#correct_true").should("have.attr", "type", "radio");
cy.get("#correct_true").should("have.attr", "checked");
});
it("should render a select input", () => {
const attr = {
name: "name",
label: "Name",
type: "select",
options: [
{ value: true, description: "Correct" },
{ value: false, description: "Incorrect" },
],
requiredKey: "value",
selectLabel: "description",
onSelected: resource => {
return resource;
},
};
cy.spy(attr, "onSelected").as("onSelected");
cy.mount(FormElement, {
props: {
attr,
resource,
},
});
cy.get("div.v-select").should("exist");
cy.get(".vs__open-indicator").click();
cy.get(".vs__dropdown-option").contains("Correct").click();
cy.get("span.vs__selected").contains("Correct");
cy.get("@onSelected").should(res => {
expect(res).to.be.calledWith(resource);
});
});
it("should render a date input", () => {
cy.mount(FormElement, {
props: {
attr: {
name: "name",
label: "Name",
type: "date",
},
resource,
},
});
cy.get("input").should("have.class", "flatpickr-input");
});
it("should handle a form error validation method", () => {
cy.mount(FormElement, {
props: {
attr: {
name: "errorField",
label: "Error check",
type: "text",
formErrorHandler: value => {
return value.length >= 4;
},
formErrorMessage: "Must be at least 4 characters",
},
resource,
},
});
cy.get("#errorField").type("tes");
cy.get("span.error").contains("Must be at least 4 characters");
cy.get("#errorField").type("t");
cy.get("span.error").should("not.exist");
});
});

View File

@ -6,6 +6,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Components App</title>
</head>
<script>const flatpickr_defaults = {}</script>
<body>
<div data-cy-root></div>
</body>

View File

@ -22,6 +22,7 @@
import { mount } from "cypress/vue";
import i18n from "@koha-vue/i18n";
import { createWebHistory, createRouter } from "vue-router";
import vSelect from "vue-select";
// Augment the Cypress namespace to include type definitions for
// your custom command.
@ -55,7 +56,7 @@ Cypress.Commands.add("mount", (component, options = {}) => {
});
options.global.plugins.push({
install(app) {
app.use(options.router);
app.use(options.router).component("v-select", vSelect);
},
});
}