From 2b6551d06aac930eaf947e2b97ecb36a1b60dd27 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 22 Apr 2021 16:17:53 +0100 Subject: [PATCH 01/13] Remove reliance on DOM API to generated message preview --- src/HtmlUtils.tsx | 13 ++++++++----- .../room-list/previews/MessageEventPreview.ts | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index 1dc342fac5..6b2568d68c 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -130,11 +130,14 @@ export function sanitizedHtmlNode(insaneHtml: string) { return
; } -export function sanitizedHtmlNodeInnerText(insaneHtml: string) { - const saneHtml = sanitizeHtml(insaneHtml, sanitizeHtmlParams); - const contentDiv = document.createElement("div"); - contentDiv.innerHTML = saneHtml; - return contentDiv.innerText; +export function getHtmlText(insaneHtml: string) { + return sanitizeHtml(insaneHtml, { + allowedTags: [], + allowedAttributes: {}, + selfClosing: [], + allowedSchemes: [], + disallowedTagsMode: 'discard', + }) } /** diff --git a/src/stores/room-list/previews/MessageEventPreview.ts b/src/stores/room-list/previews/MessageEventPreview.ts index deed7dcf2c..b900afc13f 100644 --- a/src/stores/room-list/previews/MessageEventPreview.ts +++ b/src/stores/room-list/previews/MessageEventPreview.ts @@ -20,7 +20,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { _t } from "../../../languageHandler"; import { getSenderName, isSelf, shouldPrefixMessagesIn } from "./utils"; import ReplyThread from "../../../components/views/elements/ReplyThread"; -import { sanitizedHtmlNodeInnerText } from "../../../HtmlUtils"; +import { getHtmlText } from "../../../HtmlUtils"; export class MessageEventPreview implements IPreview { public getTextFor(event: MatrixEvent, tagId?: TagID): string { @@ -55,7 +55,7 @@ export class MessageEventPreview implements IPreview { } if (hasHtml) { - body = sanitizedHtmlNodeInnerText(body); + body = getHtmlText(body); } if (msgtype === 'm.emote') { From cc5a7671a724a908571fc8c20d46cdb1751f650d Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Thu, 22 Apr 2021 11:39:58 -0400 Subject: [PATCH 02/13] Keep invite button separate from space info 60828913d22541b8295ea8cce2874a210be23887 caused the space info and invite buttons to have no separation when you are the only person in a space, since the margin was set on the face pile, which may be absent. Signed-off-by: Robin Townsend --- res/css/structures/_SpaceRoomView.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/res/css/structures/_SpaceRoomView.scss b/res/css/structures/_SpaceRoomView.scss index 2dbf0fe0fe..269f16beb7 100644 --- a/res/css/structures/_SpaceRoomView.scss +++ b/res/css/structures/_SpaceRoomView.scss @@ -214,12 +214,11 @@ $SpaceRoomViewInnerWidth: 428px; .mx_SpaceRoomView_info { display: inline-block; - margin: 0; + margin: 0 auto 0 0; } .mx_FacePile { display: inline-block; - margin-left: auto; margin-right: 12px; .mx_FacePile_faces { From a3e846685d795d81a369ff4b23f39ead31163095 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 22 Apr 2021 16:24:41 -0600 Subject: [PATCH 03/13] Add array utility tests (and support upsampling in resample) See contained diff. The upsampling is important for Voice Messages, but is being done here because it's easier to add all the tests at once. This also introduces a new Object utility - that will be tested on its own commit. --- src/utils/arrays.ts | 51 ++++++-- src/utils/objects.ts | 20 ++- test/arrays-test.ts | 294 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 354 insertions(+), 11 deletions(-) create mode 100644 test/arrays-test.ts diff --git a/src/utils/arrays.ts b/src/utils/arrays.ts index 8ab66dfb29..cea377bfe9 100644 --- a/src/utils/arrays.ts +++ b/src/utils/arrays.ts @@ -1,5 +1,5 @@ /* -Copyright 2020 The Matrix.org Foundation C.I.C. +Copyright 2020, 2021 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. @@ -15,23 +15,47 @@ limitations under the License. */ /** - * Quickly resample an array to have less data points. This isn't a perfect representation, - * though this does work best if given a large array to downsample to a much smaller array. - * @param {number[]} input The input array to downsample. + * Quickly resample an array to have less/more data points. If an input which is larger + * than the desired size is provided, it will be downsampled. Similarly, if the input + * is smaller than the desired size then it will be upsampled. + * @param {number[]} input The input array to resample. * @param {number} points The number of samples to end up with. - * @returns {number[]} The downsampled array. + * @returns {number[]} The resampled array. */ export function arrayFastResample(input: number[], points: number): number[] { - // Heavily inpired by matrix-media-repo (used with permission) + if (input.length === points) return input; // short-circuit a complicated call + + // Heavily inspired by matrix-media-repo (used with permission) // https://github.com/turt2live/matrix-media-repo/blob/abe72c87d2e29/util/util_audio/fastsample.go#L10 - const everyNth = Math.round(input.length / points); - const samples: number[] = []; - for (let i = 0; i < input.length; i += everyNth) { - samples.push(input[i]); + let samples: number[] = []; + if (input.length > points) { + // Danger: this loop can cause out of memory conditions if the input is too small. + const everyNth = Math.round(input.length / points); + for (let i = 0; i < input.length; i += everyNth) { + samples.push(input[i]); + } + } else { + // Smaller inputs mean we have to spread the values over the desired length. We + // end up overshooting the target length in doing this, so we'll resample down + // before returning. This recursion is risky, but mathematically should not go + // further than 1 level deep. + const spreadFactor = Math.ceil(points / input.length); + for (const val of input) { + samples.push(...arraySeed(val, spreadFactor)); + } + samples = arrayFastResample(samples, points); } + + // Sanity fill, just in case while (samples.length < points) { samples.push(input[input.length - 1]); } + + // Sanity trim, just in case + if (samples.length > points) { + samples = samples.slice(0, points); + } + return samples; } @@ -178,6 +202,13 @@ export class GroupedArray { constructor(private val: Map) { } + /** + * The value of this group, after all applicable alterations. + */ + public get value(): Map { + return this.val; + } + /** * Orders the grouping into an array using the provided key order. * @param keyOrder The key order. diff --git a/src/utils/objects.ts b/src/utils/objects.ts index e7f4f0f907..2c9361beba 100644 --- a/src/utils/objects.ts +++ b/src/utils/objects.ts @@ -1,5 +1,5 @@ /* -Copyright 2020 The Matrix.org Foundation C.I.C. +Copyright 2020, 2021 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. @@ -141,3 +141,21 @@ export function objectKeyChanges(a: O, b: O): (keyof O)[] { export function objectClone(obj: O): O { return JSON.parse(JSON.stringify(obj)); } + +/** + * Converts a series of entries to an object. + * @param entries The entries to convert. + * @returns The converted object. + */ +// NOTE: Deprecated once we have Object.fromEntries() support. +// @ts-ignore - return type is complaining about non-string keys, but we know better +export function objectFromEntries(entries: Iterable<[K, V]>): {[k: K]: V} { + const obj: { + // @ts-ignore - same as return type + [k: K]: V} = {}; + for (const e of entries) { + // @ts-ignore - same as return type + obj[e[0]] = e[1]; + } + return obj; +} diff --git a/test/arrays-test.ts b/test/arrays-test.ts new file mode 100644 index 0000000000..33c4ee452e --- /dev/null +++ b/test/arrays-test.ts @@ -0,0 +1,294 @@ +/* +Copyright 2021 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 { + arrayDiff, + arrayFastClone, + arrayFastResample, + arrayHasDiff, + arrayHasOrderChange, + arrayMerge, + arraySeed, + arrayUnion, + ArrayUtil, + GroupedArray, +} from "../src/utils/arrays"; +import {objectFromEntries} from "../src/utils/objects"; + +function expectSample(i: number, input: number[], expected: number[]) { + console.log(`Resample case index: ${i}`); // for debugging test failures + const result = arrayFastResample(input, expected.length); + expect(result).toBeDefined(); + expect(result).toHaveLength(expected.length); + expect(result).toEqual(expected); +} + +describe('arrays', () => { + describe('arrayFastResample', () => { + it('should downsample', () => { + [ + {input: [1, 2, 3, 4, 5], output: [1, 4]}, // Odd -> Even + {input: [1, 2, 3, 4, 5], output: [1, 3, 5]}, // Odd -> Odd + {input: [1, 2, 3, 4], output: [1, 2, 3]}, // Even -> Odd + {input: [1, 2, 3, 4], output: [1, 3]}, // Even -> Even + ].forEach((c, i) => expectSample(i, c.input, c.output)); + }); + + it('should upsample', () => { + [ + {input: [1, 2, 3], output: [1, 1, 2, 2, 3, 3]}, // Odd -> Even + {input: [1, 2, 3], output: [1, 1, 2, 2, 3]}, // Odd -> Odd + {input: [1, 2], output: [1, 1, 1, 2, 2]}, // Even -> Odd + {input: [1, 2], output: [1, 1, 1, 2, 2, 2]}, // Even -> Even + ].forEach((c, i) => expectSample(i, c.input, c.output)); + }); + + it('should maintain sample', () => { + [ + {input: [1, 2, 3], output: [1, 2, 3]}, // Odd + {input: [1, 2], output: [1, 2]}, // Even + ].forEach((c, i) => expectSample(i, c.input, c.output)); + }); + }); + + describe('arraySeed', () => { + it('should create an array of given length', () => { + const val = 1; + const output = [val, val, val]; + const result = arraySeed(val, output.length); + expect(result).toBeDefined(); + expect(result).toHaveLength(output.length); + expect(result).toEqual(output); + }); + it('should maintain pointers', () => { + const val = {}; // this works because `{} !== {}`, which is what toEqual checks + const output = [val, val, val]; + const result = arraySeed(val, output.length); + expect(result).toBeDefined(); + expect(result).toHaveLength(output.length); + expect(result).toEqual(output); + }); + }); + + describe('arrayFastClone', () => { + it('should break pointer reference on source array', () => { + const val = {}; // we'll test to make sure the values maintain pointers too + const input = [val, val, val]; + const result = arrayFastClone(input); + expect(result).toBeDefined(); + expect(result).toHaveLength(input.length); + expect(result).toEqual(input); // we want the array contents to match... + expect(result).not.toBe(input); // ... but be a different reference + }); + }); + + describe('arrayHasOrderChange', () => { + it('should flag true on B ordering difference', () => { + const a = [1, 2, 3]; + const b = [3, 2, 1]; + const result = arrayHasOrderChange(a, b); + expect(result).toBe(true); + }); + + it('should flag false on no ordering difference', () => { + const a = [1, 2, 3]; + const b = [1, 2, 3]; + const result = arrayHasOrderChange(a, b); + expect(result).toBe(false); + }); + + it('should flag true on A length > B length', () => { + const a = [1, 2, 3, 4]; + const b = [1, 2, 3]; + const result = arrayHasOrderChange(a, b); + expect(result).toBe(true); + }); + + it('should flag true on A length < B length', () => { + const a = [1, 2, 3]; + const b = [1, 2, 3, 4]; + const result = arrayHasOrderChange(a, b); + expect(result).toBe(true); + }); + }); + + describe('arrayHasDiff', () => { + it('should flag true on A length > B length', () => { + const a = [1, 2, 3, 4]; + const b = [1, 2, 3]; + const result = arrayHasDiff(a, b); + expect(result).toBe(true); + }); + + it('should flag true on A length < B length', () => { + const a = [1, 2, 3]; + const b = [1, 2, 3, 4]; + const result = arrayHasDiff(a, b); + expect(result).toBe(true); + }); + + it('should flag true on element differences', () => { + const a = [1, 2, 3]; + const b = [4, 5, 6]; + const result = arrayHasDiff(a, b); + expect(result).toBe(true); + }); + + it('should flag false if same but order different', () => { + const a = [1, 2, 3]; + const b = [3, 1, 2]; + const result = arrayHasDiff(a, b); + expect(result).toBe(false); + }); + + it('should flag false if same', () => { + const a = [1, 2, 3]; + const b = [1, 2, 3]; + const result = arrayHasDiff(a, b); + expect(result).toBe(false); + }); + }); + + describe('arrayDiff', () => { + it('should see added from A->B', () => { + const a = [1, 2, 3]; + const b = [1, 2, 3, 4]; + const result = arrayDiff(a, b); + expect(result).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.added).toHaveLength(1); + expect(result.removed).toHaveLength(0); + expect(result.added).toEqual([4]); + }); + + it('should see removed from A->B', () => { + const a = [1, 2, 3]; + const b = [1, 2]; + const result = arrayDiff(a, b); + expect(result).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.added).toHaveLength(0); + expect(result.removed).toHaveLength(1); + expect(result.removed).toEqual([3]); + }); + + it('should see added and removed in the same set', () => { + const a = [1, 2, 3]; + const b = [1, 2, 4]; // note diff + const result = arrayDiff(a, b); + expect(result).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.added).toHaveLength(1); + expect(result.removed).toHaveLength(1); + expect(result.added).toEqual([4]); + expect(result.removed).toEqual([3]); + }); + }); + + describe('arrayUnion', () => { + it('should return a union', () => { + const a = [1, 2, 3]; + const b = [1, 2, 4]; // note diff + const result = arrayUnion(a, b); + expect(result).toBeDefined(); + expect(result).toHaveLength(2); + expect(result).toEqual([1, 2]); + }); + + it('should return an empty array on no matches', () => { + const a = [1, 2, 3]; + const b = [4, 5, 6]; + const result = arrayUnion(a, b); + expect(result).toBeDefined(); + expect(result).toHaveLength(0); + }); + }); + + describe('arrayMerge', () => { + it('should merge 3 arrays with deduplication', () => { + const a = [1, 2, 3]; + const b = [1, 2, 4, 5]; // note missing 3 + const c = [6, 7, 8, 9]; + const result = arrayMerge(a, b, c); + expect(result).toBeDefined(); + expect(result).toHaveLength(9); + expect(result).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]); + }); + + it('should deduplicate a single array', () => { + // dev note: this is technically an edge case, but it is described behaviour if the + // function is only provided one function (it'll merge the array against itself) + const a = [1, 1, 2, 2, 3, 3]; + const result = arrayMerge(a); + expect(result).toBeDefined(); + expect(result).toHaveLength(3); + expect(result).toEqual([1, 2, 3]); + }); + }); + + describe('ArrayUtil', () => { + it('should maintain the pointer to the given array', () => { + const input = [1, 2, 3]; + const result = new ArrayUtil(input); + expect(result.value).toBe(input); + }); + + it('should group appropriately', () => { + const input = [['a', 1], ['b', 2], ['c', 3], ['a', 4], ['a', 5], ['b', 6]]; + const output = { + 'a': [['a', 1], ['a', 4], ['a', 5]], + 'b': [['b', 2], ['b', 6]], + 'c': [['c', 3]], + }; + const result = new ArrayUtil(input).groupBy(p => p[0]); + expect(result).toBeDefined(); + expect(result.value).toBeDefined(); + + const asObject = objectFromEntries(result.value.entries()); + expect(asObject).toMatchObject(output); + }); + }); + + describe('GroupedArray', () => { + it('should maintain the pointer to the given map', () => { + const input = new Map([ + ['a', [1, 2, 3]], + ['b', [7, 8, 9]], + ['c', [4, 5, 6]], + ]); + const result = new GroupedArray(input); + expect(result.value).toBe(input); + }); + + it('should ordering by the provided key order', () => { + const input = new Map([ + ['a', [1, 2, 3]], + ['b', [7, 8, 9]], // note counting diff + ['c', [4, 5, 6]], + ]); + const output = [4, 5, 6, 1, 2, 3, 7, 8, 9]; + const keyOrder = ['c', 'a', 'b']; // note weird order to cause the `output` to be strange + const result = new GroupedArray(input).orderBy(keyOrder); + expect(result).toBeDefined(); + expect(result.value).toBeDefined(); + expect(result.value).toEqual(output); + }); + }); +}); + From 772ff4e257930ae65bc3843f98de1740400f852e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 22 Apr 2021 19:28:56 -0600 Subject: [PATCH 04/13] Add object utility tests --- test/objects-test.ts | 262 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 test/objects-test.ts diff --git a/test/objects-test.ts b/test/objects-test.ts new file mode 100644 index 0000000000..912d371ba2 --- /dev/null +++ b/test/objects-test.ts @@ -0,0 +1,262 @@ +/* +Copyright 2021 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 { + objectClone, + objectDiff, + objectExcluding, + objectFromEntries, + objectHasDiff, + objectKeyChanges, + objectShallowClone, + objectWithOnly, +} from "../src/utils/objects"; + +describe('objects', () => { + describe('objectExcluding', () => { + it('should exclude the given properties', () => { + const input = {hello: "world", test: true}; + const output = {hello: "world"}; + const props = ["test", "doesnotexist"]; // we also make sure it doesn't explode on missing props + const result = objectExcluding(input, props); // any is to test the missing prop + expect(result).toBeDefined(); + expect(result).toMatchObject(output); + }); + }); + + describe('objectWithOnly', () => { + it('should exclusively use the given properties', () => { + const input = {hello: "world", test: true}; + const output = {hello: "world"}; + const props = ["hello", "doesnotexist"]; // we also make sure it doesn't explode on missing props + const result = objectWithOnly(input, props); // any is to test the missing prop + expect(result).toBeDefined(); + expect(result).toMatchObject(output); + }); + }); + + describe('objectShallowClone', () => { + it('should create a new object', () => { + const input = {test: 1}; + const result = objectShallowClone(input); + expect(result).toBeDefined(); + expect(result).not.toBe(input); + expect(result).toMatchObject(input); + }); + + it('should only clone the top level properties', () => { + const input = {a: 1, b: {c: 2}}; + const result = objectShallowClone(input); + expect(result).toBeDefined(); + expect(result).toMatchObject(input); + expect(result.b).toBe(input.b); + }); + + it('should support custom clone functions', () => { + const input = {a: 1, b: 2}; + const output = {a: 4, b: 8}; + const result = objectShallowClone(input, (k, v) => { + // XXX: inverted expectation for ease of assertion + expect(Object.keys(input)).toContain(k); + + return v * 4; + }); + expect(result).toBeDefined(); + expect(result).toMatchObject(output); + }); + }); + + describe('objectHasDiff', () => { + it('should return false for the same pointer', () => { + const a = {}; + const result = objectHasDiff(a, a); + expect(result).toBe(false); + }); + + it('should return true if keys for A > keys for B', () => { + const a = {a: 1, b: 2}; + const b = {a: 1}; + const result = objectHasDiff(a, b); + expect(result).toBe(true); + }); + + it('should return true if keys for A < keys for B', () => { + const a = {a: 1}; + const b = {a: 1, b: 2}; + const result = objectHasDiff(a, b); + expect(result).toBe(true); + }); + + it('should return false if the objects are the same but different pointers', () => { + const a = {a: 1, b: 2}; + const b = {a: 1, b: 2}; + const result = objectHasDiff(a, b); + expect(result).toBe(false); + }); + + it('should consider pointers when testing values', () => { + const a = {a: {}, b: 2}; // `{}` is shorthand for `new Object()` + const b = {a: {}, b: 2}; + const result = objectHasDiff(a, b); + expect(result).toBe(true); // even though the keys are the same, the value pointers vary + }); + }); + + describe('objectDiff', () => { + it('should return empty sets for the same object', () => { + const a = {a: 1, b: 2}; + const b = {a: 1, b: 2}; + const result = objectDiff(a, b); + expect(result).toBeDefined(); + expect(result.changed).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.changed).toHaveLength(0); + expect(result.added).toHaveLength(0); + expect(result.removed).toHaveLength(0); + }); + + it('should return empty sets for the same object pointer', () => { + const a = {a: 1, b: 2}; + const result = objectDiff(a, a); + expect(result).toBeDefined(); + expect(result.changed).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.changed).toHaveLength(0); + expect(result.added).toHaveLength(0); + expect(result.removed).toHaveLength(0); + }); + + it('should indicate when property changes are made', () => { + const a = {a: 1, b: 2}; + const b = {a: 11, b: 2}; + const result = objectDiff(a, b); + expect(result.changed).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.changed).toHaveLength(1); + expect(result.added).toHaveLength(0); + expect(result.removed).toHaveLength(0); + expect(result.changed).toEqual(['a']); + }); + + it('should indicate when properties are added', () => { + const a = {a: 1, b: 2}; + const b = {a: 1, b: 2, c: 3}; + const result = objectDiff(a, b); + expect(result.changed).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.changed).toHaveLength(0); + expect(result.added).toHaveLength(1); + expect(result.removed).toHaveLength(0); + expect(result.added).toEqual(['c']); + }); + + it('should indicate when properties are removed', () => { + const a = {a: 1, b: 2}; + const b = {a: 1}; + const result = objectDiff(a, b); + expect(result.changed).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.changed).toHaveLength(0); + expect(result.added).toHaveLength(0); + expect(result.removed).toHaveLength(1); + expect(result.removed).toEqual(['b']); + }); + + it('should indicate when multiple aspects change', () => { + const a = {a: 1, b: 2, c: 3}; + const b: (typeof a | {d: number}) = {a: 1, b: 22, d: 4}; + const result = objectDiff(a, b); + expect(result.changed).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.changed).toHaveLength(1); + expect(result.added).toHaveLength(1); + expect(result.removed).toHaveLength(1); + expect(result.changed).toEqual(['b']); + expect(result.removed).toEqual(['c']); + expect(result.added).toEqual(['d']); + }); + }); + + describe('objectKeyChanges', () => { + it('should return an empty set if no properties changed', () => { + const a = {a: 1, b: 2}; + const b = {a: 1, b: 2}; + const result = objectKeyChanges(a, b); + expect(result).toBeDefined(); + expect(result).toHaveLength(0); + }); + + it('should return an empty set if no properties changed for the same pointer', () => { + const a = {a: 1, b: 2}; + const result = objectKeyChanges(a, a); + expect(result).toBeDefined(); + expect(result).toHaveLength(0); + }); + + it('should return properties which were changed, added, or removed', () => { + const a = {a: 1, b: 2, c: 3}; + const b: (typeof a | {d: number}) = {a: 1, b: 22, d: 4}; + const result = objectKeyChanges(a, b); + expect(result).toBeDefined(); + expect(result).toHaveLength(3); + expect(result).toEqual(['c', 'd', 'b']); // order isn't important, but the test cares + }); + }); + + describe('objectClone', () => { + it('should deep clone an object', () => { + const a = { + hello: "world", + test: { + another: "property", + test: 42, + third: { + prop: true, + }, + }, + }; + const result = objectClone(a); + expect(result).toBeDefined(); + expect(result).not.toBe(a); + expect(result).toMatchObject(a); + expect(result.test).not.toBe(a.test); + expect(result.test.third).not.toBe(a.test.third); + }); + }); + + describe('objectFromEntries', () => { + it('should create an object from an array of entries', () => { + const output = {a: 1, b: 2, c: 3}; + const result = objectFromEntries(Object.entries(output)); + expect(result).toBeDefined(); + expect(result).toMatchObject(output); + }); + + it('should maintain pointers in values', () => { + const output = {a: {}, b: 2, c: 3}; + const result = objectFromEntries(Object.entries(output)); + expect(result).toBeDefined(); + expect(result).toMatchObject(output); + expect(result['a']).toBe(output.a); + }); + }); +}); From 21cae1502a3003b02d8700b60f94a5e6b27fb5a7 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 22 Apr 2021 19:51:11 -0600 Subject: [PATCH 05/13] Add map utility tests --- test/maps-test.ts | 245 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 test/maps-test.ts diff --git a/test/maps-test.ts b/test/maps-test.ts new file mode 100644 index 0000000000..5363ab3d03 --- /dev/null +++ b/test/maps-test.ts @@ -0,0 +1,245 @@ +/* +Copyright 2021 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 {EnhancedMap, mapDiff, mapKeyChanges} from "../src/utils/maps"; + +describe('maps', () => { + describe('mapDiff', () => { + it('should indicate no differences when the pointers are the same', () => { + const a = new Map([[1, 1], [2, 2], [3, 3]]); + const result = mapDiff(a, a); + expect(result).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.changed).toBeDefined(); + expect(result.added).toHaveLength(0); + expect(result.removed).toHaveLength(0); + expect(result.changed).toHaveLength(0); + }); + + it('should indicate no differences when there are none', () => { + const a = new Map([[1, 1], [2, 2], [3, 3]]); + const b = new Map([[1, 1], [2, 2], [3, 3]]); + const result = mapDiff(a, b); + expect(result).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.changed).toBeDefined(); + expect(result.added).toHaveLength(0); + expect(result.removed).toHaveLength(0); + expect(result.changed).toHaveLength(0); + }); + + it('should indicate added properties', () => { + const a = new Map([[1, 1], [2, 2], [3, 3]]); + const b = new Map([[1, 1], [2, 2], [3, 3], [4, 4]]); + const result = mapDiff(a, b); + expect(result).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.changed).toBeDefined(); + expect(result.added).toHaveLength(1); + expect(result.removed).toHaveLength(0); + expect(result.changed).toHaveLength(0); + expect(result.added).toEqual([4]); + }); + + it('should indicate removed properties', () => { + const a = new Map([[1, 1], [2, 2], [3, 3]]); + const b = new Map([[1, 1], [2, 2]]); + const result = mapDiff(a, b); + expect(result).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.changed).toBeDefined(); + expect(result.added).toHaveLength(0); + expect(result.removed).toHaveLength(1); + expect(result.changed).toHaveLength(0); + expect(result.removed).toEqual([3]); + }); + + it('should indicate changed properties', () => { + const a = new Map([[1, 1], [2, 2], [3, 3]]); + const b = new Map([[1, 1], [2, 2], [3, 4]]); // note change + const result = mapDiff(a, b); + expect(result).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.changed).toBeDefined(); + expect(result.added).toHaveLength(0); + expect(result.removed).toHaveLength(0); + expect(result.changed).toHaveLength(1); + expect(result.changed).toEqual([3]); + }); + + it('should indicate changed, added, and removed properties', () => { + const a = new Map([[1, 1], [2, 2], [3, 3]]); + const b = new Map([[1, 1], [2, 8], [4, 4]]); // note change + const result = mapDiff(a, b); + expect(result).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.changed).toBeDefined(); + expect(result.added).toHaveLength(1); + expect(result.removed).toHaveLength(1); + expect(result.changed).toHaveLength(1); + expect(result.added).toEqual([4]); + expect(result.removed).toEqual([3]); + expect(result.changed).toEqual([2]); + }); + + it('should indicate changes for difference in pointers', () => { + const a = new Map([[1, {}]]); // {} always creates a new object + const b = new Map([[1, {}]]); + const result = mapDiff(a, b); + expect(result).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.changed).toBeDefined(); + expect(result.added).toHaveLength(0); + expect(result.removed).toHaveLength(0); + expect(result.changed).toHaveLength(1); + expect(result.changed).toEqual([1]); + }); + }); + + describe('mapKeyChanges', () => { + it('should indicate no changes for unchanged pointers', () => { + const a = new Map([[1, 1], [2, 2], [3, 3]]); + const result = mapKeyChanges(a, a); + expect(result).toBeDefined(); + expect(result).toHaveLength(0); + }); + + it('should indicate no changes for unchanged maps with different pointers', () => { + const a = new Map([[1, 1], [2, 2], [3, 3]]); + const b = new Map([[1, 1], [2, 2], [3, 3]]); + const result = mapKeyChanges(a, b); + expect(result).toBeDefined(); + expect(result).toHaveLength(0); + }); + + it('should indicate changes for added properties', () => { + const a = new Map([[1, 1], [2, 2], [3, 3]]); + const b = new Map([[1, 1], [2, 2], [3, 3], [4, 4]]); + const result = mapKeyChanges(a, b); + expect(result).toBeDefined(); + expect(result).toHaveLength(1); + expect(result).toEqual([4]); + }); + + it('should indicate changes for removed properties', () => { + const a = new Map([[1, 1], [2, 2], [3, 3], [4, 4]]); + const b = new Map([[1, 1], [2, 2], [3, 3]]); + const result = mapKeyChanges(a, b); + expect(result).toBeDefined(); + expect(result).toHaveLength(1); + expect(result).toEqual([4]); + }); + + it('should indicate changes for changed properties', () => { + const a = new Map([[1, 1], [2, 2], [3, 3], [4, 4]]); + const b = new Map([[1, 1], [2, 2], [3, 3], [4, 55]]); + const result = mapKeyChanges(a, b); + expect(result).toBeDefined(); + expect(result).toHaveLength(1); + expect(result).toEqual([4]); + }); + + it('should indicate changes for properties with different pointers', () => { + const a = new Map([[1, {}]]); // {} always creates a new object + const b = new Map([[1, {}]]); + const result = mapKeyChanges(a, b); + expect(result).toBeDefined(); + expect(result).toHaveLength(1); + expect(result).toEqual([1]); + }); + + it('should indicate changes for changed, added, and removed properties', () => { + const a = new Map([[1, 1], [2, 2], [3, 3]]); + const b = new Map([[1, 1], [2, 8], [4, 4]]); // note change + const result = mapKeyChanges(a, b); + expect(result).toBeDefined(); + expect(result).toHaveLength(3); + expect(result).toEqual([3, 4, 2]); // order irrelevant, but the test cares + }); + }); + + describe('EnhancedMap', () => { + // Most of these tests will make sure it implements the Map class + + it('should be empty by default', () => { + const result = new EnhancedMap(); + expect(result.size).toBe(0); + }); + + it('should use the provided entries', () => { + const obj = {a: 1, b: 2}; + const result = new EnhancedMap(Object.entries(obj)); + expect(result.size).toBe(2); + expect(result.get('a')).toBe(1); + expect(result.get('b')).toBe(2); + }); + + it('should create keys if they do not exist', () => { + const key = 'a'; + const val = {}; // we'll check pointers + + const result = new EnhancedMap(); + expect(result.size).toBe(0); + + let get = result.getOrCreate(key, val); + expect(get).toBeDefined(); + expect(get).toBe(val); + expect(result.size).toBe(1); + + get = result.getOrCreate(key, 44); // specifically change `val` + expect(get).toBeDefined(); + expect(get).toBe(val); + expect(result.size).toBe(1); + + get = result.get(key); // use the base class function + expect(get).toBeDefined(); + expect(get).toBe(val); + expect(result.size).toBe(1); + }); + + it('should proxy remove to delete and return it', () => { + const val = {}; + const result = new EnhancedMap(); + result.set('a', val); + + expect(result.size).toBe(1); + + const removed = result.remove('a'); + expect(result.size).toBe(0); + expect(removed).toBeDefined(); + expect(removed).toBe(val); + }); + + it('should support removing unknown keys', () => { + const val = {}; + const result = new EnhancedMap(); + result.set('a', val); + + expect(result.size).toBe(1); + + const removed = result.remove('not-a'); + expect(result.size).toBe(1); + expect(removed).not.toBeDefined(); + }); + }); +}); From 0d4218ee35371ddd0354ffd8d770927c8abe20f4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 22 Apr 2021 20:07:38 -0600 Subject: [PATCH 06/13] Add enum utility tests --- src/utils/enums.ts | 22 +++++++++++---- test/enums-test.ts | 67 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 test/enums-test.ts diff --git a/src/utils/enums.ts b/src/utils/enums.ts index f7f4787896..d3ca318c28 100644 --- a/src/utils/enums.ts +++ b/src/utils/enums.ts @@ -1,5 +1,5 @@ /* -Copyright 2020 The Matrix.org Foundation C.I.C. +Copyright 2020, 2021 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. @@ -19,11 +19,23 @@ limitations under the License. * @param e The enum. * @returns The enum values. */ -export function getEnumValues(e: any): T[] { +export function getEnumValues(e: any): (string | number)[] { + // String-based enums will simply be objects ({Key: "value"}), but number-based + // enums will instead map themselves twice: in one direction for {Key: 12} and + // the reverse for easy lookup, presumably ({12: Key}). In the reverse mapping, + // the key is a string, not a number. + // + // For this reason, we try to determine what kind of enum we're dealing with. + const keys = Object.keys(e); - return keys - .filter(k => ['string', 'number'].includes(typeof(e[k]))) - .map(k => e[k]); + const values: (string | number)[] = []; + for (const key of keys) { + const value = e[key]; + if (Number.isFinite(value) || e[value.toString()] !== Number(key)) { + values.push(value); + } + } + return values; } /** diff --git a/test/enums-test.ts b/test/enums-test.ts new file mode 100644 index 0000000000..e519186a51 --- /dev/null +++ b/test/enums-test.ts @@ -0,0 +1,67 @@ +/* +Copyright 2021 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 {getEnumValues, isEnumValue} from "../src/utils/enums"; + +enum TestStringEnum { + First = "__first__", + Second = "__second__", +} + +enum TestNumberEnum { + FirstKey = 10, + SecondKey = 20, +} + +describe('enums', () => { + describe('getEnumValues', () => { + it('should work on string enums', () => { + const result = getEnumValues(TestStringEnum); + expect(result).toBeDefined(); + expect(result).toHaveLength(2); + expect(result).toEqual(['__first__', '__second__']); + }); + + it('should work on number enums', () => { + const result = getEnumValues(TestNumberEnum); + expect(result).toBeDefined(); + expect(result).toHaveLength(2); + expect(result).toEqual([10, 20]); + }); + }); + + describe('isEnumValue', () => { + it('should return true on values in a string enum', () => { + const result = isEnumValue(TestStringEnum, '__first__'); + expect(result).toBe(true); + }); + + it('should return false on values not in a string enum', () => { + const result = isEnumValue(TestStringEnum, 'not a value'); + expect(result).toBe(false); + }); + + it('should return true on values in a number enum', () => { + const result = isEnumValue(TestNumberEnum, 10); + expect(result).toBe(true); + }); + + it('should return false on values not in a number enum', () => { + const result = isEnumValue(TestStringEnum, 99); + expect(result).toBe(false); + }); + }); +}); From 6124a8319b803ce36e91d84f60597e4b28499b1e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 22 Apr 2021 20:11:20 -0600 Subject: [PATCH 07/13] Add iterable utility tests Unsurprisingly, it's a copy/paste of the array tests --- test/iterables-test.ts | 77 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 test/iterables-test.ts diff --git a/test/iterables-test.ts b/test/iterables-test.ts new file mode 100644 index 0000000000..af0232fb93 --- /dev/null +++ b/test/iterables-test.ts @@ -0,0 +1,77 @@ +/* +Copyright 2021 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 {iterableDiff, iterableUnion} from "../src/utils/iterables"; + +describe('iterables', () => { + describe('iterableUnion', () => { + it('should return a union', () => { + const a = [1, 2, 3]; + const b = [1, 2, 4]; // note diff + const result = iterableUnion(a, b); + expect(result).toBeDefined(); + expect(result).toHaveLength(2); + expect(result).toEqual([1, 2]); + }); + + it('should return an empty array on no matches', () => { + const a = [1, 2, 3]; + const b = [4, 5, 6]; + const result = iterableUnion(a, b); + expect(result).toBeDefined(); + expect(result).toHaveLength(0); + }); + }); + + describe('iterableDiff', () => { + it('should see added from A->B', () => { + const a = [1, 2, 3]; + const b = [1, 2, 3, 4]; + const result = iterableDiff(a, b); + expect(result).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.added).toHaveLength(1); + expect(result.removed).toHaveLength(0); + expect(result.added).toEqual([4]); + }); + + it('should see removed from A->B', () => { + const a = [1, 2, 3]; + const b = [1, 2]; + const result = iterableDiff(a, b); + expect(result).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.added).toHaveLength(0); + expect(result.removed).toHaveLength(1); + expect(result.removed).toEqual([3]); + }); + + it('should see added and removed in the same set', () => { + const a = [1, 2, 3]; + const b = [1, 2, 4]; // note diff + const result = iterableDiff(a, b); + expect(result).toBeDefined(); + expect(result.added).toBeDefined(); + expect(result.removed).toBeDefined(); + expect(result.added).toHaveLength(1); + expect(result.removed).toHaveLength(1); + expect(result.added).toEqual([4]); + expect(result.removed).toEqual([3]); + }); + }); +}); From 27af3291ed85c3a95a0b139c992e10dbd2847c62 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 22 Apr 2021 20:26:48 -0600 Subject: [PATCH 08/13] Add number utility tests --- test/numbers-test.ts | 163 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 test/numbers-test.ts diff --git a/test/numbers-test.ts b/test/numbers-test.ts new file mode 100644 index 0000000000..6e0e3f58ce --- /dev/null +++ b/test/numbers-test.ts @@ -0,0 +1,163 @@ +/* +Copyright 2021 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 {clamp, defaultNumber, percentageOf, percentageWithin, sum} from "../src/utils/numbers"; + +describe('numbers', () => { + describe('defaultNumber', () => { + it('should use the default when the input is not a number', () => { + const def = 42; + + let result = defaultNumber(null, def); + expect(result).toBe(def); + + result = defaultNumber(undefined, def); + expect(result).toBe(def); + + result = defaultNumber(Number.NaN, def); + expect(result).toBe(def); + }); + + it('should use the number when it is a number', () => { + const input = 24; + const def = 42; + const result = defaultNumber(input, def); + expect(result).toBe(input); + }); + }); + + describe('clamp', () => { + it('should clamp high numbers', () => { + const input = 101; + const min = 0; + const max = 100; + const result = clamp(input, min, max); + expect(result).toBe(max); + }); + + it('should clamp low numbers', () => { + const input = -1; + const min = 0; + const max = 100; + const result = clamp(input, min, max); + expect(result).toBe(min); + }); + + it('should not clamp numbers in range', () => { + const input = 50; + const min = 0; + const max = 100; + const result = clamp(input, min, max); + expect(result).toBe(input); + }); + + it('should clamp floats', () => { + const min = -0.10; + const max = +0.10; + + let result = clamp(-1.2, min, max); + expect(result).toBe(min); + + result = clamp(1.2, min, max); + expect(result).toBe(max); + + result = clamp(0.02, min, max); + expect(result).toBe(0.02); + }); + }); + + describe('sum', () => { + it('should sum', () => { // duh + const result = sum(1, 2, 1, 4); + expect(result).toBe(8); + }); + }); + + describe('percentageWithin', () => { + it('should work within 0-100', () => { + const result = percentageWithin(0.4, 0, 100); + expect(result).toBe(40); + }); + + it('should work within 0-100 when pct > 1', () => { + const result = percentageWithin(1.4, 0, 100); + expect(result).toBe(140); + }); + + it('should work within 0-100 when pct < 0', () => { + const result = percentageWithin(-1.4, 0, 100); + expect(result).toBe(-140); + }); + + it('should work with ranges other than 0-100', () => { + const result = percentageWithin(0.4, 10, 20); + expect(result).toBe(14); + }); + + it('should work with ranges other than 0-100 when pct > 1', () => { + const result = percentageWithin(1.4, 10, 20); + expect(result).toBe(24); + }); + + it('should work with ranges other than 0-100 when pct < 0', () => { + const result = percentageWithin(-1.4, 10, 20); + expect(result).toBe(-4); + }); + + it('should work with floats', () => { + const result = percentageWithin(0.4, 10.2, 20.4); + expect(result).toBe(14.28); + }); + }); + + // These are the inverse of percentageWithin + describe('percentageOf', () => { + it('should work within 0-100', () => { + const result = percentageOf(40, 0, 100); + expect(result).toBe(0.4); + }); + + it('should work within 0-100 when val > 100', () => { + const result = percentageOf(140, 0, 100); + expect(result).toBe(1.40); + }); + + it('should work within 0-100 when val < 0', () => { + const result = percentageOf(-140, 0, 100); + expect(result).toBe(-1.40); + }); + + it('should work with ranges other than 0-100', () => { + const result = percentageOf(14, 10, 20); + expect(result).toBe(0.4); + }); + + it('should work with ranges other than 0-100 when val > 100', () => { + const result = percentageOf(24, 10, 20); + expect(result).toBe(1.4); + }); + + it('should work with ranges other than 0-100 when val < 0', () => { + const result = percentageOf(-4, 10, 20); + expect(result).toBe(-1.4); + }); + + it('should work with floats', () => { + const result = percentageOf(14.28, 10.2, 20.4); + expect(result).toBe(0.4); + }); + }); +}); From 374f51452ee364e4bc5f8921ef992ceebc562778 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 22 Apr 2021 20:28:49 -0600 Subject: [PATCH 09/13] Add set utility tests --- test/sets-test.ts | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 test/sets-test.ts diff --git a/test/sets-test.ts b/test/sets-test.ts new file mode 100644 index 0000000000..e884d0e9af --- /dev/null +++ b/test/sets-test.ts @@ -0,0 +1,56 @@ +/* +Copyright 2021 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 {setHasDiff} from "../src/utils/sets"; + +describe('sets', () => { + describe('setHasDiff', () => { + it('should flag true on A length > B length', () => { + const a = new Set([1, 2, 3, 4]); + const b = new Set([1, 2, 3]); + const result = setHasDiff(a, b); + expect(result).toBe(true); + }); + + it('should flag true on A length < B length', () => { + const a = new Set([1, 2, 3]); + const b = new Set([1, 2, 3, 4]); + const result = setHasDiff(a, b); + expect(result).toBe(true); + }); + + it('should flag true on element differences', () => { + const a = new Set([1, 2, 3]); + const b = new Set([4, 5, 6]); + const result = setHasDiff(a, b); + expect(result).toBe(true); + }); + + it('should flag false if same but order different', () => { + const a = new Set([1, 2, 3]); + const b = new Set([3, 1, 2]); + const result = setHasDiff(a, b); + expect(result).toBe(false); + }); + + it('should flag false if same', () => { + const a = new Set([1, 2, 3]); + const b = new Set([1, 2, 3]); + const result = setHasDiff(a, b); + expect(result).toBe(false); + }); + }); +}); From 2c459c482872a6314ab3749a1f04e6b056ff8460 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 22 Apr 2021 20:30:14 -0600 Subject: [PATCH 10/13] Move utility tests to the right place --- test/{ => utils}/Singleflight-test.ts | 2 +- test/{ => utils}/arrays-test.ts | 4 ++-- test/{ => utils}/enums-test.ts | 2 +- test/{ => utils}/iterables-test.ts | 2 +- test/{ => utils}/maps-test.ts | 2 +- test/{ => utils}/numbers-test.ts | 2 +- test/{ => utils}/objects-test.ts | 2 +- test/{ => utils}/sets-test.ts | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) rename test/{ => utils}/Singleflight-test.ts (98%) rename test/{ => utils}/arrays-test.ts (99%) rename test/{ => utils}/enums-test.ts (96%) rename test/{ => utils}/iterables-test.ts (97%) rename test/{ => utils}/maps-test.ts (99%) rename test/{ => utils}/numbers-test.ts (99%) rename test/{ => utils}/objects-test.ts (99%) rename test/{ => utils}/sets-test.ts (97%) diff --git a/test/Singleflight-test.ts b/test/utils/Singleflight-test.ts similarity index 98% rename from test/Singleflight-test.ts rename to test/utils/Singleflight-test.ts index 4f0c6e0da3..80258701bb 100644 --- a/test/Singleflight-test.ts +++ b/test/utils/Singleflight-test.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {Singleflight} from "../src/utils/Singleflight"; +import {Singleflight} from "../../src/utils/Singleflight"; describe('Singleflight', () => { afterEach(() => { diff --git a/test/arrays-test.ts b/test/utils/arrays-test.ts similarity index 99% rename from test/arrays-test.ts rename to test/utils/arrays-test.ts index 33c4ee452e..ececd274b2 100644 --- a/test/arrays-test.ts +++ b/test/utils/arrays-test.ts @@ -25,8 +25,8 @@ import { arrayUnion, ArrayUtil, GroupedArray, -} from "../src/utils/arrays"; -import {objectFromEntries} from "../src/utils/objects"; +} from "../../src/utils/arrays"; +import {objectFromEntries} from "../../src/utils/objects"; function expectSample(i: number, input: number[], expected: number[]) { console.log(`Resample case index: ${i}`); // for debugging test failures diff --git a/test/enums-test.ts b/test/utils/enums-test.ts similarity index 96% rename from test/enums-test.ts rename to test/utils/enums-test.ts index e519186a51..423b135f77 100644 --- a/test/enums-test.ts +++ b/test/utils/enums-test.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {getEnumValues, isEnumValue} from "../src/utils/enums"; +import {getEnumValues, isEnumValue} from "../../src/utils/enums"; enum TestStringEnum { First = "__first__", diff --git a/test/iterables-test.ts b/test/utils/iterables-test.ts similarity index 97% rename from test/iterables-test.ts rename to test/utils/iterables-test.ts index af0232fb93..9b30b6241c 100644 --- a/test/iterables-test.ts +++ b/test/utils/iterables-test.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {iterableDiff, iterableUnion} from "../src/utils/iterables"; +import {iterableDiff, iterableUnion} from "../../src/utils/iterables"; describe('iterables', () => { describe('iterableUnion', () => { diff --git a/test/maps-test.ts b/test/utils/maps-test.ts similarity index 99% rename from test/maps-test.ts rename to test/utils/maps-test.ts index 5363ab3d03..8764a8f2cf 100644 --- a/test/maps-test.ts +++ b/test/utils/maps-test.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {EnhancedMap, mapDiff, mapKeyChanges} from "../src/utils/maps"; +import {EnhancedMap, mapDiff, mapKeyChanges} from "../../src/utils/maps"; describe('maps', () => { describe('mapDiff', () => { diff --git a/test/numbers-test.ts b/test/utils/numbers-test.ts similarity index 99% rename from test/numbers-test.ts rename to test/utils/numbers-test.ts index 6e0e3f58ce..36e7d4f7e7 100644 --- a/test/numbers-test.ts +++ b/test/utils/numbers-test.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {clamp, defaultNumber, percentageOf, percentageWithin, sum} from "../src/utils/numbers"; +import {clamp, defaultNumber, percentageOf, percentageWithin, sum} from "../../src/utils/numbers"; describe('numbers', () => { describe('defaultNumber', () => { diff --git a/test/objects-test.ts b/test/utils/objects-test.ts similarity index 99% rename from test/objects-test.ts rename to test/utils/objects-test.ts index 912d371ba2..b7a80e6761 100644 --- a/test/objects-test.ts +++ b/test/utils/objects-test.ts @@ -23,7 +23,7 @@ import { objectKeyChanges, objectShallowClone, objectWithOnly, -} from "../src/utils/objects"; +} from "../../src/utils/objects"; describe('objects', () => { describe('objectExcluding', () => { diff --git a/test/sets-test.ts b/test/utils/sets-test.ts similarity index 97% rename from test/sets-test.ts rename to test/utils/sets-test.ts index e884d0e9af..98dc218309 100644 --- a/test/sets-test.ts +++ b/test/utils/sets-test.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {setHasDiff} from "../src/utils/sets"; +import {setHasDiff} from "../../src/utils/sets"; describe('sets', () => { describe('setHasDiff', () => { From 024cf7f66c7eb27cc354b4d56ac22ae1615c1c34 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Thu, 22 Apr 2021 11:44:33 -0400 Subject: [PATCH 11/13] Cut off long names in add rooms to space dialog Signed-off-by: Robin Townsend --- res/css/views/dialogs/_AddExistingToSpaceDialog.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/res/css/views/dialogs/_AddExistingToSpaceDialog.scss b/res/css/views/dialogs/_AddExistingToSpaceDialog.scss index 80ad4d6c0e..826bb3f7e9 100644 --- a/res/css/views/dialogs/_AddExistingToSpaceDialog.scss +++ b/res/css/views/dialogs/_AddExistingToSpaceDialog.scss @@ -148,6 +148,10 @@ limitations under the License. font-size: $font-15px; line-height: 30px; flex-grow: 1; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + margin-right: 12px; } .mx_FormButton { From 0f84b3dff315b0d19ac99a2685eaf56e81d41ea4 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Thu, 22 Apr 2021 11:46:31 -0400 Subject: [PATCH 12/13] Align checkboxes with names in add rooms to space dialog Signed-off-by: Robin Townsend --- res/css/views/dialogs/_AddExistingToSpaceDialog.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/res/css/views/dialogs/_AddExistingToSpaceDialog.scss b/res/css/views/dialogs/_AddExistingToSpaceDialog.scss index 826bb3f7e9..f288241e04 100644 --- a/res/css/views/dialogs/_AddExistingToSpaceDialog.scss +++ b/res/css/views/dialogs/_AddExistingToSpaceDialog.scss @@ -154,6 +154,10 @@ limitations under the License. margin-right: 12px; } + .mx_Checkbox { + align-items: center; + } + .mx_FormButton { min-width: 92px; font-weight: normal; From 3d7842d6964af7c2698abc706b5e3bafee584d5f Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Thu, 22 Apr 2021 11:46:56 -0400 Subject: [PATCH 13/13] Remove old FormButton CSS Signed-off-by: Robin Townsend --- res/css/views/dialogs/_AddExistingToSpaceDialog.scss | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/res/css/views/dialogs/_AddExistingToSpaceDialog.scss b/res/css/views/dialogs/_AddExistingToSpaceDialog.scss index f288241e04..247df52b4a 100644 --- a/res/css/views/dialogs/_AddExistingToSpaceDialog.scss +++ b/res/css/views/dialogs/_AddExistingToSpaceDialog.scss @@ -157,12 +157,6 @@ limitations under the License. .mx_Checkbox { align-items: center; } - - .mx_FormButton { - min-width: 92px; - font-weight: normal; - box-sizing: border-box; - } } } @@ -200,8 +194,4 @@ limitations under the License. padding: 0; } } - - .mx_FormButton { - padding: 8px 22px; - } }