Bugzilla – Attachment 183359 Details for
Bug 40170
Replace cypress-mysql with mysql2
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 40170: Replace cypress-mysql with mysql2 - Cypress tests
Bug-40170-Replace-cypress-mysql-with-mysql2---Cypr.patch (text/plain), 17.76 KB, created by
David Nind
on 2025-06-18 22:37:57 UTC
(
hide
)
Description:
Bug 40170: Replace cypress-mysql with mysql2 - Cypress tests
Filename:
MIME Type:
Creator:
David Nind
Created:
2025-06-18 22:37:57 UTC
Size:
17.76 KB
patch
obsolete
>From 4ae980f1e3ef8bd8d26a63b5af9e68147e886dff Mon Sep 17 00:00:00 2001 >From: Jonathan Druart <jonathan.druart@bugs.koha-community.org> >Date: Thu, 12 Jun 2025 15:42:17 +0200 >Subject: [PATCH] Bug 40170: Replace cypress-mysql with mysql2 - Cypress tests > >This patch suggests to replace cypress-mysql with mysql2 > >cypress-mysql provides a custom Cypress command (cy.query) that run within the browser context. >This approach mixes test-side and server-side and is limited in flexibility and error handling. > >mysql2 runs inside Cypress plugins (Node process) via cy.task calls, which are executed server-side (outside the browser) > >This is needed for the follow-up bug reports where we need to request >the DB from other plugins > >Test plan: >Run `yarn install` to install the mysql2 >All the Cypress tests modified by this patch must still pass >The new test t/cypress/integration/t/db.ts must also pass > >Signed-off-by: David Nind <david@davidnind.com> >--- > cypress.config.ts | 8 --- > package.json | 2 +- > t/cypress/integration/Auth/csrf.ts | 66 +++++++++---------- > .../Islands/AcquisitionsMenu_spec.ts | 10 +-- > .../integration/KohaTable/Holdings_spec.ts | 24 ++++--- > .../KohaTable/PatronSearch_spec.ts | 36 +++++----- > t/cypress/integration/t/db.ts | 15 +++++ > t/cypress/plugins/db.js | 17 +++++ > t/cypress/plugins/index.js | 7 +- > t/cypress/support/e2e.js | 3 - > 10 files changed, 107 insertions(+), 81 deletions(-) > create mode 100644 t/cypress/integration/t/db.ts > create mode 100644 t/cypress/plugins/db.js > >diff --git a/cypress.config.ts b/cypress.config.ts >index e90da4e019..e67eb344f3 100644 >--- a/cypress.config.ts >+++ b/cypress.config.ts >@@ -15,13 +15,5 @@ export default defineConfig({ > baseUrl: "http://localhost:8081", > specPattern: "t/cypress/integration/**/*.*", > supportFile: "t/cypress/support/e2e.js", >- env: { >- db: { >- host: "db", >- user: "koha_kohadev", >- password: "password", >- database: "koha_kohadev", >- }, >- }, > }, > }); >diff --git a/package.json b/package.json >index d16166e856..66748e8699 100644 >--- a/package.json >+++ b/package.json >@@ -19,7 +19,6 @@ > "bootstrap": "^5.3.3", > "css-loader": "^6.6.0", > "cypress": "^12.17.2", >- "cypress-mysql": "^1.0.0", > "datatables.net-buttons": "^2.3.4", > "datatables.net-vue3": "^2.0.0", > "gulp": "^4.0.2", >@@ -82,6 +81,7 @@ > "globals": "^16.0.0", > "gulp-tap": "^1.0.1", > "html-webpack-plugin": "^5.5.0", >+ "mysql2": "^3.14.1", > "node-sass-tilde-importer": "^1.0.2", > "postcss": "^8.4.14", > "postcss-selector-parser": "^6.0.10", >diff --git a/t/cypress/integration/Auth/csrf.ts b/t/cypress/integration/Auth/csrf.ts >index c17763090b..c24b691d04 100644 >--- a/t/cypress/integration/Auth/csrf.ts >+++ b/t/cypress/integration/Auth/csrf.ts >@@ -5,7 +5,7 @@ const branchname = "test_branchname"; > > function cleanup() { > const sql = "DELETE FROM branches WHERE branchcode=?"; >- cy.query(sql, branchcode); >+ cy.task("query", { sql, values: [branchcode] }); > } > > describe("CSRF", () => { >@@ -32,10 +32,10 @@ describe("CSRF", () => { > .find(".alert") > .contains(/No CSRF token passed for POST/); > >- cy.query( >- "SELECT COUNT(*) as count FROM branches WHERE branchcode=?", >- branchcode >- ).then(result => { >+ cy.task("query", { >+ sql: "SELECT COUNT(*) as count FROM branches WHERE branchcode=?", >+ values: [branchcode], >+ }).then(result => { > expect(result[0].count).to.equal(0); > }); > }); >@@ -53,10 +53,10 @@ describe("CSRF", () => { > .find(".alert") > .contains(/Wrong CSRF token/); > >- cy.query( >- "SELECT COUNT(*) as count FROM branches WHERE branchcode=?", >- branchcode >- ).then(result => { >+ cy.task("query", { >+ sql: "SELECT COUNT(*) as count FROM branches WHERE branchcode=?", >+ values: [branchcode], >+ }).then(result => { > expect(result[0].count).to.equal(0); > }); > }); >@@ -89,10 +89,10 @@ describe("CSRF", () => { > // We do not want Wrong CSRF token here > cy.get(".message").should("not.exist"); > >- cy.query( >- "SELECT COUNT(*) as count FROM branches WHERE branchcode=?", >- branchcode >- ).then(result => { >+ cy.task("query", { >+ sql: "SELECT COUNT(*) as count FROM branches WHERE branchcode=?", >+ values: [branchcode], >+ }).then(result => { > expect(result[0].count).to.equal(0); > }); > }); >@@ -112,19 +112,19 @@ describe("CSRF", () => { > cy.get("select[name='libraries_length']").select("-1"); > cy.get("td").contains(branchcode); > >- cy.query( >- "SELECT COUNT(*) as count FROM branches WHERE branchcode=?", >- branchcode >- ).then(result => { >+ cy.task("query", { >+ sql: "SELECT COUNT(*) as count FROM branches WHERE branchcode=?", >+ values: [branchcode], >+ }).then(result => { > expect(result[0].count).to.equal(1); > }); > }); > > it("Delete without CSRF", () => { >- cy.query("INSERT INTO branches(branchcode, branchname) VALUES (?, ?)", [ >- branchcode, >- branchname, >- ]); >+ cy.task("query", { >+ sql: "INSERT INTO branches(branchcode, branchname) VALUES (?, ?)", >+ values: [branchcode, branchname], >+ }); > > cy.visit("/cgi-bin/koha/admin/branches.pl"); > cy.get("select[name='libraries_length']").select("-1"); >@@ -141,19 +141,19 @@ describe("CSRF", () => { > .find(".alert") > .contains(/No CSRF token passed for POST/); > >- cy.query( >- "SELECT COUNT(*) as count FROM branches WHERE branchcode=?", >- branchcode >- ).then(result => { >+ cy.task("query", { >+ sql: "SELECT COUNT(*) as count FROM branches WHERE branchcode=?", >+ values: [branchcode], >+ }).then(result => { > expect(result[0].count).to.equal(1); > }); > }); > > it("Delete", () => { >- cy.query("INSERT INTO branches(branchcode, branchname) VALUES (?, ?)", [ >- branchcode, >- branchname, >- ]); >+ cy.task("query", { >+ sql: "INSERT INTO branches(branchcode, branchname) VALUES (?, ?)", >+ values: [branchcode, branchname], >+ }); > > cy.visit("/cgi-bin/koha/admin/branches.pl"); > cy.get("select[name='libraries_length']").select("-1"); >@@ -165,10 +165,10 @@ describe("CSRF", () => { > .find(".alert") > .contains(/Library deleted successfully/); > >- cy.query( >- "SELECT COUNT(*) as count FROM branches WHERE branchcode=?", >- branchcode >- ).then(result => { >+ cy.task("query", { >+ sql: "SELECT COUNT(*) as count FROM branches WHERE branchcode=?", >+ values: [branchcode], >+ }).then(result => { > expect(result[0].count).to.equal(0); > }); > }); >diff --git a/t/cypress/integration/Islands/AcquisitionsMenu_spec.ts b/t/cypress/integration/Islands/AcquisitionsMenu_spec.ts >index 8d2413324d..121e1c9402 100644 >--- a/t/cypress/integration/Islands/AcquisitionsMenu_spec.ts >+++ b/t/cypress/integration/Islands/AcquisitionsMenu_spec.ts >@@ -23,12 +23,14 @@ describe("Acquisitions menu", () => { > it("Should show/hide links based on permissions", () => { > cy.get(".sidebar_menu").should("be.visible"); > >- cy.query( >- "UPDATE borrowers SET flags=2052 WHERE borrowernumber=51" >- ).then(() => { >+ cy.task("query", { >+ sql: "UPDATE borrowers SET flags=2052 WHERE borrowernumber=51", >+ }).then(() => { > cy.reload(true); > cy.get(".sidebar_menu a").should("have.length", 8); >- cy.query("UPDATE borrowers SET flags=1 WHERE borrowernumber=51"); >+ cy.task("query", { >+ sql: "UPDATE borrowers SET flags=1 WHERE borrowernumber=51", >+ }); > }); > }); > it("Should correctly apply the 'current' class", () => { >diff --git a/t/cypress/integration/KohaTable/Holdings_spec.ts b/t/cypress/integration/KohaTable/Holdings_spec.ts >index beee5044af..380ac9d2d8 100644 >--- a/t/cypress/integration/KohaTable/Holdings_spec.ts >+++ b/t/cypress/integration/KohaTable/Holdings_spec.ts >@@ -42,7 +42,9 @@ describe("catalogue/detail/holdings_table with items", () => { > cy.wrap(Promise.resolve()) > .then(() => { > return queries.reduce((chain, { query, values }) => { >- return chain.then(() => cy.query(query, values)); >+ return chain.then(() => >+ cy.task("query", { sql: query, values }) >+ ); > }, Promise.resolve()); > }) > .then(() => { >@@ -102,9 +104,9 @@ describe("catalogue/detail/holdings_table with items", () => { > }); > }); > }); >- cy.query( >- "SELECT value FROM systempreferences WHERE variable='AlwaysShowHoldingsTableFilters'" >- ).then(value => { >+ cy.task("query", { >+ sql: "SELECT value FROM systempreferences WHERE variable='AlwaysShowHoldingsTableFilters'", >+ }).then(value => { > cy.wrap(value).as("syspref_AlwaysShowHoldingsTableFilters"); > }); > }); >@@ -435,14 +437,16 @@ describe("catalogue/detail/holdings_table without items", () => { > const item_type = generated_objects["item_type"]; > const queries = [ > { >- query: "INSERT INTO itemtypes(itemtype, description) VALUES (?, ?)", >+ sql: "INSERT INTO itemtypes(itemtype, description) VALUES (?, ?)", > values: [item_type.item_type_id, item_type.description], > }, > ]; > cy.wrap(Promise.resolve()) > .then(() => { >- return queries.reduce((chain, { query, values }) => { >- return chain.then(() => cy.query(query, values)); >+ return queries.reduce((chain, { sql, values }) => { >+ return chain.then(() => >+ cy.task("query", { sql, values }) >+ ); > }, Promise.resolve()); > }) > .then(() => { >@@ -491,9 +495,9 @@ describe("catalogue/detail/holdings_table without items", () => { > }); > }); > }); >- cy.query( >- "SELECT value FROM systempreferences WHERE variable='AlwaysShowHoldingsTableFilters'" >- ).then(value => { >+ cy.task("query", { >+ sql: "SELECT value FROM systempreferences WHERE variable='AlwaysShowHoldingsTableFilters'", >+ }).then(value => { > cy.wrap(value).as("syspref_AlwaysShowHoldingsTableFilters"); > }); > }); >diff --git a/t/cypress/integration/KohaTable/PatronSearch_spec.ts b/t/cypress/integration/KohaTable/PatronSearch_spec.ts >index a9cc064f40..782cb45f4f 100644 >--- a/t/cypress/integration/KohaTable/PatronSearch_spec.ts >+++ b/t/cypress/integration/KohaTable/PatronSearch_spec.ts >@@ -9,7 +9,7 @@ const patron_attr_type = "attribute_type4TEST"; > > function cleanup() { > const sql = "DELETE FROM borrower_attribute_types WHERE code=?"; >- cy.query(sql, patron_attr_type); >+ cy.task("query", { sql, values: [patron_attr_type] }); > } > describe("ExtendedPatronAttributes", () => { > beforeEach(() => { >@@ -19,9 +19,9 @@ describe("ExtendedPatronAttributes", () => { > cy.window().then(win => { > win.localStorage.clear(); > }); >- cy.query( >- "SELECT value FROM systempreferences WHERE variable='ExtendedPatronAttributes'" >- ).then(value => { >+ cy.task("query", { >+ sql: "SELECT value FROM systempreferences WHERE variable='ExtendedPatronAttributes'", >+ }).then(value => { > cy.wrap(value).as("syspref_ExtendedPatronAttributes"); > }); > }); >@@ -45,9 +45,9 @@ describe("ExtendedPatronAttributes", () => { > cy.get("#search_patron_filter").type("something"); > cy.get("form.patron_search_form input[type='submit']").click(); > >- cy.query( >- "select count(*) as nb_searchable from borrower_attribute_types where staff_searchable=1" >- ).then(result => { >+ cy.task("query", { >+ sql: "select count(*) as nb_searchable from borrower_attribute_types where staff_searchable=1", >+ }).then(result => { > const has_searchable = result[0].nb_searchable; > cy.wait("@searchPatrons").then(interception => { > const q = interception.request.query.q; >@@ -55,10 +55,10 @@ describe("ExtendedPatronAttributes", () => { > }); > }); > >- cy.query( >- "INSERT INTO borrower_attribute_types(code, description, staff_searchable, searched_by_default) VALUES (?, 'only for tests', 1, 1)", >- patron_attr_type >- ).then(() => { >+ cy.task("query", { >+ sql: "INSERT INTO borrower_attribute_types(code, description, staff_searchable, searched_by_default) VALUES (?, 'only for tests', 1, 1)", >+ values: [patron_attr_type], >+ }).then(() => { > cy.visit("/cgi-bin/koha/members/members-home.pl"); > > cy.get("#search_patron_filter").type("something"); >@@ -80,9 +80,9 @@ describe("ExtendedPatronAttributes", () => { > cy.get("#search_patron_filter").type("something"); > cy.get("form.patron_search_form input[type='submit']").click(); > >- cy.query( >- "select count(*) as nb_searchable from borrower_attribute_types where staff_searchable=1 AND searched_by_default=1" >- ).then(result => { >+ cy.task("query", { >+ sql: "select count(*) as nb_searchable from borrower_attribute_types where staff_searchable=1 AND searched_by_default=1", >+ }).then(result => { > const has_searchable = result[0].nb_searchable; > cy.wait("@searchPatrons").then(interception => { > const q = interception.request.query.q; >@@ -94,10 +94,10 @@ describe("ExtendedPatronAttributes", () => { > }); > }); > >- cy.query( >- "INSERT INTO borrower_attribute_types(code, description, staff_searchable, searched_by_default) VALUES (?, 'only for tests', 1, 1)", >- patron_attr_type >- ).then(() => { >+ cy.task("query", { >+ sql: "INSERT INTO borrower_attribute_types(code, description, staff_searchable, searched_by_default) VALUES (?, 'only for tests', 1, 1)", >+ values: [patron_attr_type], >+ }).then(() => { > cy.visit("/cgi-bin/koha/members/members-home.pl"); > > cy.get("#search_patron_filter").type("something"); >diff --git a/t/cypress/integration/t/db.ts b/t/cypress/integration/t/db.ts >new file mode 100644 >index 0000000000..5715c8870f >--- /dev/null >+++ b/t/cypress/integration/t/db.ts >@@ -0,0 +1,15 @@ >+describe("DB tests", () => { >+ it("should be able to SELECT", () => { >+ cy.task("query", { sql: "SELECT count(*) FROM borrowers" }).then( >+ rows => { >+ expect(typeof rows.length).to.be.equal("number"); >+ } >+ ); >+ cy.task("query", { >+ sql: "SELECT count(*) FROM borrowers WHERE `surname` = ?", >+ values: ["john"], >+ }).then(rows => { >+ expect(typeof rows.length).to.be.equal("number"); >+ }); >+ }); >+}); >diff --git a/t/cypress/plugins/db.js b/t/cypress/plugins/db.js >new file mode 100644 >index 0000000000..fb4568886c >--- /dev/null >+++ b/t/cypress/plugins/db.js >@@ -0,0 +1,17 @@ >+const mysql = require("mysql2/promise"); >+ >+const connectionConfig = { >+ host: "db", >+ user: "koha_kohadev", >+ password: "password", >+ database: "koha_kohadev", >+}; >+ >+async function query(sql, params = []) { >+ const connection = await mysql.createConnection(connectionConfig); >+ const [rows] = await connection.execute(sql, params); >+ await connection.end(); >+ return rows; >+} >+ >+module.exports = { query }; >diff --git a/t/cypress/plugins/index.js b/t/cypress/plugins/index.js >index 837e6d747c..5221471779 100644 >--- a/t/cypress/plugins/index.js >+++ b/t/cypress/plugins/index.js >@@ -1,9 +1,9 @@ > const { startDevServer } = require("@cypress/webpack-dev-server"); > >-const mysql = require("cypress-mysql"); >- > const { buildSampleObject, buildSampleObjects } = require("./mockData.js"); > >+const { query } = require("./db.js"); >+ > module.exports = (on, config) => { > on("dev-server:start", options => > startDevServer({ >@@ -11,11 +11,10 @@ module.exports = (on, config) => { > }) > ); > >- mysql.configurePlugin(on); >- > on("task", { > buildSampleObject, > buildSampleObjects, >+ query, > }); > return config; > }; >diff --git a/t/cypress/support/e2e.js b/t/cypress/support/e2e.js >index e420889c3b..40265d607b 100644 >--- a/t/cypress/support/e2e.js >+++ b/t/cypress/support/e2e.js >@@ -1874,9 +1874,6 @@ cy.getVendor = () => { > }; > }; > >-const mysql = require("cypress-mysql"); >-mysql.addCommands(); >- > Cypress.Commands.add("set_syspref", (variable, value) => { > cy.window().then(win => { > const client = win.APIClient.sysprefs; >-- >2.39.5
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 40170
:
183352
| 183359