Move update tests to Cypress (#8716)

* Move update tests to Cypress

* Fix /version intercept to account for cachebuster
t3chguy/dedup-icons-17oct
Michael Telatynski 2022-05-30 14:40:55 +01:00 committed by GitHub
parent eaace4b4d1
commit af6ded3b0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 53 additions and 124 deletions

View File

@ -0,0 +1,53 @@
/*
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.
*/
/// <reference types="cypress" />
import { SynapseInstance } from "../../plugins/synapsedocker";
describe("Update", () => {
let synapse: SynapseInstance;
beforeEach(() => {
cy.startSynapse("default").then(data => {
synapse = data;
});
});
afterEach(() => {
cy.stopSynapse(synapse);
});
it("should navigate to ?updated=$VERSION if realises it is immediately out of date on load", () => {
const NEW_VERSION = "some-new-version";
cy.intercept("/version*", {
statusCode: 200,
body: NEW_VERSION,
headers: {
"Content-Type": "test/plain",
},
}).as("version");
cy.initTestUser(synapse, "Ursa");
cy.wait("@version");
cy.url().should("contain", "updated=" + NEW_VERSION).then(href => {
const url = new URL(href);
expect(url.searchParams.get("updated")).to.equal(NEW_VERSION);
});
});
});

View File

@ -27,7 +27,6 @@ import { RestMultiSession } from "./rest/multi";
import { RestSession } from "./rest/session"; import { RestSession } from "./rest/session";
import { stickerScenarios } from './scenarios/sticker'; import { stickerScenarios } from './scenarios/sticker';
import { userViewScenarios } from "./scenarios/user-view"; import { userViewScenarios } from "./scenarios/user-view";
import { updateScenarios } from "./scenarios/update";
export async function scenario(createSession: (s: string) => Promise<ElementSession>, export async function scenario(createSession: (s: string) => Promise<ElementSession>,
restCreator: RestSessionCreator): Promise<void> { restCreator: RestSessionCreator): Promise<void> {
@ -63,10 +62,6 @@ export async function scenario(createSession: (s: string) => Promise<ElementSess
// closing them as we go rather than leaving them all open until the end). // closing them as we go rather than leaving them all open until the end).
const stickerSession = await createSession("sally"); const stickerSession = await createSession("sally");
await stickerScenarios("sally", "ilikestickers", stickerSession, restCreator); await stickerScenarios("sally", "ilikestickers", stickerSession, restCreator);
// Create a new window to test app auto-updating
const updateSession = await createSession("update");
await updateScenarios(updateSession);
} }
async function createRestUsers(restCreator: RestSessionCreator): Promise<RestMultiSession> { async function createRestUsers(restCreator: RestSessionCreator): Promise<RestMultiSession> {

View File

@ -1,49 +0,0 @@
/*
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 { HTTPRequest } from "puppeteer";
import { strict as assert } from 'assert';
import { ElementSession } from "../session";
const NEW_VERSION = "some-new-version";
async function mockVersionHTTPResponse(session: ElementSession) {
// Mock the HTTP response to return a new version to trigger auto-update behaviour
await session.page.setRequestInterception(true);
session.page.on('request', (request: HTTPRequest) => {
if (request.isInterceptResolutionHandled()) return;
const url = new URL(request.url());
if (url.pathname === "/version") {
request.respond({
contentType: "text/html",
status: 200,
body: NEW_VERSION,
});
} else {
request.continue();
}
});
}
export async function updateScenarios(session: ElementSession) {
// Mock the HTTP response to return a newer version, then wait for the page to reload in response
await mockVersionHTTPResponse(session);
await session.goto(session.url('/'));
await session.waitForReload();
const newUrl = new URL(session.page.url());
assert.equal(newUrl.searchParams.get("updated"), NEW_VERSION);
}

View File

@ -23,10 +23,6 @@ import { delay, serializeLog } from './util';
const DEFAULT_TIMEOUT = 20000; const DEFAULT_TIMEOUT = 20000;
interface XHRLogger {
logs: () => string;
}
export class ElementSession { export class ElementSession {
readonly consoleLog: LogBuffer<puppeteer.ConsoleMessage>; readonly consoleLog: LogBuffer<puppeteer.ConsoleMessage>;
readonly networkLog: LogBuffer<puppeteer.HTTPRequest>; readonly networkLog: LogBuffer<puppeteer.HTTPRequest>;
@ -80,10 +76,6 @@ export class ElementSession {
return this.getElementProperty(field, 'innerText'); return this.getElementProperty(field, 'innerText');
} }
public getOuterHTML(field: puppeteer.ElementHandle): Promise<string> {
return this.getElementProperty(field, 'outerHTML');
}
public isChecked(field: puppeteer.ElementHandle): Promise<string> { public isChecked(field: puppeteer.ElementHandle): Promise<string> {
return this.getElementProperty(field, 'checked'); return this.getElementProperty(field, 'checked');
} }
@ -96,29 +88,6 @@ export class ElementSession {
return this.networkLog.buffer; return this.networkLog.buffer;
} }
public logXHRRequests(): XHRLogger {
let buffer = "";
this.page.on('requestfinished', async (req) => {
const type = req.resourceType();
const response = await req.response();
//if (type === 'xhr' || type === 'fetch') {
buffer += `${type} ${response.status()} ${req.method()} ${req.url()} \n`;
// if (req.method() === "POST") {
// buffer += " Post data: " + req.postData();
// }
//}
});
return {
logs() {
return buffer;
},
};
}
public async printElements(label: string, elements: puppeteer.ElementHandle[]): Promise<void> {
console.log(label, await Promise.all(elements.map(this.getOuterHTML)));
}
public async replaceInputText(input: puppeteer.ElementHandle, text: string): Promise<void> { public async replaceInputText(input: puppeteer.ElementHandle, text: string): Promise<void> {
// click 3 times to select all text // click 3 times to select all text
await input.click({ clickCount: 3 }); await input.click({ clickCount: 3 });
@ -149,45 +118,6 @@ export class ElementSession {
return await this.page.$$(selector); return await this.page.$$(selector);
} }
public waitForReload(): Promise<void> {
const timeout = DEFAULT_TIMEOUT;
return new Promise((resolve, reject) => {
const timeoutHandle = setTimeout(() => {
this.page.off('domcontentloaded', callback);
reject(new Error(`timeout of ${timeout}ms for waitForReload elapsed`));
}, timeout);
const callback = async () => {
clearTimeout(timeoutHandle);
resolve();
};
this.page.once('domcontentloaded', callback);
});
}
public waitForNewPage(): Promise<void> {
const timeout = DEFAULT_TIMEOUT;
return new Promise((resolve, reject) => {
const timeoutHandle = setTimeout(() => {
this.browser.off('targetcreated', callback);
reject(new Error(`timeout of ${timeout}ms for waitForNewPage elapsed`));
}, timeout);
const callback = async (target) => {
if (target.type() !== 'page') {
return;
}
this.browser.off('targetcreated', callback);
clearTimeout(timeoutHandle);
const page = await target.page();
resolve(page);
};
this.browser.on('targetcreated', callback);
});
}
/** wait for a /sync request started after this call that gets a 200 response */ /** wait for a /sync request started after this call that gets a 200 response */
public async waitForNextSuccessfulSync(): Promise<void> { public async waitForNextSuccessfulSync(): Promise<void> {
const syncUrls = []; const syncUrls = [];