Bug 38201: Add component tests for ShowElement

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-29 17:08:06 +01:00 committed by Lucas Gass
parent 083a000d37
commit 0edc8702aa
No known key found for this signature in database
GPG Key ID: 60B447398B0D36CA
7 changed files with 333 additions and 0 deletions

View File

@ -1,4 +1,5 @@
import { defineConfig } from "cypress";
import { devServer } from "cypress-rspack-dev-server";
export default defineConfig({
fixturesFolder: "t/cypress/fixtures",
@ -21,4 +22,16 @@ export default defineConfig({
apiPassword: "koha",
},
},
component: {
devServer(devServerConfig) {
return devServer({
...devServerConfig,
rspackConfig: require("./rspack.config.js")[0],
});
},
indexHtmlFile: "t/cypress/support/component-index.html",
specPattern: "t/cypress/component/**/*.*",
supportFile: "t/cypress/support/component.ts",
},
});

View File

@ -0,0 +1,15 @@
<template>
<h1>{{ testString }}</h1>
<h2>{{ resourceProp }}</h2>
</template>
<script>
export default {
props: {
testString: String,
resourceProp: String,
},
};
</script>
<style></style>

View File

@ -1,5 +1,11 @@
const methods = ["__", "__x", "__n", "__nx", "__p", "__px", "__np", "__npx"];
if (window.Cypress.testingType === "component") {
methods.forEach(method => {
window[method] = string => string;
});
}
const translators = Object.fromEntries(
methods.map(method => [method, (...args) => window[method](...args)])
);

View File

@ -16,6 +16,7 @@
"@koha-community/prettier-plugin-template-toolkit": "^2.0.2",
"@popperjs/core": "^2.11.6",
"@redocly/cli": "^1.25.11",
"@rspack/binding-darwin-arm64": "^1.4.10",
"bootstrap": "^5.3.3",
"css-loader": "^6.6.0",
"cypress": "^12.17.2",
@ -74,6 +75,7 @@
"@vue/compiler-sfc": "^3.2.31",
"browserify": "^17.0.0",
"clean-webpack-plugin": "^4.0.0",
"cypress-rspack-dev-server": "^1.1.0",
"eslint": "^9.12.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",

View File

