From c616bd7a62567a2789dc0538643bd56a062b5d27 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 4 May 2022 12:07:37 +0100 Subject: [PATCH] Attempt to fix the cypress flake (#8492) --- .../integration/1-register/register.spec.ts | 14 ++--- cypress/plugins/index.ts | 9 ++- cypress/plugins/synapsedocker/index.ts | 13 +++-- cypress/support/index.ts | 22 +++++++- cypress/support/synapse.ts | 55 +++++++++++++++++++ 5 files changed, 95 insertions(+), 18 deletions(-) create mode 100644 cypress/support/synapse.ts diff --git a/cypress/integration/1-register/register.spec.ts b/cypress/integration/1-register/register.spec.ts index f719da5547..e06d1e5f28 100644 --- a/cypress/integration/1-register/register.spec.ts +++ b/cypress/integration/1-register/register.spec.ts @@ -16,27 +16,25 @@ limitations under the License. /// -import { SynapseInstance } from "../../plugins/synapsedocker/index"; +import { SynapseInstance } from "../../plugins/synapsedocker"; describe("Registration", () => { - let synapseId; - let synapsePort; + let synapse: SynapseInstance; beforeEach(() => { - cy.task("synapseStart", "consent").then(result => { - synapseId = result.synapseId; - synapsePort = result.port; + cy.startSynapse("consent").then(data => { + synapse = data; }); cy.visit("/#/register"); }); afterEach(() => { - cy.task("synapseStop", synapseId); + cy.stopSynapse(synapse); }); it("registers an account and lands on the home screen", () => { cy.get(".mx_ServerPicker_change", { timeout: 15000 }).click(); - cy.get(".mx_ServerPickerDialog_otherHomeserver").type(`http://localhost:${synapsePort}`); + cy.get(".mx_ServerPickerDialog_otherHomeserver").type(`http://localhost:${synapse.port}`); cy.get(".mx_ServerPickerDialog_continue").click(); // wait for the dialog to go away cy.get('.mx_ServerPickerDialog').should('not.exist'); diff --git a/cypress/plugins/index.ts b/cypress/plugins/index.ts index db01ceceb4..9438d13606 100644 --- a/cypress/plugins/index.ts +++ b/cypress/plugins/index.ts @@ -16,8 +16,13 @@ limitations under the License. /// -import { synapseDocker } from "./synapsedocker/index"; +import { synapseDocker } from "./synapsedocker"; +import PluginEvents = Cypress.PluginEvents; +import PluginConfigOptions = Cypress.PluginConfigOptions; -export default function(on, config) { +/** + * @type {Cypress.PluginConfig} + */ +export default function(on: PluginEvents, config: PluginConfigOptions) { synapseDocker(on, config); } diff --git a/cypress/plugins/synapsedocker/index.ts b/cypress/plugins/synapsedocker/index.ts index 0f029e7b2e..912e99431e 100644 --- a/cypress/plugins/synapsedocker/index.ts +++ b/cypress/plugins/synapsedocker/index.ts @@ -22,6 +22,9 @@ import * as crypto from "crypto"; import * as childProcess from "child_process"; import * as fse from "fs-extra"; +import PluginEvents = Cypress.PluginEvents; +import PluginConfigOptions = Cypress.PluginConfigOptions; + // A cypress plugins to add command to start & stop synapses in // docker with preset templates. @@ -130,7 +133,7 @@ async function synapseStart(template: string): Promise { return synapses.get(synapseId); } -async function synapseStop(id) { +async function synapseStop(id: string): Promise { const synCfg = synapses.get(id); if (!synCfg) throw new Error("Unknown synapse ID"); @@ -186,10 +189,10 @@ async function synapseStop(id) { /** * @type {Cypress.PluginConfig} */ -// eslint-disable-next-line no-unused-vars -export function synapseDocker(on, config) { +export function synapseDocker(on: PluginEvents, config: PluginConfigOptions) { on("task", { - synapseStart, synapseStop, + synapseStart, + synapseStop, }); on("after:spec", async (spec) => { @@ -197,7 +200,7 @@ export function synapseDocker(on, config) { // This is on the theory that we should avoid re-using synapse // instances between spec runs: they should be cheap enough to // start that we can have a separate one for each spec run or even - // test. If we accidentally re-use synapses, we could inadvertantly + // test. If we accidentally re-use synapses, we could inadvertently // make our tests depend on each other. for (const synId of synapses.keys()) { console.warn(`Cleaning up synapse ID ${synId} after ${spec.name}`); diff --git a/cypress/support/index.ts b/cypress/support/index.ts index 9901ef4cb8..289319fe97 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -1,3 +1,19 @@ -// Empty file to prevent cypress from recreating a helpful example -// file on every run (their example file doesn't use semicolons and -// so fails our lint rules). +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/// + +import "./synapse"; diff --git a/cypress/support/synapse.ts b/cypress/support/synapse.ts new file mode 100644 index 0000000000..f49f55a19d --- /dev/null +++ b/cypress/support/synapse.ts @@ -0,0 +1,55 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/// + +import Chainable = Cypress.Chainable; +import AUTWindow = Cypress.AUTWindow; +import { SynapseInstance } from "../plugins/synapsedocker"; + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Cypress { + interface Chainable { + /** + * Start a synapse instance with a given config template. + * @param template path to template within cypress/plugins/synapsedocker/template/ directory. + */ + startSynapse(template: string): Chainable; + /** + * Custom command wrapping task:synapseStop whilst preventing uncaught exceptions + * for if Synapse stopping races with the app's background sync loop. + * @param synapse the synapse instance returned by startSynapse + */ + stopSynapse(synapse: SynapseInstance): Chainable; + } + } +} + +function startSynapse(template: string): Chainable { + return cy.task("synapseStart", template); +} + +function stopSynapse(synapse: SynapseInstance): Chainable { + // Navigate away from app to stop the background network requests which will race with Synapse shutting down + return cy.window().then((win) => { + win.location.href = 'about:blank'; + cy.task("synapseStop", synapse.synapseId); + }); +} + +Cypress.Commands.add("startSynapse", startSynapse); +Cypress.Commands.add("stopSynapse", stopSynapse);