From 5b51efd8614aa358533bcad80791bd153319d87d Mon Sep 17 00:00:00 2001 From: r00ster Date: Mon, 23 May 2022 18:29:16 +0200 Subject: [PATCH] Make system fonts work more reliably (#8602) * Make system fonts work more reliably * Make it more sophisticated * Missing semicolon * Apply suggestions * Fix formatting Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> * Create FontWatcher-test.tsx * Add actual tests * Fix some errors * Apply suggestions * Apply suggestions from code review * Apply suggestions from code review * Apply suggestions from code review * Apply suggestions from code review * Fix FontWatcher tests * Correct test fixture Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> --- src/settings/watchers/FontWatcher.ts | 16 +++++- test/CallHandler-test.ts | 16 +----- test/settings/watchers/FontWatcher-test.tsx | 54 +++++++++++++++++++++ test/test-utils/utilities.ts | 16 ++++++ 4 files changed, 86 insertions(+), 16 deletions(-) create mode 100644 test/settings/watchers/FontWatcher-test.tsx diff --git a/src/settings/watchers/FontWatcher.ts b/src/settings/watchers/FontWatcher.ts index c70dd78349..88d2a70081 100644 --- a/src/settings/watchers/FontWatcher.ts +++ b/src/settings/watchers/FontWatcher.ts @@ -64,6 +64,20 @@ export class FontWatcher implements IWatcher { }; private setSystemFont = ({ useSystemFont, font }) => { - document.body.style.fontFamily = useSystemFont ? font : ""; + if (useSystemFont) { + // Make sure that fonts with spaces in their names get interpreted properly + document.body.style.fontFamily = font + .split(',') + .map(font => { + font = font.trim(); + if (!font.startsWith('"') && !font.endsWith('"')) { + font = `"${font}"`; + } + return font; + }) + .join(','); + } else { + document.body.style.fontFamily = ""; + } }; } diff --git a/test/CallHandler-test.ts b/test/CallHandler-test.ts index 01c1085032..6a4f29aaa1 100644 --- a/test/CallHandler-test.ts +++ b/test/CallHandler-test.ts @@ -21,12 +21,10 @@ import EventEmitter from 'events'; import CallHandler, { CallHandlerEvent, PROTOCOL_PSTN, PROTOCOL_PSTN_PREFIXED, PROTOCOL_SIP_NATIVE, PROTOCOL_SIP_VIRTUAL, } from '../src/CallHandler'; -import { stubClient, mkStubRoom } from './test-utils'; +import { stubClient, mkStubRoom, untilDispatch } from './test-utils'; import { MatrixClientPeg } from '../src/MatrixClientPeg'; -import dis from '../src/dispatcher/dispatcher'; import DMRoomMap from '../src/utils/DMRoomMap'; import SdkConfig from '../src/SdkConfig'; -import { ActionPayload } from '../src/dispatcher/payloads'; import { Action } from "../src/dispatcher/actions"; // The Matrix IDs that the user sees when talking to Alice & Bob @@ -95,18 +93,6 @@ class FakeCall extends EventEmitter { } } -function untilDispatch(waitForAction: string): Promise { - let dispatchHandle; - return new Promise(resolve => { - dispatchHandle = dis.register(payload => { - if (payload.action === waitForAction) { - dis.unregister(dispatchHandle); - resolve(payload); - } - }); - }); -} - function untilCallHandlerEvent(callHandler: CallHandler, event: CallHandlerEvent): Promise { return new Promise((resolve) => { callHandler.addListener(event, () => { diff --git a/test/settings/watchers/FontWatcher-test.tsx b/test/settings/watchers/FontWatcher-test.tsx new file mode 100644 index 0000000000..95f3085330 --- /dev/null +++ b/test/settings/watchers/FontWatcher-test.tsx @@ -0,0 +1,54 @@ +/* +Copyright 2022 r00ster91 + +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 { sleep } from 'matrix-js-sdk/src/utils'; + +import SettingsStore from '../../../src/settings/SettingsStore'; +import { SettingLevel } from '../../../src/settings/SettingLevel'; +import { FontWatcher } from "../../../src/settings/watchers/FontWatcher"; +import { Action } from "../../../src/dispatcher/actions"; +import { untilDispatch } from "../../test-utils"; + +async function setSystemFont(font: string): Promise { + await SettingsStore.setValue("systemFont", null, SettingLevel.DEVICE, font); + await untilDispatch(Action.UpdateSystemFont); + await sleep(1); // await the FontWatcher doing its action +} + +describe('FontWatcher', function() { + let fontWatcher: FontWatcher; + beforeEach(() => { + fontWatcher = new FontWatcher(); + fontWatcher.start(); + return SettingsStore.setValue("useSystemFont", null, SettingLevel.DEVICE, true); + }); + afterEach(() => { + fontWatcher.stop(); + }); + + it('encloses the fonts by double quotes and sets them as the system font', async () => { + await setSystemFont("Fira Sans Thin, Commodore 64"); + expect(document.body.style.fontFamily).toBe(`"Fira Sans Thin","Commodore 64"`); + }); + it('does not add double quotes if already present and sets the font as the system font', async () => { + await setSystemFont(`"Commodore 64"`); + expect(document.body.style.fontFamily).toBe(`"Commodore 64"`); + }); + it('trims whitespace, encloses the fonts by double quotes, and sets them as the system font', async () => { + await setSystemFont(` Fira Code , "Commodore 64" `); + expect(document.body.style.fontFamily).toBe(`"Fira Code","Commodore 64"`); + }); +}); diff --git a/test/test-utils/utilities.ts b/test/test-utils/utilities.ts index a4370fbc9e..d7da56407b 100644 --- a/test/test-utils/utilities.ts +++ b/test/test-utils/utilities.ts @@ -17,8 +17,24 @@ limitations under the License. import { ReactWrapper } from "enzyme"; import EventEmitter from "events"; +import { ActionPayload } from "../../src/dispatcher/payloads"; +import defaultDispatcher from "../../src/dispatcher/dispatcher"; +import { DispatcherAction } from "../../src/dispatcher/actions"; + export const emitPromise = (e: EventEmitter, k: string | symbol) => new Promise(r => e.once(k, r)); +export function untilDispatch(waitForAction: DispatcherAction): Promise { + let dispatchHandle: string; + return new Promise(resolve => { + dispatchHandle = defaultDispatcher.register(payload => { + if (payload.action === waitForAction) { + defaultDispatcher.unregister(dispatchHandle); + resolve(payload); + } + }); + }); +} + const findByAttr = (attr: string) => (component: ReactWrapper, value: string) => component.find(`[${attr}="${value}"]`); export const findByTestId = findByAttr('data-test-id'); export const findById = findByAttr('id');