@ -0,0 +1,246 @@
import ShowElement from "../../../koha-tmpl/intranet-tmpl/prog/js/vue/components/ShowElement.vue";
const resource = {
id: 1,
name: "test",
showName: "For show",
nestedObject: {
nestedProperty: "nested",
},
radioCheck: true,
tableTest: [
{ columnValueOne: "Col 1", columnValueTwo: "Value1" },
{ columnValueOne: "Col 2", columnValueTwo: "Value2" },
{ columnValueOne: "Col 3", columnValueTwo: "Value3" },
],
};
const instancedResource = {
get_lib_from_av: (avCat, value) => {
return "avValue";
},
};
describe("ShowElement", () => {
it("should render a string based on the type and name property of the attribute", () => {
cy.mount(ShowElement, {
props: {
resource,
instancedResource,
attr: {
type: "text",
name: "name",
label: "Name",
},
},
});
cy.get("label").contains("Name");
cy.get("span").contains(resource.name);
});
it("should hide that property if the hidden attribute is set and falsy", () => {
cy.mount(ShowElement, {
props: {
resource,
instancedResource,
attr: {
type: "text",
name: "name",
label: "Name",
hidden: res => false,
},
},
});
cy.get("label").should("not.exist");
});
it("should wrap that property in a link if the link attribute is set", () => {
cy.mount(ShowElement, {
props: {
resource,
instancedResource,
attr: {
type: "text",
name: "name",
label: "Name",
link: {
href: "blah",
},
},
},
});
cy.get("label").contains("Name");
cy.get("a").should("have.attr", "href", "blah");
cy.get("span").contains(resource.name);
});
it("should detect a showElement property and use the data from that", () => {
cy.mount(ShowElement, {
props: {
resource,
instancedResource,
attr: {
type: "text",
name: "name",
label: "Name",
showElement: {
type: "text",
name: "showName",
},
},
},
});
cy.get("label").contains("Name");
cy.get("span").contains(resource.showName);
});
it("should retrieve a value from a nested object if nesting is detected", () => {
cy.mount(ShowElement, {
props: {
resource,
instancedResource,
attr: {
type: "text",
name: "name",
label: "Name",
showElement: {
type: "text",
name: "showName",
value: "nestedObject.nestedProperty",
},
},
},
});
cy.get("label").contains("Name");
cy.get("span").contains(resource.nestedObject.nestedProperty);
});
it("should format the value if the formatValue attribute is set", () => {
cy.mount(ShowElement, {
props: {
resource,
instancedResource,
attr: {
type: "text",
name: "name",
label: "Name",
showElement: {
type: "text",
name: "showName",
format: (attr, resource) => "This has been formatted",
},
},
},
});
cy.get("label").contains("Name");
cy.get("span").contains("This has been formatted");
});
it("should correctly assign the value to a radio type attr", () => {
cy.mount(ShowElement, {
props: {
resource,
instancedResource,
attr: {
type: "text",
name: "name",
label: "Name",
showElement: {
type: "radio",
name: "radioCheck",
value: "radioCheck",
options: [
{ value: true, description: "Correct" },
{ value: false, description: "Incorrect" },
],
},
},
},
});
cy.get("span").contains("Correct");
});
it("should correctly assign the value to a select/av type attr", () => {
cy.mount(ShowElement, {
props: {
resource,
instancedResource,
attr: {
type: "text",
name: "name",
label: "Name",
avCat: "testAv",
showElement: {
type: "select",
},
},
},
});
cy.get("span").contains("avValue");
});
it("should correctly assign the value to boolean based attrs", () => {
cy.mount(ShowElement, {
props: {
resource,
instancedResource,
attr: {
type: "text",
name: "name",
label: "Name",
showElement: {
type: "boolean",
},
},
},
});
cy.get("span").contains("Yes");
});
it("should correctly render a table when required", () => {
cy.mount(ShowElement, {
props: {
resource,
instancedResource,
attr: {
type: "text",
name: "name",
label: "Name",
showElement: {
type: "table",
columnData: "tableTest",
hidden: () => true,
columns: [
{ name: "Column 1", value: "columnValueOne" },
{ name: "Column 2", value: "columnValueTwo" },
],
},
},
},
});
cy.get("table thead th:first").contains("Column 1");
cy.get("table thead th").eq(1).contains("Column 2");
cy.get("table tbody tr:first td:first").contains("Col 1");
cy.get("table tbody tr:first td").eq(1).contains("Value1");
cy.get("table tbody tr:nth-child(2) td:first").contains("Col 2");
cy.get("table tbody tr:nth-child(2) td").eq(1).contains("Value2");
});
it("should render a component if passed a path and assign props", () => {
cy.mount(ShowElement, {
props: {
resource,
instancedResource,
attr: {
type: "text",
name: "name",
showElement: {
type: "component",
hidden: () => true,
componentPath: "./Cypress/ShowElementComponentType.vue",
componentProps: {
testString: {
type: "string",
value: "A test string",
},
resourceProp: {
resourceProperty: "showName",
},
},
},
},
},
});
cy.get("h1").contains("A test string");
cy.get("h2").contains(resource.showName);
});
});

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Components App</title>
</head>
<body>
<div data-cy-root></div>
</body>
</html>

View File

@ -0,0 +1,39 @@
// ***********************************************************
// This example support/component.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
// import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')
import { mount } from "cypress/vue";
// Augment the Cypress namespace to include type definitions for
// your custom command.
// Alternatively, can be defined in cypress/support/component.d.ts
// with a <reference path="./component" /> at the top of your spec.
// declare global {
// namespace Cypress {
// interface Chainable {
// mount: typeof mount
// }
// }
// };
Cypress.Commands.add("mount", mount);
// Example use:
// cy.mount(MyComponent)