Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Cypress tests for soft logout #11402

Merged
merged 1 commit into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 2 additions & 28 deletions cypress/e2e/login/login.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
/// <reference types="cypress" />

import { HomeserverInstance } from "../../plugins/utils/homeserver";
import { doTokenRegistration } from "./utils";

describe("Login", () => {
let homeserver: HomeserverInstance;
Expand Down Expand Up @@ -93,7 +94,6 @@ describe("Login", () => {
})
.then((data) => {
homeserver = data;
cy.visit("/#/login");
});
});

Expand All @@ -108,33 +108,7 @@ describe("Login", () => {
// If you are using ufw, try something like:
// sudo ufw allow in on docker0
//
cy.findByRole("button", { name: "Edit" }).click();
cy.findByRole("textbox", { name: "Other homeserver" }).type(homeserver.baseUrl);
cy.findByRole("button", { name: "Continue" }).click();
// wait for the dialog to go away
cy.get(".mx_ServerPickerDialog").should("not.exist");

// click on "Continue with OAuth test"
cy.findByRole("button", { name: "Continue with OAuth test" }).click();

// wait for the Test OAuth Page to load
cy.findByText("Test OAuth page");

// click the submit button
cy.findByRole("button", { name: "Submit" }).click();

// Synapse prompts us to pick a user ID
cy.findByRole("heading", { name: "Create your account" });
cy.findByRole("textbox", { name: "Username (required)" }).type("alice");

// wait for username validation to start, and complete
cy.wait(50);
cy.get("#field-username-output").should("have.value", "");
cy.findByRole("button", { name: "Continue" }).click();

// Synapse prompts us to grant permission to Element
cy.findByRole("heading", { name: "Continue to your account" });
cy.findByRole("link", { name: "Continue" }).click();
doTokenRegistration(homeserver.baseUrl);

// Eventually, we should end up at the home screen.
cy.url().should("contain", "/#/home", { timeout: 30000 });
Expand Down
143 changes: 143 additions & 0 deletions cypress/e2e/login/soft_logout.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
Copyright 2023 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 { HomeserverInstance } from "../../plugins/utils/homeserver";
import { UserCredentials } from "../../support/login";
import { doTokenRegistration } from "./utils";

describe("Soft logout", () => {
let homeserver: HomeserverInstance;

beforeEach(() => {
cy.task("startOAuthServer")
.then((oAuthServerPort: number) => {
return cy.startHomeserver({ template: "default", oAuthServerPort });
})
.then((data) => {
homeserver = data;
});
});

afterEach(() => {
cy.stopHomeserver(homeserver);
});

describe("with password user", () => {
let testUserCreds: UserCredentials;

beforeEach(() => {
cy.initTestUser(homeserver, "Alice").then((creds) => {
testUserCreds = creds;
});
});

it("shows the soft-logout page when a request fails, and allows a re-login", () => {
interceptRequestsWithSoftLogout();
cy.findByText("You're signed out");
cy.findByPlaceholderText("Password").type(testUserCreds.password).type("{enter}");

// back to the welcome page
cy.url().should("contain", "/#/home", { timeout: 30000 });
cy.findByRole("heading", { name: "Welcome Alice" });
});

it("still shows the soft-logout page when the page is reloaded after a soft-logout", () => {
interceptRequestsWithSoftLogout();
cy.findByText("You're signed out");
cy.reload();
cy.findByText("You're signed out");
});
});

describe("with SSO user", () => {
beforeEach(() => {
doTokenRegistration(homeserver.baseUrl);

// Eventually, we should end up at the home screen.
cy.url().should("contain", "/#/home", { timeout: 30000 });
cy.findByRole("heading", { name: "Welcome Alice" });
});

it("shows the soft-logout page when a request fails, and allows a re-login", () => {
// there is a bug in Element which means this only actually works if there is an app reload between
// the token login and the soft-logout: https://github.com/vector-im/element-web/issues/25957
cy.reload();
cy.findByRole("heading", { name: "Welcome Alice" });

interceptRequestsWithSoftLogout();

cy.findByText("You're signed out");
cy.findByRole("button", { name: "Continue with OAuth test" }).click();

// click the submit button
cy.findByRole("button", { name: "Submit" }).click();

// Synapse prompts us to grant permission to Element
cy.findByRole("heading", { name: "Continue to your account" });
cy.findByRole("link", { name: "Continue" }).click();

// back to the welcome page
cy.url().should("contain", "/#/home", { timeout: 30000 });
cy.findByRole("heading", { name: "Welcome Alice" });
});
});
});

/**
* Intercept calls to /sync and have them fail with a soft-logout
*
* Any further requests to /sync with the same access token are blocked.
*/
function interceptRequestsWithSoftLogout(): void {
let expiredAccessToken: string | null = null;
cy.intercept(
{
pathname: "/_matrix/client/*/sync",
},
(req) => {
const accessToken = req.headers["authorization"] as string;

// on the first request, record the access token
if (!expiredAccessToken) {
console.log(`Soft-logout on access token ${accessToken}`);
expiredAccessToken = accessToken;
}

// now, if the access token on this request matches the expired one, block it
if (expiredAccessToken && accessToken === expiredAccessToken) {
console.log(`Intercepting request with soft-logged-out access token`);
req.reply({
statusCode: 401,
body: {
errcode: "M_UNKNOWN_TOKEN",
error: "Soft logout",
soft_logout: true,
},
});
return;
}

// otherwise, pass through as normal
req.continue();
},
);

// do something to make the active /sync return: create a new room
cy.getClient().then((client) => {
// don't wait for this to complete: it probably won't, because of the broken sync
return client.createRoom({});
});
}
49 changes: 49 additions & 0 deletions cypress/e2e/login/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
Copyright 2023 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.
*/

/** Visit the login page, choose to log in with "OAuth test", register a new account, and redirect back to Element
*/
export function doTokenRegistration(homeserverUrl: string) {
cy.visit("/#/login");

cy.findByRole("button", { name: "Edit" }).click();
cy.findByRole("textbox", { name: "Other homeserver" }).type(homeserverUrl);
cy.findByRole("button", { name: "Continue" }).click();
// wait for the dialog to go away
cy.get(".mx_ServerPickerDialog").should("not.exist");

// click on "Continue with OAuth test"
cy.findByRole("button", { name: "Continue with OAuth test" }).click();

// wait for the Test OAuth Page to load
cy.findByText("Test OAuth page");

// click the submit button
cy.findByRole("button", { name: "Submit" }).click();

// Synapse prompts us to pick a user ID
cy.findByRole("heading", { name: "Create your account" });
cy.findByRole("textbox", { name: "Username (required)" }).type("alice");

// wait for username validation to start, and complete
cy.wait(50);
cy.get("#field-username-output").should("have.value", "");
cy.findByRole("button", { name: "Continue" }).click();

// Synapse prompts us to grant permission to Element
cy.findByRole("heading", { name: "Continue to your account" });
cy.findByRole("link", { name: "Continue" }).click();
}