Write additional tests (#22802)

* Write additional tests

* Make Sonar happier
This commit is contained in:
Michael Telatynski 2022-07-11 13:22:37 +01:00 committed by GitHub
parent a4f9e7adad
commit 017dfb6606
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 87 additions and 40 deletions

View file

@ -54,7 +54,7 @@ export default class Favicon {
private isReady = false;
// callback to run once isReady is asserted, allows for a badge to be queued for when it can be shown
private readyCb = () => {};
private readyCb?: () => void;
constructor(params: Partial<IParams> = {}) {
this.params = { ...defaults, ...params };
@ -180,7 +180,7 @@ export default class Favicon {
private ready() {
if (this.isReady) return;
this.isReady = true;
this.readyCb();
this.readyCb?.();
}
private setIcon(canvas) {
@ -230,9 +230,9 @@ export default class Favicon {
private static getLinks() {
const icons: HTMLLinkElement[] = [];
const links = window.document.getElementsByTagName("head")[0].getElementsByTagName("link");
for (let i = 0; i < links.length; i++) {
if ((/(^|\s)icon(\s|$)/i).test(links[i].getAttribute("rel"))) {
icons.push(links[i]);
for (const link of links) {
if ((/(^|\s)icon(\s|$)/i).test(link.getAttribute("rel"))) {
icons.push(link);
}
}
return icons;

View file

@ -92,8 +92,8 @@ export function loadOlm(): Promise<void> {
locateFile: () => olmWasmPath,
}).then(() => {
logger.log("Using WebAssembly Olm");
}).catch((e) => {
logger.log("Failed to load Olm: trying legacy version", e);
}).catch((wasmLoadError) => {
logger.log("Failed to load Olm: trying legacy version", wasmLoadError);
return new Promise((resolve, reject) => {
const s = document.createElement('script');
s.src = 'olm_legacy.js'; // XXX: This should be cache-busted too
@ -106,8 +106,8 @@ export function loadOlm(): Promise<void> {
return window.Olm.init();
}).then(() => {
logger.log("Using legacy Olm");
}).catch((e) => {
logger.log("Both WebAssembly and asm.js Olm failed!", e);
}).catch((legacyLoadError) => {
logger.log("Both WebAssembly and asm.js Olm failed!", legacyLoadError);
});
});
}

View file

@ -48,7 +48,7 @@ export class IPCManager {
return deferred.promise;
}
private onIpcReply = (ev: {}, payload: IPCPayload): void => {
private onIpcReply = (_ev: {}, payload: IPCPayload): void => {
if (payload.id === undefined) {
logger.warn("Ignoring IPC reply with no ID");
return;

View file

@ -47,7 +47,8 @@ export default abstract class VectorBasePlatform extends BasePlatform {
if (this._favicon) {
return this._favicon;
}
return this._favicon = new Favicon();
this._favicon = new Favicon();
return this._favicon;
}
private updateFavicon() {

View file

@ -80,7 +80,7 @@ export default class WebPlatform extends VectorBasePlatform {
// annoyingly, the latest spec says this returns a
// promise, but this is only supported in Chrome 46
// and Firefox 47, so adapt the callback API.
return new Promise(function(resolve, reject) {
return new Promise(function(resolve) {
window.Notification.requestPermission((result) => {
resolve(result);
});

View file

@ -39,7 +39,7 @@ export function initRageshake() {
logger.log("To fix line numbers in Chrome: " +
"Meatball menu → Settings → Ignore list → Add /rageshake\\.js$");
window.addEventListener('beforeunload', (e) => {
window.addEventListener('beforeunload', () => {
logger.log('element-web closing');
// try to flush the logs to indexeddb
rageshake.flush();

View file

@ -41,7 +41,7 @@ function routeUrl(location: Location) {
(window.matrixChat as MatrixChatType).showScreen(s.screen, s.params);
}
function onHashChange(ev: HashChangeEvent) {
function onHashChange() {
if (decodeURIComponent(window.location.hash) === lastLocationHashSet) {
// we just set this: no need to route it!
return;

View file

@ -48,16 +48,16 @@ describe('getVectorConfig()', () => {
it('requests specific config for document domain', async () => {
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(specificConfig))
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
await getVectorConfig();
expect(request.mock.calls[0][0]).toEqual({ method: "GET", url: 'config.app.element.io.json', qs: { cachebuster: now } })
});
it('adds trailing slash to relativeLocation when not an empty string', async () => {
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(specificConfig))
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
await getVectorConfig('..');
expect(request.mock.calls[0][0]).toEqual(expect.objectContaining({ url: '../config.app.element.io.json' }))
@ -67,7 +67,7 @@ describe('getVectorConfig()', () => {
it('returns parsed specific config when it is non-empty', async () => {
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(specificConfig))
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
const result = await getVectorConfig();
expect(result).toEqual(specificConfig);
});
@ -75,7 +75,7 @@ describe('getVectorConfig()', () => {
it('returns general config when specific config succeeds but is empty', async () => {
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify({}))
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
const result = await getVectorConfig();
expect(result).toEqual(generalConfig);
});
@ -83,7 +83,7 @@ describe('getVectorConfig()', () => {
it('returns general config when specific config 404s', async () => {
setRequestMockImplementationOnce(undefined, { status: 404 })
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
const result = await getVectorConfig();
expect(result).toEqual(generalConfig);
});
@ -91,7 +91,7 @@ describe('getVectorConfig()', () => {
it('returns general config when specific config is fetched from a file and is empty', async () => {
setRequestMockImplementationOnce(undefined, { status: 0 }, '')
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
const result = await getVectorConfig();
expect(result).toEqual(generalConfig);
});
@ -99,7 +99,7 @@ describe('getVectorConfig()', () => {
it('returns general config when specific config returns a non-200 status', async () => {
setRequestMockImplementationOnce(undefined, { status: 401 })
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
const result = await getVectorConfig();
expect(result).toEqual(generalConfig);
});
@ -107,7 +107,7 @@ describe('getVectorConfig()', () => {
it('returns general config when specific config returns an error', async () => {
setRequestMockImplementationOnce('err1')
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
const result = await getVectorConfig();
expect(result).toEqual(generalConfig);
});
@ -119,4 +119,12 @@ describe('getVectorConfig()', () => {
await expect(() => getVectorConfig()).rejects.toEqual({"err": "err-general", "response": undefined});
});
it('rejects with an error when config is invalid JSON', async () => {
setRequestMockImplementationOnce('err-specific');
setRequestMockImplementationOnce(undefined, { status: 200 }, '{"invalid": "json",}');
await expect(() => getVectorConfig()).rejects.toEqual({
err: new SyntaxError("Unexpected token } in JSON at position 19"),
});
});
});

View file

@ -0,0 +1,33 @@
/*
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 PWAPlatform from "../../../../src/vector/platform/PWAPlatform";
describe('PWAPlatform', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe("setNotificationCount", () => {
it("should call Navigator::setAppBadge", () => {
navigator.setAppBadge = jest.fn().mockResolvedValue(undefined);
const platform = new PWAPlatform();
expect(navigator.setAppBadge).not.toHaveBeenCalled();
platform.setNotificationCount(123);
expect(navigator.setAppBadge).toHaveBeenCalledWith(123);
});
});
});

View file

@ -30,6 +30,13 @@ describe('WebPlatform', () => {
expect(platform.getHumanReadableName()).toEqual('Web Platform');
});
it('registers service worker', () => {
// @ts-ignore - mocking readonly object
navigator.serviceWorker = { register: jest.fn() };
new WebPlatform();
expect(navigator.serviceWorker.register).toHaveBeenCalled();
});
describe('notification support', () => {
const mockNotification = {
requestPermission: jest.fn(),
@ -50,7 +57,7 @@ describe('WebPlatform', () => {
it('supportsNotifications returns true when platform supports notifications', () => {
expect(new WebPlatform().supportsNotifications()).toBe(true);
});
it('maySendNotifications returns true when notification permissions are not granted', () => {
expect(new WebPlatform().maySendNotifications()).toBe(false);
});
@ -109,78 +116,76 @@ describe('WebPlatform', () => {
});
describe('pollForUpdate()', () => {
it('should return not available and call showNoUpdate when current version matches most recent version', async () => {
process.env.VERSION = prodVersion;
setRequestMockImplementation(undefined, { status: 200}, prodVersion);
const platform = new WebPlatform();
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();
});
it('should strip v prefix from versions before comparing', async () => {
process.env.VERSION = prodVersion;
setRequestMockImplementation(undefined, { status: 200}, `v${prodVersion}`);
const platform = new WebPlatform();
const showUpdate = jest.fn();
const showNoUpdate = jest.fn();
const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
// versions only differ by v prefix, no update
expect(result).toEqual({ status: UpdateCheckStatus.NotAvailable });
expect(showUpdate).not.toHaveBeenCalled();
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
setRequestMockImplementation(undefined, { status: 200}, prodVersion);
const platform = new WebPlatform();
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();
});
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);
setRequestMockImplementation(undefined, { status: 200}, prodVersion);
const platform = new WebPlatform();
const showUpdate = jest.fn();
const showNoUpdate = jest.fn();
const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
expect(result).toEqual({ status: UpdateCheckStatus.Ready });
expect(showUpdate).not.toHaveBeenCalled();
expect(showNoUpdate).not.toHaveBeenCalled();
});
it('should return error when version check fails', async () => {
setRequestMockImplementation('oups');
const platform = new WebPlatform();
const showUpdate = jest.fn();
const showNoUpdate = jest.fn();
const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
expect(result).toEqual({ status: UpdateCheckStatus.Error, detail: 'Unknown Error' });
expect(showUpdate).not.toHaveBeenCalled();
expect(showNoUpdate).not.toHaveBeenCalled();
});
});
});
});