Apply prettier formatting

This commit is contained in:
Michael Weimann 2022-12-09 13:28:29 +01:00
parent a32f12c8f3
commit 7921a6cbf8
No known key found for this signature in database
GPG key ID: 53F535A266BB9584
104 changed files with 12169 additions and 11047 deletions

View file

@ -18,27 +18,27 @@ limitations under the License.
/* loading.js: test the myriad paths we have for loading the application */
import "fake-indexeddb/auto";
import React from 'react';
import React from "react";
import { render, screen, fireEvent, waitFor, RenderResult, waitForElementToBeRemoved } from "@testing-library/react";
import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg';
import { MatrixClientPeg } from 'matrix-react-sdk/src/MatrixClientPeg';
import MatrixChat from 'matrix-react-sdk/src/components/structures/MatrixChat';
import dis from 'matrix-react-sdk/src/dispatcher/dispatcher';
import MockHttpBackend from 'matrix-mock-request';
import PlatformPeg from "matrix-react-sdk/src/PlatformPeg";
import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg";
import MatrixChat from "matrix-react-sdk/src/components/structures/MatrixChat";
import dis from "matrix-react-sdk/src/dispatcher/dispatcher";
import MockHttpBackend from "matrix-mock-request";
import { makeType } from "matrix-react-sdk/src/utils/TypeUtils";
import { ValidatedServerConfig } from 'matrix-react-sdk/src/utils/ValidatedServerConfig';
import { ValidatedServerConfig } from "matrix-react-sdk/src/utils/ValidatedServerConfig";
import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store";
import { QueryDict, sleep } from "matrix-js-sdk/src/utils";
import "../jest-mocks";
import WebPlatform from '../../src/vector/platform/WebPlatform';
import { parseQs, parseQsFromFragment } from '../../src/vector/url_utils';
import WebPlatform from "../../src/vector/platform/WebPlatform";
import { parseQs, parseQsFromFragment } from "../../src/vector/url_utils";
import { cleanLocalstorage, deleteIndexedDB } from "../test-utils";
const DEFAULT_HS_URL = 'http://my_server';
const DEFAULT_IS_URL = 'http://my_is';
const DEFAULT_HS_URL = "http://my_server";
const DEFAULT_IS_URL = "http://my_is";
describe('loading:', function() {
describe("loading:", function () {
let parentDiv;
let httpBackend;
@ -51,10 +51,10 @@ describe('loading:', function() {
// a promise which resolves when the MatrixChat calls onTokenLoginCompleted
let tokenLoginCompletePromise;
beforeEach(function() {
beforeEach(function () {
httpBackend = new MockHttpBackend();
window.fetch = httpBackend.fetchFn;
parentDiv = document.createElement('div');
parentDiv = document.createElement("div");
// uncomment this to actually add the div to the UI, to help with
// debugging (but slow things down)
@ -64,17 +64,14 @@ describe('loading:', function() {
matrixChat = null;
});
afterEach(async function() {
afterEach(async function () {
console.log(`${Date.now()}: loading: afterEach`);
matrixChat?.unmount();
// unmounting should have cleared the MatrixClientPeg
expect(MatrixClientPeg.get()).toBe(null);
// clear the indexeddbs so we can start from a clean slate next time.
await Promise.all([
deleteIndexedDB('matrix-js-sdk:crypto'),
deleteIndexedDB('matrix-js-sdk:riot-web-sync'),
]);
await Promise.all([deleteIndexedDB("matrix-js-sdk:crypto"), deleteIndexedDB("matrix-js-sdk:riot-web-sync")]);
cleanLocalstorage();
console.log(`${Date.now()}: loading: afterEach complete`);
});
@ -92,19 +89,21 @@ describe('loading:', function() {
windowLocation = {
search: queryString,
hash: uriFragment,
toString: function(): string { return this.search + this.hash; },
toString: function (): string {
return this.search + this.hash;
},
};
function onNewScreen(screen): void {
console.log(Date.now() + " newscreen "+screen);
const hash = '#/' + screen;
console.log(Date.now() + " newscreen " + screen);
const hash = "#/" + screen;
windowLocation.hash = hash;
console.log(Date.now() + " browser URI now "+ windowLocation);
console.log(Date.now() + " browser URI now " + windowLocation);
}
// Parse the given window.location and return parameters that can be used when calling
// MatrixChat.showScreen(screen, params)
function getScreenFromLocation(location): { screen: string, params: QueryDict } {
function getScreenFromLocation(location): { screen: string; params: QueryDict } {
const fragparts = parseQsFromFragment(location);
return {
screen: fragparts.location.substring(1),
@ -114,25 +113,28 @@ describe('loading:', function() {
const fragParts = parseQsFromFragment(windowLocation);
const config = Object.assign({
default_hs_url: DEFAULT_HS_URL,
default_is_url: DEFAULT_IS_URL,
validated_server_config: makeType(ValidatedServerConfig, {
hsUrl: DEFAULT_HS_URL,
hsName: "TEST_ENVIRONMENT",
hsNameIsDifferent: false, // yes, we lie
isUrl: DEFAULT_IS_URL,
}),
embeddedPages: {
homeUrl: 'data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==',
const config = Object.assign(
{
default_hs_url: DEFAULT_HS_URL,
default_is_url: DEFAULT_IS_URL,
validated_server_config: makeType(ValidatedServerConfig, {
hsUrl: DEFAULT_HS_URL,
hsName: "TEST_ENVIRONMENT",
hsNameIsDifferent: false, // yes, we lie
isUrl: DEFAULT_IS_URL,
}),
embeddedPages: {
homeUrl: "data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==",
},
},
}, opts.config || {});
opts.config || {},
);
PlatformPeg.set(new WebPlatform());
const params = parseQs(windowLocation);
tokenLoginCompletePromise = new Promise<void>(resolve => {
tokenLoginCompletePromise = new Promise<void>((resolve) => {
matrixChat = render(
<MatrixChat
onNewScreen={onNewScreen}
@ -143,8 +145,11 @@ describe('loading:', function() {
enableGuest={true}
onTokenLoginCompleted={resolve}
initialScreenAfterLogin={getScreenFromLocation(windowLocation)}
makeRegistrationUrl={(): string => {throw new Error('Not implemented');}}
/>, parentDiv,
makeRegistrationUrl={(): string => {
throw new Error("Not implemented");
}}
/>,
parentDiv,
);
});
}
@ -155,21 +160,23 @@ describe('loading:', function() {
// returns a promise resolving to the received request
async function expectAndAwaitSync(opts?): Promise<any> {
let syncRequest = null;
httpBackend.when('GET', '/_matrix/client/versions')
.respond(200, {
"versions": ["r0.3.0"],
"unstable_features": {
"m.lazy_load_members": true,
},
});
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
versions: ["r0.3.0"],
unstable_features: {
"m.lazy_load_members": true,
},
});
const isGuest = opts && opts.isGuest;
if (!isGuest) {
// the call to create the LL filter
httpBackend.when('POST', '/filter').respond(200, { filter_id: 'llfid' });
httpBackend.when('GET', '/pushrules').respond(200, {});
httpBackend.when("POST", "/filter").respond(200, { filter_id: "llfid" });
httpBackend.when("GET", "/pushrules").respond(200, {});
}
httpBackend.when('GET', '/sync')
.check((r) => {syncRequest = r;})
httpBackend
.when("GET", "/sync")
.check((r) => {
syncRequest = r;
})
.respond(200, {});
for (let attempts = 10; attempts > 0; attempts--) {
@ -182,29 +189,35 @@ describe('loading:', function() {
throw new Error("Gave up waiting for /sync");
}
describe("Clean load with no stored credentials:", function() {
it('gives a welcome page by default', function() {
describe("Clean load with no stored credentials:", function () {
it("gives a welcome page by default", function () {
loadApp();
return sleep(1).then(async () => {
// at this point, we're trying to do a guest registration;
// we expect a spinner
await assertAtLoadingSpinner();
return sleep(1)
.then(async () => {
// at this point, we're trying to do a guest registration;
// we expect a spinner
await assertAtLoadingSpinner();
httpBackend.when('POST', '/register').check(function(req) {
expect(req.queryParams.kind).toEqual('guest');
}).respond(403, "Guest access is disabled");
httpBackend
.when("POST", "/register")
.check(function (req) {
expect(req.queryParams.kind).toEqual("guest");
})
.respond(403, "Guest access is disabled");
return httpBackend.flush();
}).then(() => {
// Wait for another trip around the event loop for the UI to update
return awaitWelcomeComponent(matrixChat);
}).then(() => {
return waitFor(() => expect(windowLocation.hash).toEqual("#/welcome"));
});
return httpBackend.flush();
})
.then(() => {
// Wait for another trip around the event loop for the UI to update
return awaitWelcomeComponent(matrixChat);
})
.then(() => {
return waitFor(() => expect(windowLocation.hash).toEqual("#/welcome"));
});
});
it('should follow the original link after successful login', function() {
it("should follow the original link after successful login", function () {
loadApp({
uriFragment: "#/room/!room:id",
});
@ -213,39 +226,48 @@ describe('loading:', function() {
httpBackend.when("GET", "/versions").respond(200, { versions: ["r0.4.0"] });
httpBackend.when("GET", "/api/v1").respond(200, {});
return sleep(1).then(async () => {
// at this point, we're trying to do a guest registration;
// we expect a spinner
await assertAtLoadingSpinner();
return sleep(1)
.then(async () => {
// at this point, we're trying to do a guest registration;
// we expect a spinner
await assertAtLoadingSpinner();
httpBackend.when('POST', '/register').check(function(req) {
expect(req.queryParams.kind).toEqual('guest');
}).respond(403, "Guest access is disabled");
httpBackend
.when("POST", "/register")
.check(function (req) {
expect(req.queryParams.kind).toEqual("guest");
})
.respond(403, "Guest access is disabled");
return httpBackend.flush();
}).then(() => {
// Wait for another trip around the event loop for the UI to update
return sleep(10);
}).then(() => {
return moveFromWelcomeToLogin(matrixChat);
}).then(() => {
return completeLogin(matrixChat);
}).then(() => {
// once the sync completes, we should have a room view
return awaitRoomView(matrixChat);
}).then(() => {
httpBackend.verifyNoOutstandingExpectation();
expect(windowLocation.hash).toEqual("#/room/!room:id");
return httpBackend.flush();
})
.then(() => {
// Wait for another trip around the event loop for the UI to update
return sleep(10);
})
.then(() => {
return moveFromWelcomeToLogin(matrixChat);
})
.then(() => {
return completeLogin(matrixChat);
})
.then(() => {
// once the sync completes, we should have a room view
return awaitRoomView(matrixChat);
})
.then(() => {
httpBackend.verifyNoOutstandingExpectation();
expect(windowLocation.hash).toEqual("#/room/!room:id");
// and the localstorage should have been updated
expect(localStorage.getItem('mx_user_id')).toEqual('@user:id');
expect(localStorage.getItem('mx_access_token')).toEqual('access_token');
expect(localStorage.getItem('mx_hs_url')).toEqual(DEFAULT_HS_URL);
expect(localStorage.getItem('mx_is_url')).toEqual(DEFAULT_IS_URL);
});
// and the localstorage should have been updated
expect(localStorage.getItem("mx_user_id")).toEqual("@user:id");
expect(localStorage.getItem("mx_access_token")).toEqual("access_token");
expect(localStorage.getItem("mx_hs_url")).toEqual(DEFAULT_HS_URL);
expect(localStorage.getItem("mx_is_url")).toEqual(DEFAULT_IS_URL);
});
});
it.skip('should not register as a guest when using a #/login link', function() {
it.skip("should not register as a guest when using a #/login link", function () {
loadApp({
uriFragment: "#/login",
});
@ -254,37 +276,35 @@ describe('loading:', function() {
httpBackend.when("GET", "/versions").respond(200, { versions: ["r0.4.0"] });
httpBackend.when("GET", "/api/v1").respond(200, {});
return awaitLoginComponent(matrixChat).then(async () => {
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
// we expect a single <Login> component
await screen.findByRole("main");
screen.getAllByText("Sign in");
return awaitLoginComponent(matrixChat)
.then(async () => {
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading..."));
// we expect a single <Login> component
await screen.findByRole("main");
screen.getAllByText("Sign in");
// the only outstanding request should be a GET /login
// (in particular there should be no /register request for
// guest registration).
const allowedRequests = [
"/_matrix/client/r0/login",
"/versions",
"/api/v1",
];
for (const req of httpBackend.requests) {
if (req.method === 'GET' && allowedRequests.find(p => req.path.endsWith(p))) {
continue;
// the only outstanding request should be a GET /login
// (in particular there should be no /register request for
// guest registration).
const allowedRequests = ["/_matrix/client/r0/login", "/versions", "/api/v1"];
for (const req of httpBackend.requests) {
if (req.method === "GET" && allowedRequests.find((p) => req.path.endsWith(p))) {
continue;
}
throw new Error(`Unexpected HTTP request to ${req}`);
}
throw new Error(`Unexpected HTTP request to ${req}`);
}
return completeLogin(matrixChat);
}).then(() => {
expect(matrixChat.container.querySelector(".mx_HomePage")).toBeTruthy();
expect(windowLocation.hash).toEqual("#/home");
});
return completeLogin(matrixChat);
})
.then(() => {
expect(matrixChat.container.querySelector(".mx_HomePage")).toBeTruthy();
expect(windowLocation.hash).toEqual("#/home");
});
});
});
describe("MatrixClient rehydrated from stored credentials:", function() {
beforeEach(async function() {
describe("MatrixClient rehydrated from stored credentials:", function () {
beforeEach(async function () {
localStorage.setItem("mx_hs_url", "http://localhost");
localStorage.setItem("mx_is_url", "http://localhost");
localStorage.setItem("mx_access_token", "access_token");
@ -292,63 +312,68 @@ describe('loading:', function() {
localStorage.setItem("mx_last_room_id", "!last_room:id");
// Create a crypto store as well to satisfy storage consistency checks
const cryptoStore = new IndexedDBCryptoStore(
indexedDB,
"matrix-js-sdk:crypto",
);
const cryptoStore = new IndexedDBCryptoStore(indexedDB, "matrix-js-sdk:crypto");
await cryptoStore.startup();
});
it('shows the last known room by default', function() {
it("shows the last known room by default", function () {
loadApp();
return awaitLoggedIn(matrixChat).then(() => {
// we are logged in - let the sync complete
return expectAndAwaitSync();
}).then(() => {
// once the sync completes, we should have a room view
return awaitRoomView(matrixChat);
}).then(() => {
httpBackend.verifyNoOutstandingExpectation();
expect(windowLocation.hash).toEqual("#/room/!last_room:id");
});
return awaitLoggedIn(matrixChat)
.then(() => {
// we are logged in - let the sync complete
return expectAndAwaitSync();
})
.then(() => {
// once the sync completes, we should have a room view
return awaitRoomView(matrixChat);
})
.then(() => {
httpBackend.verifyNoOutstandingExpectation();
expect(windowLocation.hash).toEqual("#/room/!last_room:id");
});
});
it('shows a home page by default if we have no joined rooms', function() {
it("shows a home page by default if we have no joined rooms", function () {
localStorage.removeItem("mx_last_room_id");
loadApp();
return awaitLoggedIn(matrixChat).then(() => {
// we are logged in - let the sync complete
return expectAndAwaitSync();
}).then(() => {
// once the sync completes, we should have a home page
httpBackend.verifyNoOutstandingExpectation();
expect(matrixChat.container.querySelector(".mx_HomePage")).toBeTruthy();
expect(windowLocation.hash).toEqual("#/home");
});
return awaitLoggedIn(matrixChat)
.then(() => {
// we are logged in - let the sync complete
return expectAndAwaitSync();
})
.then(() => {
// once the sync completes, we should have a home page
httpBackend.verifyNoOutstandingExpectation();
expect(matrixChat.container.querySelector(".mx_HomePage")).toBeTruthy();
expect(windowLocation.hash).toEqual("#/home");
});
});
it('shows a room view if we followed a room link', function() {
it("shows a room view if we followed a room link", function () {
loadApp({
uriFragment: "#/room/!room:id",
});
return awaitLoggedIn(matrixChat).then(() => {
// we are logged in - let the sync complete
return expectAndAwaitSync();
}).then(() => {
// once the sync completes, we should have a room view
return awaitRoomView(matrixChat);
}).then(() => {
httpBackend.verifyNoOutstandingExpectation();
expect(windowLocation.hash).toEqual("#/room/!room:id");
});
return awaitLoggedIn(matrixChat)
.then(() => {
// we are logged in - let the sync complete
return expectAndAwaitSync();
})
.then(() => {
// once the sync completes, we should have a room view
return awaitRoomView(matrixChat);
})
.then(() => {
httpBackend.verifyNoOutstandingExpectation();
expect(windowLocation.hash).toEqual("#/room/!room:id");
});
});
describe('/#/login link:', function() {
beforeEach(function() {
describe("/#/login link:", function () {
beforeEach(function () {
loadApp({
uriFragment: "#/login",
});
@ -357,7 +382,7 @@ describe('loading:', function() {
return expectAndAwaitSync();
});
it('does not show a login view', async function() {
it("does not show a login view", async function () {
await awaitRoomView(matrixChat);
await screen.findByLabelText("Spaces");
@ -366,136 +391,165 @@ describe('loading:', function() {
});
});
describe('Guest auto-registration:', function() {
it('shows a welcome page by default', function() {
describe("Guest auto-registration:", function () {
it("shows a welcome page by default", function () {
loadApp();
return sleep(1).then(async () => {
// at this point, we're trying to do a guest registration;
// we expect a spinner
await assertAtLoadingSpinner();
return sleep(1)
.then(async () => {
// at this point, we're trying to do a guest registration;
// we expect a spinner
await assertAtLoadingSpinner();
httpBackend.when('POST', '/register').check(function(req) {
expect(req.queryParams.kind).toEqual('guest');
}).respond(200, {
user_id: "@guest:localhost",
access_token: "secret_token",
httpBackend
.when("POST", "/register")
.check(function (req) {
expect(req.queryParams.kind).toEqual("guest");
})
.respond(200, {
user_id: "@guest:localhost",
access_token: "secret_token",
});
return httpBackend.flush();
})
.then(() => {
return awaitLoggedIn(matrixChat);
})
.then(() => {
// we are logged in - let the sync complete
return expectAndAwaitSync({ isGuest: true });
})
.then(() => {
// once the sync completes, we should have a welcome page
httpBackend.verifyNoOutstandingExpectation();
expect(matrixChat.container.querySelector(".mx_Welcome")).toBeTruthy();
expect(windowLocation.hash).toEqual("#/welcome");
});
return httpBackend.flush();
}).then(() => {
return awaitLoggedIn(matrixChat);
}).then(() => {
// we are logged in - let the sync complete
return expectAndAwaitSync({ isGuest: true });
}).then(() => {
// once the sync completes, we should have a welcome page
httpBackend.verifyNoOutstandingExpectation();
expect(matrixChat.container.querySelector(".mx_Welcome")).toBeTruthy();
expect(windowLocation.hash).toEqual("#/welcome");
});
});
it('uses the default homeserver to register with', function() {
it("uses the default homeserver to register with", function () {
loadApp();
return sleep(1).then(async () => {
// at this point, we're trying to do a guest registration;
// we expect a spinner
await assertAtLoadingSpinner();
return sleep(1)
.then(async () => {
// at this point, we're trying to do a guest registration;
// we expect a spinner
await assertAtLoadingSpinner();
httpBackend.when('POST', '/register').check(function(req) {
httpBackend
.when("POST", "/register")
.check(function (req) {
expect(req.path.startsWith(DEFAULT_HS_URL)).toBe(true);
expect(req.queryParams.kind).toEqual("guest");
})
.respond(200, {
user_id: "@guest:localhost",
access_token: "secret_token",
});
return httpBackend.flush();
})
.then(() => {
return awaitLoggedIn(matrixChat);
})
.then(() => {
return expectAndAwaitSync({ isGuest: true });
})
.then((req) => {
expect(req.path.startsWith(DEFAULT_HS_URL)).toBe(true);
expect(req.queryParams.kind).toEqual('guest');
}).respond(200, {
user_id: "@guest:localhost",
access_token: "secret_token",
// once the sync completes, we should have a welcome page
httpBackend.verifyNoOutstandingExpectation();
expect(matrixChat.container.querySelector(".mx_Welcome")).toBeTruthy();
expect(windowLocation.hash).toEqual("#/welcome");
expect(MatrixClientPeg.get().baseUrl).toEqual(DEFAULT_HS_URL);
expect(MatrixClientPeg.get().idBaseUrl).toEqual(DEFAULT_IS_URL);
});
return httpBackend.flush();
}).then(() => {
return awaitLoggedIn(matrixChat);
}).then(() => {
return expectAndAwaitSync({ isGuest: true });
}).then((req) => {
expect(req.path.startsWith(DEFAULT_HS_URL)).toBe(true);
// once the sync completes, we should have a welcome page
httpBackend.verifyNoOutstandingExpectation();
expect(matrixChat.container.querySelector(".mx_Welcome")).toBeTruthy();
expect(windowLocation.hash).toEqual("#/welcome");
expect(MatrixClientPeg.get().baseUrl).toEqual(DEFAULT_HS_URL);
expect(MatrixClientPeg.get().idBaseUrl).toEqual(DEFAULT_IS_URL);
});
});
it('shows a room view if we followed a room link', function() {
it("shows a room view if we followed a room link", function () {
loadApp({
uriFragment: "#/room/!room:id",
});
return sleep(1).then(async () => {
// at this point, we're trying to do a guest registration;
// we expect a spinner
await assertAtLoadingSpinner();
return sleep(1)
.then(async () => {
// at this point, we're trying to do a guest registration;
// we expect a spinner
await assertAtLoadingSpinner();
httpBackend.when('POST', '/register').check(function(req) {
expect(req.queryParams.kind).toEqual('guest');
}).respond(200, {
user_id: "@guest:localhost",
access_token: "secret_token",
httpBackend
.when("POST", "/register")
.check(function (req) {
expect(req.queryParams.kind).toEqual("guest");
})
.respond(200, {
user_id: "@guest:localhost",
access_token: "secret_token",
});
return httpBackend.flush();
})
.then(() => {
return awaitLoggedIn(matrixChat);
})
.then(() => {
return expectAndAwaitSync({ isGuest: true });
})
.then(() => {
// once the sync completes, we should have a room view
return awaitRoomView(matrixChat);
})
.then(() => {
httpBackend.verifyNoOutstandingExpectation();
expect(windowLocation.hash).toEqual("#/room/!room:id");
});
return httpBackend.flush();
}).then(() => {
return awaitLoggedIn(matrixChat);
}).then(() => {
return expectAndAwaitSync({ isGuest: true });
}).then(() => {
// once the sync completes, we should have a room view
return awaitRoomView(matrixChat);
}).then(() => {
httpBackend.verifyNoOutstandingExpectation();
expect(windowLocation.hash).toEqual("#/room/!room:id");
});
});
describe('Login as user', function() {
beforeEach(function() {
describe("Login as user", function () {
beforeEach(function () {
// first we have to load the homepage
loadApp();
httpBackend.when('POST', '/register').check(function(req) {
expect(req.queryParams.kind).toEqual('guest');
}).respond(200, {
user_id: "@guest:localhost",
access_token: "secret_token",
});
httpBackend
.when("POST", "/register")
.check(function (req) {
expect(req.queryParams.kind).toEqual("guest");
})
.respond(200, {
user_id: "@guest:localhost",
access_token: "secret_token",
});
return httpBackend.flush().then(() => {
return awaitLoggedIn(matrixChat);
}).then(() => {
// we got a sync spinner - let the sync complete
return expectAndAwaitSync();
}).then(async () => {
// once the sync completes, we should have a home page
await waitFor(() => matrixChat.container.querySelector(".mx_HomePage"));
return httpBackend
.flush()
.then(() => {
return awaitLoggedIn(matrixChat);
})
.then(() => {
// we got a sync spinner - let the sync complete
return expectAndAwaitSync();
})
.then(async () => {
// once the sync completes, we should have a home page
await waitFor(() => matrixChat.container.querySelector(".mx_HomePage"));
// we simulate a click on the 'login' button by firing off
// the relevant dispatch.
//
// XXX: is it an anti-pattern to access the react-sdk's
// dispatcher in this way? Is it better to find the login
// button and simulate a click? (we might have to arrange
// for it to be shown - it's not always, due to the
// collapsing left panel
// we simulate a click on the 'login' button by firing off
// the relevant dispatch.
//
// XXX: is it an anti-pattern to access the react-sdk's
// dispatcher in this way? Is it better to find the login
// button and simulate a click? (we might have to arrange
// for it to be shown - it's not always, due to the
// collapsing left panel
dis.dispatch({ action: 'start_login' });
dis.dispatch({ action: "start_login" });
return awaitLoginComponent(matrixChat);
});
return awaitLoginComponent(matrixChat);
});
});
it('should give us a login page', async function() {
it("should give us a login page", async function () {
// we expect a single <Login> component
await screen.findByRole("main");
screen.getAllByText("Sign in");
@ -505,44 +559,50 @@ describe('loading:', function() {
});
});
describe('Token login:', function() {
it('logs in successfully', function() {
describe("Token login:", function () {
it("logs in successfully", function () {
localStorage.setItem("mx_sso_hs_url", "https://homeserver");
localStorage.setItem("mx_sso_is_url", "https://idserver");
loadApp({
queryString: "?loginToken=secretToken",
});
return sleep(1).then(async () => {
// we expect a spinner while we're logging in
await assertAtLoadingSpinner();
return sleep(1)
.then(async () => {
// we expect a spinner while we're logging in
await assertAtLoadingSpinner();
httpBackend.when('POST', '/login').check(function(req) {
expect(req.path).toMatch(new RegExp("^https://homeserver/"));
expect(req.data.type).toEqual("m.login.token");
expect(req.data.token).toEqual("secretToken");
}).respond(200, {
user_id: "@user:localhost",
device_id: 'DEVICE_ID',
access_token: "access_token",
httpBackend
.when("POST", "/login")
.check(function (req) {
expect(req.path).toMatch(new RegExp("^https://homeserver/"));
expect(req.data.type).toEqual("m.login.token");
expect(req.data.token).toEqual("secretToken");
})
.respond(200, {
user_id: "@user:localhost",
device_id: "DEVICE_ID",
access_token: "access_token",
});
return httpBackend.flush();
})
.then(() => {
// at this point, MatrixChat should fire onTokenLoginCompleted, which
// makes index.js reload the app. We're not going to attempt to
// simulate the reload - just check that things are left in the
// right state for the reloaded app.
return tokenLoginCompletePromise;
})
.then(() => {
// check that the localstorage has been set up in such a way that
// the reloaded app can pick up where we leave off.
expect(localStorage.getItem("mx_user_id")).toEqual("@user:localhost");
expect(localStorage.getItem("mx_access_token")).toEqual("access_token");
expect(localStorage.getItem("mx_hs_url")).toEqual("https://homeserver");
expect(localStorage.getItem("mx_is_url")).toEqual("https://idserver");
});
return httpBackend.flush();
}).then(() => {
// at this point, MatrixChat should fire onTokenLoginCompleted, which
// makes index.js reload the app. We're not going to attempt to
// simulate the reload - just check that things are left in the
// right state for the reloaded app.
return tokenLoginCompletePromise;
}).then(() => {
// check that the localstorage has been set up in such a way that
// the reloaded app can pick up where we leave off.
expect(localStorage.getItem('mx_user_id')).toEqual('@user:localhost');
expect(localStorage.getItem('mx_access_token')).toEqual('access_token');
expect(localStorage.getItem('mx_hs_url')).toEqual('https://homeserver');
expect(localStorage.getItem('mx_is_url')).toEqual('https://idserver');
});
});
});
@ -551,38 +611,44 @@ describe('loading:', function() {
async function completeLogin(matrixChat: RenderResult): Promise<void> {
// When we switch to the login component, it'll hit the login endpoint
// for proof of life and to get flows. We'll only give it one option.
httpBackend.when('GET', '/login')
.respond(200, { flows: [{ type: "m.login.password" }] });
httpBackend.when("GET", "/login").respond(200, { flows: [{ type: "m.login.password" }] });
httpBackend.flush(); // We already would have tried the GET /login request
// Give the component some time to finish processing the login flows before
// continuing.
await sleep(100);
httpBackend.when('POST', '/login').check(function(req) {
expect(req.data.type).toEqual('m.login.password');
expect(req.data.identifier.type).toEqual('m.id.user');
expect(req.data.identifier.user).toEqual('user');
expect(req.data.password).toEqual('pass');
}).respond(200, {
user_id: '@user:id',
device_id: 'DEVICE_ID',
access_token: 'access_token',
});
httpBackend
.when("POST", "/login")
.check(function (req) {
expect(req.data.type).toEqual("m.login.password");
expect(req.data.identifier.type).toEqual("m.id.user");
expect(req.data.identifier.user).toEqual("user");
expect(req.data.password).toEqual("pass");
})
.respond(200, {
user_id: "@user:id",
device_id: "DEVICE_ID",
access_token: "access_token",
});
fireEvent.change(matrixChat.container.querySelector("#mx_LoginForm_username"), { target: { value: "user" } });
fireEvent.change(matrixChat.container.querySelector("#mx_LoginForm_password"), { target: { value: "pass" } });
fireEvent.click(screen.getByText("Sign in", { selector: ".mx_Login_submit" }));
return httpBackend.flush().then(() => {
// Wait for another trip around the event loop for the UI to update
return sleep(1);
}).then(() => {
return expectAndAwaitSync().catch((e) => {
throw new Error("Never got /sync after login: did the client start?");
return httpBackend
.flush()
.then(() => {
// Wait for another trip around the event loop for the UI to update
return sleep(1);
})
.then(() => {
return expectAndAwaitSync().catch((e) => {
throw new Error("Never got /sync after login: did the client start?");
});
})
.then(() => {
httpBackend.verifyNoOutstandingExpectation();
});
}).then(() => {
httpBackend.verifyNoOutstandingExpectation();
});
}
});
@ -594,7 +660,7 @@ async function assertAtLoadingSpinner(): Promise<void> {
async function awaitLoggedIn(matrixChat: RenderResult): Promise<void> {
if (matrixChat.container.querySelector(".mx_MatrixChat_wrapper")) return; // already logged in
return new Promise(resolve => {
return new Promise((resolve) => {
const onAction = ({ action }): void => {
if (action !== "on_logged_in") {
return;
@ -621,6 +687,6 @@ async function awaitWelcomeComponent(matrixChat: RenderResult): Promise<void> {
}
function moveFromWelcomeToLogin(matrixChat: RenderResult): Promise<void> {
dis.dispatch({ action: 'start_login' });
dis.dispatch({ action: "start_login" });
return awaitLoginComponent(matrixChat);
}

View file

@ -15,9 +15,9 @@ limitations under the License.
*/
// https://jestjs.io/docs/en/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom
Object.defineProperty(window, 'matchMedia', {
Object.defineProperty(window, "matchMedia", {
writable: true,
value: jest.fn().mockImplementation(query => ({
value: jest.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,

View file

@ -34,14 +34,12 @@ export function deleteIndexedDB(dbName: string): Promise<void> {
};
req.onerror = (ev): void => {
reject(new Error(
`${Date.now()}: unable to delete indexeddb ${dbName}: ${req.error}`,
));
reject(new Error(`${Date.now()}: unable to delete indexeddb ${dbName}: ${req.error}`));
};
req.onsuccess = (): void => {
const now = Date.now();
console.log(`${now}: Removed indexeddb instance: ${dbName} in ${now-startTime} ms`);
console.log(`${now}: Removed indexeddb instance: ${dbName} in ${now - startTime} ms`);
resolve();
};
}).catch((e) => {

View file

@ -20,88 +20,88 @@ import { getVectorConfig } from "../../../src/vector/getconfig";
fetchMock.config.overwriteRoutes = true;
describe('getVectorConfig()', () => {
describe("getVectorConfig()", () => {
const prevDocumentDomain = document.domain;
const elementDomain = 'app.element.io';
const elementDomain = "app.element.io";
const now = 1234567890;
const specificConfig = {
brand: 'specific',
brand: "specific",
};
const generalConfig = {
brand: 'general',
brand: "general",
};
beforeEach(() => {
document.domain = elementDomain;
// stable value for cachebuster
jest.spyOn(Date, 'now').mockReturnValue(now);
jest.spyOn(Date, "now").mockReturnValue(now);
jest.clearAllMocks();
fetchMock.mockClear();
});
afterAll(() => {
document.domain = prevDocumentDomain;
jest.spyOn(Date, 'now').mockRestore();
jest.spyOn(Date, "now").mockRestore();
});
it('requests specific config for document domain', async () => {
it("requests specific config for document domain", async () => {
fetchMock.getOnce("express:/config.app.element.io.json", specificConfig);
fetchMock.getOnce("express:/config.json", generalConfig);
await expect(getVectorConfig()).resolves.toEqual(specificConfig);
});
it('adds trailing slash to relativeLocation when not an empty string', async () => {
it("adds trailing slash to relativeLocation when not an empty string", async () => {
fetchMock.getOnce("express:../config.app.element.io.json", specificConfig);
fetchMock.getOnce("express:../config.json", generalConfig);
await expect(getVectorConfig("..")).resolves.toEqual(specificConfig);
});
it('returns general config when specific config succeeds but is empty', async () => {
it("returns general config when specific config succeeds but is empty", async () => {
fetchMock.getOnce("express:/config.app.element.io.json", {});
fetchMock.getOnce("express:/config.json", generalConfig);
await expect(getVectorConfig()).resolves.toEqual(generalConfig);
});
it('returns general config when specific config 404s', async () => {
it("returns general config when specific config 404s", async () => {
fetchMock.getOnce("express:/config.app.element.io.json", { status: 404 });
fetchMock.getOnce("express:/config.json", generalConfig);
await expect(getVectorConfig()).resolves.toEqual(generalConfig);
});
it('returns general config when specific config is fetched from a file and is empty', async () => {
it("returns general config when specific config is fetched from a file and is empty", async () => {
fetchMock.getOnce("express:/config.app.element.io.json", 0);
fetchMock.getOnce("express:/config.json", generalConfig);
await expect(getVectorConfig()).resolves.toEqual(generalConfig);
});
it('returns general config when specific config returns a non-200 status', async () => {
it("returns general config when specific config returns a non-200 status", async () => {
fetchMock.getOnce("express:/config.app.element.io.json", { status: 401 });
fetchMock.getOnce("express:/config.json", generalConfig);
await expect(getVectorConfig()).resolves.toEqual(generalConfig);
});
it('returns general config when specific config returns an error', async () => {
it("returns general config when specific config returns an error", async () => {
fetchMock.getOnce("express:/config.app.element.io.json", { throws: "err1" });
fetchMock.getOnce("express:/config.json", generalConfig);
await expect(getVectorConfig()).resolves.toEqual(generalConfig);
});
it('rejects with an error when general config rejects', async () => {
it("rejects with an error when general config rejects", async () => {
fetchMock.getOnce("express:/config.app.element.io.json", { throws: "err-specific" });
fetchMock.getOnce("express:/config.json", { throws: "err-general" });
await expect(getVectorConfig()).rejects.toBe("err-general");
});
it('rejects with an error when config is invalid JSON', async () => {
it("rejects with an error when config is invalid JSON", async () => {
fetchMock.getOnce("express:/config.app.element.io.json", { throws: "err-specific" });
fetchMock.getOnce("express:/config.json", '{"invalid": "json",}');

View file

@ -14,33 +14,34 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { logger } from 'matrix-js-sdk/src/logger';
import { MatrixEvent, Room } from 'matrix-js-sdk/src/matrix';
import { UpdateCheckStatus } from 'matrix-react-sdk/src/BasePlatform';
import { Action } from 'matrix-react-sdk/src/dispatcher/actions';
import dispatcher from 'matrix-react-sdk/src/dispatcher/dispatcher';
import * as rageshake from 'matrix-react-sdk/src/rageshake/rageshake';
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
import { UpdateCheckStatus } from "matrix-react-sdk/src/BasePlatform";
import { Action } from "matrix-react-sdk/src/dispatcher/actions";
import dispatcher from "matrix-react-sdk/src/dispatcher/dispatcher";
import * as rageshake from "matrix-react-sdk/src/rageshake/rageshake";
import ElectronPlatform from '../../../../src/vector/platform/ElectronPlatform';
import ElectronPlatform from "../../../../src/vector/platform/ElectronPlatform";
jest.mock('matrix-react-sdk/src/rageshake/rageshake', () => ({
jest.mock("matrix-react-sdk/src/rageshake/rageshake", () => ({
flush: jest.fn(),
}));
describe('ElectronPlatform', () => {
const defaultUserAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 ' +
'(KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36';
describe("ElectronPlatform", () => {
const defaultUserAgent =
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36";
const mockElectron = {
on: jest.fn(),
send: jest.fn(),
};
const dispatchSpy = jest.spyOn(dispatcher, 'dispatch');
const dispatchFireSpy = jest.spyOn(dispatcher, 'fire');
const logSpy = jest.spyOn(logger, 'log').mockImplementation(() => {});
const dispatchSpy = jest.spyOn(dispatcher, "dispatch");
const dispatchFireSpy = jest.spyOn(dispatcher, "fire");
const logSpy = jest.spyOn(logger, "log").mockImplementation(() => {});
const userId = '@alice:server.org';
const deviceId = 'device-id';
const userId = "@alice:server.org";
const deviceId = "device-id";
window.electron = mockElectron;
beforeEach(() => {
@ -53,9 +54,9 @@ describe('ElectronPlatform', () => {
const getElectronEventHandlerCall = (eventType: string): [type: string, handler: Function] | undefined =>
mockElectron.on.mock.calls.find(([type]) => type === eventType);
it('flushes rageshake before quitting', () => {
it("flushes rageshake before quitting", () => {
new ElectronPlatform();
const [event, handler] = getElectronEventHandlerCall('before-quit');
const [event, handler] = getElectronEventHandlerCall("before-quit");
// correct event bound
expect(event).toBeTruthy();
@ -65,9 +66,9 @@ describe('ElectronPlatform', () => {
expect(rageshake.flush).toHaveBeenCalled();
});
it('dispatches view settings action on preferences event', () => {
it("dispatches view settings action on preferences event", () => {
new ElectronPlatform();
const [event, handler] = getElectronEventHandlerCall('preferences');
const [event, handler] = getElectronEventHandlerCall("preferences");
// correct event bound
expect(event).toBeTruthy();
@ -76,10 +77,10 @@ describe('ElectronPlatform', () => {
expect(dispatchFireSpy).toHaveBeenCalledWith(Action.ViewUserSettings);
});
describe('updates', () => {
it('dispatches on check updates action', () => {
describe("updates", () => {
it("dispatches on check updates action", () => {
new ElectronPlatform();
const [event, handler] = getElectronEventHandlerCall('check_updates');
const [event, handler] = getElectronEventHandlerCall("check_updates");
// correct event bound
expect(event).toBeTruthy();
@ -90,9 +91,9 @@ describe('ElectronPlatform', () => {
});
});
it('dispatches on check updates action when update not available', () => {
it("dispatches on check updates action when update not available", () => {
new ElectronPlatform();
const [, handler] = getElectronEventHandlerCall('check_updates');
const [, handler] = getElectronEventHandlerCall("check_updates");
handler({}, false);
expect(dispatchSpy).toHaveBeenCalledWith({
@ -101,55 +102,42 @@ describe('ElectronPlatform', () => {
});
});
it('starts update check', () => {
it("starts update check", () => {
const platform = new ElectronPlatform();
platform.startUpdateCheck();
expect(mockElectron.send).toHaveBeenCalledWith('check_updates');
expect(mockElectron.send).toHaveBeenCalledWith("check_updates");
});
it('installs update', () => {
it("installs update", () => {
const platform = new ElectronPlatform();
platform.installUpdate();
expect(mockElectron.send).toHaveBeenCalledWith('install_update');
expect(mockElectron.send).toHaveBeenCalledWith("install_update");
});
});
it('returns human readable name', () => {
it("returns human readable name", () => {
const platform = new ElectronPlatform();
expect(platform.getHumanReadableName()).toEqual('Electron Platform');
expect(platform.getHumanReadableName()).toEqual("Electron Platform");
});
describe("getDefaultDeviceDisplayName", () => {
it.each([[
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
"Element Desktop: macOS",
],
[
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " +
"electron/1.0.0 Chrome/53.0.2785.113 Electron/1.4.3 Safari/537.36",
"Element Desktop: Windows",
],
[
"Mozilla/5.0 (X11; Linux i686; rv:21.0) Gecko/20100101 Firefox/21.0",
"Element Desktop: Linux",
],
[
"Mozilla/5.0 (X11; FreeBSD i686; rv:21.0) Gecko/20100101 Firefox/21.0",
"Element Desktop: FreeBSD",
],
[
"Mozilla/5.0 (X11; OpenBSD i686; rv:21.0) Gecko/20100101 Firefox/21.0",
"Element Desktop: OpenBSD",
],
[
"Mozilla/5.0 (X11; SunOS i686; rv:21.0) Gecko/20100101 Firefox/21.0",
"Element Desktop: SunOS",
],
[
"custom user agent",
"Element Desktop: Unknown",
]])("%s = %s", (userAgent, result) => {
it.each([
[
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
"Element Desktop: macOS",
],
[
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " +
"electron/1.0.0 Chrome/53.0.2785.113 Electron/1.4.3 Safari/537.36",
"Element Desktop: Windows",
],
["Mozilla/5.0 (X11; Linux i686; rv:21.0) Gecko/20100101 Firefox/21.0", "Element Desktop: Linux"],
["Mozilla/5.0 (X11; FreeBSD i686; rv:21.0) Gecko/20100101 Firefox/21.0", "Element Desktop: FreeBSD"],
["Mozilla/5.0 (X11; OpenBSD i686; rv:21.0) Gecko/20100101 Firefox/21.0", "Element Desktop: OpenBSD"],
["Mozilla/5.0 (X11; SunOS i686; rv:21.0) Gecko/20100101 Firefox/21.0", "Element Desktop: SunOS"],
["custom user agent", "Element Desktop: Unknown"],
])("%s = %s", (userAgent, result) => {
delete window.navigator;
window.navigator = { userAgent } as unknown as Navigator;
const platform = new ElectronPlatform();
@ -157,119 +145,119 @@ describe('ElectronPlatform', () => {
});
});
it('returns true for needsUrlTooltips', () => {
it("returns true for needsUrlTooltips", () => {
const platform = new ElectronPlatform();
expect(platform.needsUrlTooltips()).toBe(true);
});
it('should override browser shortcuts', () => {
it("should override browser shortcuts", () => {
const platform = new ElectronPlatform();
expect(platform.overrideBrowserShortcuts()).toBe(true);
});
it('allows overriding native context menus', () => {
it("allows overriding native context menus", () => {
const platform = new ElectronPlatform();
expect(platform.allowOverridingNativeContextMenus()).toBe(true);
});
it('indicates support for desktop capturer', () => {
it("indicates support for desktop capturer", () => {
const platform = new ElectronPlatform();
expect(platform.supportsDesktopCapturer()).toBe(true);
});
it('indicates no support for jitsi screensharing', () => {
it("indicates no support for jitsi screensharing", () => {
const platform = new ElectronPlatform();
expect(platform.supportsJitsiScreensharing()).toBe(false);
});
describe('notifications', () => {
it('indicates support for notifications', () => {
describe("notifications", () => {
it("indicates support for notifications", () => {
const platform = new ElectronPlatform();
expect(platform.supportsNotifications()).toBe(true);
});
it('may send notifications', () => {
it("may send notifications", () => {
const platform = new ElectronPlatform();
expect(platform.maySendNotifications()).toBe(true);
});
it('pretends to request notification permission', async () => {
it("pretends to request notification permission", async () => {
const platform = new ElectronPlatform();
const result = await platform.requestNotificationPermission();
expect(result).toEqual('granted');
expect(result).toEqual("granted");
});
it('creates a loud notification', async () => {
it("creates a loud notification", async () => {
const platform = new ElectronPlatform();
platform.loudNotification(new MatrixEvent(), new Room('!room:server', {} as any, userId));
expect(mockElectron.send).toHaveBeenCalledWith('loudNotification');
platform.loudNotification(new MatrixEvent(), new Room("!room:server", {} as any, userId));
expect(mockElectron.send).toHaveBeenCalledWith("loudNotification");
});
it('sets notification count when count is changing', async () => {
it("sets notification count when count is changing", async () => {
const platform = new ElectronPlatform();
platform.setNotificationCount(0);
// not called because matches internal notificaiton count
expect(mockElectron.send).not.toHaveBeenCalledWith('setBadgeCount', 0);
expect(mockElectron.send).not.toHaveBeenCalledWith("setBadgeCount", 0);
platform.setNotificationCount(1);
expect(mockElectron.send).toHaveBeenCalledWith('setBadgeCount', 1);
expect(mockElectron.send).toHaveBeenCalledWith("setBadgeCount", 1);
});
});
describe('spellcheck', () => {
it('indicates support for spellcheck settings', () => {
describe("spellcheck", () => {
it("indicates support for spellcheck settings", () => {
const platform = new ElectronPlatform();
expect(platform.supportsSpellCheckSettings()).toBe(true);
});
it('gets available spellcheck languages', () => {
it("gets available spellcheck languages", () => {
const platform = new ElectronPlatform();
mockElectron.send.mockClear();
platform.getAvailableSpellCheckLanguages();
const [channel, { name }] = mockElectron.send.mock.calls[0];
expect(channel).toEqual("ipcCall");
expect(name).toEqual('getAvailableSpellCheckLanguages');
expect(name).toEqual("getAvailableSpellCheckLanguages");
});
});
describe('pickle key', () => {
it('makes correct ipc call to get pickle key', () => {
describe("pickle key", () => {
it("makes correct ipc call to get pickle key", () => {
const platform = new ElectronPlatform();
mockElectron.send.mockClear();
platform.getPickleKey(userId, deviceId);
const [, { name, args }] = mockElectron.send.mock.calls[0];
expect(name).toEqual('getPickleKey');
expect(name).toEqual("getPickleKey");
expect(args).toEqual([userId, deviceId]);
});
it('makes correct ipc call to create pickle key', () => {
it("makes correct ipc call to create pickle key", () => {
const platform = new ElectronPlatform();
mockElectron.send.mockClear();
platform.createPickleKey(userId, deviceId);
const [, { name, args }] = mockElectron.send.mock.calls[0];
expect(name).toEqual('createPickleKey');
expect(name).toEqual("createPickleKey");
expect(args).toEqual([userId, deviceId]);
});
it('makes correct ipc call to destroy pickle key', () => {
it("makes correct ipc call to destroy pickle key", () => {
const platform = new ElectronPlatform();
mockElectron.send.mockClear();
platform.destroyPickleKey(userId, deviceId);
const [, { name, args }] = mockElectron.send.mock.calls[0];
expect(name).toEqual('destroyPickleKey');
expect(name).toEqual("destroyPickleKey");
expect(args).toEqual([userId, deviceId]);
});
});
describe('versions', () => {
it('calls install update', () => {
describe("versions", () => {
it("calls install update", () => {
const platform = new ElectronPlatform();
platform.installUpdate();
expect(mockElectron.send).toHaveBeenCalledWith('install_update');
expect(mockElectron.send).toHaveBeenCalledWith("install_update");
});
});
});

View file

@ -21,7 +21,7 @@ import WebPlatform from "../../../../src/vector/platform/WebPlatform";
jest.mock("../../../../src/vector/platform/WebPlatform");
describe('PWAPlatform', () => {
describe("PWAPlatform", () => {
beforeEach(() => {
jest.clearAllMocks();
});
@ -54,7 +54,7 @@ describe('PWAPlatform', () => {
});
it("should handle Navigator::setAppBadge rejecting gracefully", () => {
navigator.setAppBadge = jest.fn().mockRejectedValue(new Error);
navigator.setAppBadge = jest.fn().mockRejectedValue(new Error());
const platform = new PWAPlatform();
expect(() => platform.setNotificationCount(123)).not.toThrow();
});

View file

@ -15,24 +15,24 @@ limitations under the License.
*/
import fetchMock from "fetch-mock-jest";
import { UpdateCheckStatus } from 'matrix-react-sdk/src/BasePlatform';
import { MatrixClientPeg } from 'matrix-react-sdk/src/MatrixClientPeg';
import { UpdateCheckStatus } from "matrix-react-sdk/src/BasePlatform";
import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg";
import WebPlatform from '../../../../src/vector/platform/WebPlatform';
import WebPlatform from "../../../../src/vector/platform/WebPlatform";
fetchMock.config.overwriteRoutes = true;
describe('WebPlatform', () => {
describe("WebPlatform", () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('returns human readable name', () => {
it("returns human readable name", () => {
const platform = new WebPlatform();
expect(platform.getHumanReadableName()).toEqual('Web Platform');
expect(platform.getHumanReadableName()).toEqual("Web Platform");
});
it('registers service worker', () => {
it("registers service worker", () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - mocking readonly object
navigator.serviceWorker = { register: jest.fn() };
@ -65,12 +65,14 @@ describe('WebPlatform', () => {
});
describe("getDefaultDeviceDisplayName", () => {
it.each([[
"https://develop.element.io/#/room/!foo:bar",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) " +
"Chrome/105.0.0.0 Safari/537.36",
"develop.element.io: Chrome on macOS",
]])("%s & %s = %s", (url, userAgent, result) => {
it.each([
[
"https://develop.element.io/#/room/!foo:bar",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) " +
"Chrome/105.0.0.0 Safari/537.36",
"develop.element.io: Chrome on macOS",
],
])("%s & %s = %s", (url, userAgent, result) => {
delete window.navigator;
window.navigator = { userAgent } as unknown as Navigator;
delete window.location;
@ -80,66 +82,66 @@ describe('WebPlatform', () => {
});
});
describe('notification support', () => {
describe("notification support", () => {
const mockNotification = {
requestPermission: jest.fn(),
permission: 'notGranted',
permission: "notGranted",
};
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.Notification = mockNotification;
mockNotification.permission = 'notGranted';
mockNotification.permission = "notGranted";
});
it('supportsNotifications returns false when platform does not support notifications', () => {
it("supportsNotifications returns false when platform does not support notifications", () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.Notification = undefined;
expect(new WebPlatform().supportsNotifications()).toBe(false);
});
it('supportsNotifications returns true when platform supports notifications', () => {
it("supportsNotifications returns true when platform supports notifications", () => {
expect(new WebPlatform().supportsNotifications()).toBe(true);
});
it('maySendNotifications returns true when notification permissions are not granted', () => {
it("maySendNotifications returns true when notification permissions are not granted", () => {
expect(new WebPlatform().maySendNotifications()).toBe(false);
});
it('maySendNotifications returns true when notification permissions are granted', () => {
mockNotification.permission = 'granted';
it("maySendNotifications returns true when notification permissions are granted", () => {
mockNotification.permission = "granted";
expect(new WebPlatform().maySendNotifications()).toBe(true);
});
it('requests notification permissions and returns result ', async () => {
mockNotification.requestPermission.mockImplementation(callback => callback('test'));
it("requests notification permissions and returns result ", async () => {
mockNotification.requestPermission.mockImplementation((callback) => callback("test"));
const platform = new WebPlatform();
const result = await platform.requestNotificationPermission();
expect(result).toEqual('test');
expect(result).toEqual("test");
});
});
describe('app version', () => {
describe("app version", () => {
const envVersion = process.env.VERSION;
const prodVersion = '1.10.13';
const prodVersion = "1.10.13";
beforeEach(() => {
jest.spyOn(MatrixClientPeg, 'userRegisteredWithinLastHours').mockReturnValue(false);
jest.spyOn(MatrixClientPeg, "userRegisteredWithinLastHours").mockReturnValue(false);
});
afterAll(() => {
process.env.VERSION = envVersion;
});
it('should return true from canSelfUpdate()', async () => {
it("should return true from canSelfUpdate()", async () => {
const platform = new WebPlatform();
const result = await platform.canSelfUpdate();
expect(result).toBe(true);
});
it('getAppVersion returns normalized app version', async () => {
it("getAppVersion returns normalized app version", async () => {
process.env.VERSION = prodVersion;
const platform = new WebPlatform();
@ -156,23 +158,26 @@ describe('WebPlatform', () => {
expect(notSemverVersion).toEqual(`version not like semver`);
});
describe('pollForUpdate()', () => {
it('should return not available and call showNoUpdate when current version ' +
'matches most recent version', async () => {
process.env.VERSION = prodVersion;
fetchMock.getOnce("/version", prodVersion);
const platform = new WebPlatform();
describe("pollForUpdate()", () => {
it(
"should return not available and call showNoUpdate when current version " +
"matches most recent version",
async () => {
process.env.VERSION = prodVersion;
fetchMock.getOnce("/version", prodVersion);
const platform = new WebPlatform();
const showUpdate = jest.fn();
const showNoUpdate = jest.fn();
const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
const showUpdate = jest.fn();
const showNoUpdate = jest.fn();
const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
expect(result).toEqual({ status: UpdateCheckStatus.NotAvailable });
expect(showUpdate).not.toHaveBeenCalled();
expect(showNoUpdate).toHaveBeenCalled();
});
expect(result).toEqual({ status: UpdateCheckStatus.NotAvailable });
expect(showUpdate).not.toHaveBeenCalled();
expect(showNoUpdate).toHaveBeenCalled();
},
);
it('should strip v prefix from versions before comparing', async () => {
it("should strip v prefix from versions before comparing", async () => {
process.env.VERSION = prodVersion;
fetchMock.getOnce("/version", `v${prodVersion}`);
const platform = new WebPlatform();
@ -187,24 +192,26 @@ describe('WebPlatform', () => {
expect(showNoUpdate).toHaveBeenCalled();
});
it('should return ready and call showUpdate when current version ' +
'differs from most recent version', async () => {
process.env.VERSION = '0.0.0'; // old version
fetchMock.getOnce("/version", prodVersion);
const platform = new WebPlatform();
it(
"should return ready and call showUpdate when current version " + "differs from most recent version",
async () => {
process.env.VERSION = "0.0.0"; // old version
fetchMock.getOnce("/version", prodVersion);
const platform = new WebPlatform();
const showUpdate = jest.fn();
const showNoUpdate = jest.fn();
const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
const showUpdate = jest.fn();
const showNoUpdate = jest.fn();
const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
expect(result).toEqual({ status: UpdateCheckStatus.Ready });
expect(showUpdate).toHaveBeenCalledWith('0.0.0', prodVersion);
expect(showNoUpdate).not.toHaveBeenCalled();
});
expect(result).toEqual({ status: UpdateCheckStatus.Ready });
expect(showUpdate).toHaveBeenCalledWith("0.0.0", prodVersion);
expect(showNoUpdate).not.toHaveBeenCalled();
},
);
it('should return ready without showing update when user registered in last 24', async () => {
process.env.VERSION = '0.0.0'; // old version
jest.spyOn(MatrixClientPeg, 'userRegisteredWithinLastHours').mockReturnValue(true);
it("should return ready without showing update when user registered in last 24", async () => {
process.env.VERSION = "0.0.0"; // old version
jest.spyOn(MatrixClientPeg, "userRegisteredWithinLastHours").mockReturnValue(true);
fetchMock.getOnce("/version", prodVersion);
const platform = new WebPlatform();
@ -217,7 +224,7 @@ describe('WebPlatform', () => {
expect(showNoUpdate).not.toHaveBeenCalled();
});
it('should return error when version check fails', async () => {
it("should return error when version check fails", async () => {
fetchMock.getOnce("/version", { throws: "oups" });
const platform = new WebPlatform();
@ -225,7 +232,7 @@ describe('WebPlatform', () => {
const showNoUpdate = jest.fn();
const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
expect(result).toEqual({ status: UpdateCheckStatus.Error, detail: 'Unknown Error' });
expect(result).toEqual({ status: UpdateCheckStatus.Error, detail: "Unknown Error" });
expect(showUpdate).not.toHaveBeenCalled();
expect(showNoUpdate).not.toHaveBeenCalled();
});

View file

@ -16,7 +16,7 @@ limitations under the License.
import { parseQsFromFragment, parseQs } from "../../../src/vector/url_utils";
describe("url_utils.ts", function() {
describe("url_utils.ts", function () {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const location: Location = {
@ -24,28 +24,28 @@ describe("url_utils.ts", function() {
search: "",
};
it("parseQsFromFragment", function() {
it("parseQsFromFragment", function () {
location.hash = "/home?foo=bar";
expect(parseQsFromFragment(location)).toEqual({
location: "home",
params: {
"foo": "bar",
foo: "bar",
},
});
});
describe("parseQs", function() {
describe("parseQs", function () {
location.search = "?foo=bar";
expect(parseQs(location)).toEqual({
"foo": "bar",
foo: "bar",
});
});
describe("parseQs with arrays", function() {
describe("parseQs with arrays", function () {
location.search = "?via=s1&via=s2&via=s2&foo=bar";
expect(parseQs(location)).toEqual({
"via": ["s1", "s2", "s2"],
"foo": "bar",
via: ["s1", "s2", "s2"],
foo: "bar",
});
});
});