diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/http-client.js b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/http-client.js index 23a2b6b286..f31856bf10 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/http-client.js +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/http-client.js @@ -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( diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/i18n/index.js b/koha-tmpl/intranet-tmpl/prog/js/vue/i18n/index.js index 163f474bf5..3a2930f7c0 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/vue/i18n/index.js +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/i18n/index.js @@ -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; }); diff --git a/t/cypress/component/FormElement_spec.ts b/t/cypress/component/FormElement_spec.ts new file mode 100644 index 0000000000..5e68ca47fc --- /dev/null +++ b/t/cypress/component/FormElement_spec.ts @@ -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"); + }); +}); diff --git a/t/cypress/support/component-index.html b/t/cypress/support/component-index.html index ac6e79fd83..a6163db6a8 100644 --- a/t/cypress/support/component-index.html +++ b/t/cypress/support/component-index.html @@ -6,6 +6,7 @@ Components App +
diff --git a/t/cypress/support/component.ts b/t/cypress/support/component.ts index 06278124a0..22c5f0c578 100644 --- a/t/cypress/support/component.ts +++ b/t/cypress/support/component.ts @@ -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); }, }); }