From ad8fbdec543d09755cd76d81620ce5a9f04e3b49 Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Mon, 26 Oct 2026 17:12:31 -0700 Subject: [PATCH] case 4690 - E2E: reliable login and notification wait on remote servers Over the internet the login page reloads once on a first visit (service worker taking control) and can move the focus back to the username field while it settles; both made logins fail at random (8.2.4 and 8.3.0). ayLogin now lets the service worker take control first, waits for notify/hello and the trial users list, checks both fields and types again if needed, and clicks the login button. 0900 reloads Notifications until the notification arrives (the server delivers about once a minute). 27/27 on the 8.3.0 release candidate. Co-Authored-By: Claude Opus 5.5 --- e2e/support/commands.js | 54 ++++++++++++++++--- .../0900-notification-arrived.cy.js | 15 ++++++ 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/e2e/support/commands.js b/e2e/support/commands.js index 2258259..172b444 100644 --- a/e2e/support/commands.js +++ b/e2e/support/commands.js @@ -56,19 +56,61 @@ Cypress.Commands.add("ayLogin", (user, urlAfterLogin) => { if (!urlAfterLogin) { urlAfterLogin = "/ay-evaluate"; } + // On a first visit to a built server the app's service worker installs, takes control and + // App.vue then reloads the page (controllerchange); a reload while typing fails the test. + // Let that happen first: wait until the service worker controls the page (at most 15 seconds, + // never failing; the local dev server has none), then open the login page again. cy.visit("/login"); - cy.get("input[name=username]").clear(); - cy.get("input[name=password]").clear(); + cy.window().then( + { timeout: 20000 }, + (win) => + new Cypress.Promise((resolve) => { + const sw = win.navigator.serviceWorker; + if (!sw || sw.controller) { + return resolve(); + } + const giveUp = setTimeout(resolve, 15000); + sw.addEventListener("controllerchange", () => { + clearTimeout(giveUp); + resolve(); + }); + }) + ); - cy.get("input[name=username]").clear().type(Cypress.env(user).login); + // the login page fills in sample credentials and adds the trial users list above the + // username field when notify/hello answers (trial servers); wait for that first. + // The suite always runs on a seeded trial. + cy.intercept("GET", "**/notify/hello").as("hello"); + cy.visit("/login"); + cy.wait("@hello"); + cy.get("[data-cy=selecttrialuser]"); - // {enter} causes the form to submit - cy.get("input[name=password]").clear() - .type(`${Cypress.env(user).password}{enter}`); + ayFillLogin(Cypress.env(user).login, Cypress.env(user).password, 5); + + // the button logs in with whatever is in the fields, wherever the keyboard focus is + cy.get("[data-cy=loginbutton]").click(); cy.url().should("include", urlAfterLogin); }); +// Over the internet the login page can still redraw while it settles, and on a first visit the +// service worker reloads it once (App.vue, controllerchange). A redraw moves the keyboard focus +// back to the username field (v-focus) mid-typing, a reload wipes both fields (both seen on 8.2.4 +// and 8.3.0). So type, give any redraw under way a moment, check both fields, and type again if +// either is wrong. +function ayFillLogin(login, password, triesLeft) { + cy.get("input[name=username]").clear().type(login); + cy.get("input[name=password]").clear().type(password); + cy.wait(500); + cy.get("input[name=username]").then(($u) => { + cy.get("input[name=password]").then(($p) => { + if (($u.val() !== login || $p.val() !== password) && triesLeft > 0) { + ayFillLogin(login, password, triesLeft - 1); + } + }); + }); +} + Cypress.Commands.add("ayChooseExistingTags", (dataCyTag, tagList) => { for (let i = 0; i < tagList.length; i++) { cy.get(`[data-cy='${dataCyTag}']`).click().type(tagList[i]); diff --git a/e2e/tests/regression/3-accounting-user/0900-notification-arrived.cy.js b/e2e/tests/regression/3-accounting-user/0900-notification-arrived.cy.js index 0797883..23cbca9 100644 --- a/e2e/tests/regression/3-accounting-user/0900-notification-arrived.cy.js +++ b/e2e/tests/regression/3-accounting-user/0900-notification-arrived.cy.js @@ -6,9 +6,24 @@ describe("Tax code", () => { cy.get("[data-cy=notification]").click(); cy.url().should("include", "/home-notifications"); + // the server delivers notifications about once a minute (release build) and this page does not + // refresh itself, so reload until the contract notification from 0500 has arrived + waitForNotification(15); cy.contains("Object created"); //Going to assume this is a virgin run so test contract text should be there but not going to assume it's due to current run so not checking for cyid //cy.contains(`${Cypress.config("cyid")}-test-contract`); cy.contains('-test-contract'); }); }); + +// check 5 seconds after each (re)load, up to the given number of times, until "Object created" shows +function waitForNotification(triesLeft) { + cy.wait(5000); + cy.get("body").then(($body) => { + if ($body.text().includes("Object created") || triesLeft <= 0) { + return; + } + cy.reload(); + waitForNotification(triesLeft - 1); + }); +}