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

Commit

Permalink
Cypress tests for soft logout
Browse files Browse the repository at this point in the history
  • Loading branch information
richvdh committed Aug 11, 2023
1 parent 8633a07 commit 8b9ea1b
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 28 deletions.
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 with an Auth header, 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
client.createRoom({}).then(() => {});
});
}
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();
}

0 comments on commit 8b9ea1b

Please sign in to comment.