fix(ui): isMacOS always returned false on Macs + plugin-logic tests (+49)

Coverage work found a 3rd real bug: isMacOS() compared os.name against the
legacy 'Mac OS' string, but ua-parser-js v2 reports 'macOS' — so it was dead,
and Mac users saw "Ctrl + k" instead of "⌘ + k" in the editor toolbar, search,
and settings shortcut hints. Now accepts both 'macOS' and 'Mac OS'.

Suites (via subagent, verified): via-servers (10 — power/popularity server
selection), bad-words (9), syntaxHighlight tokenize (14), plugins/utils
getEmoticonSearchStr (5), imageCompression formatFileSize/isCompressible (5),
user-agent (6, now asserting the fixed behavior).

Full suite now 501 tests, all passing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 14:58:06 -04:00
parent 24662fa994
commit 30d0331174
7 changed files with 465 additions and 1 deletions
+61
View File
@@ -0,0 +1,61 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { testBadWords, BAD_WORDS_REGEX } from './bad-words';
test('testBadWords returns false for clean text', () => {
assert.equal(testBadWords('hello world'), false);
assert.equal(testBadWords('this is clean text'), false);
assert.equal(testBadWords(''), false);
});
test('testBadWords matches custom additions', () => {
// 'torture' and 't0rture' are appended in additionalBadWords
assert.equal(testBadWords('torture'), true);
assert.equal(testBadWords('t0rture'), true);
});
test('testBadWords is case-insensitive', () => {
assert.equal(testBadWords('Torture'), true);
assert.equal(testBadWords('TORTURE'), true);
assert.equal(testBadWords('DamN'), true);
});
test('testBadWords matches words from the base list', () => {
assert.equal(testBadWords('damn'), true);
assert.equal(testBadWords('hell'), true);
assert.equal(testBadWords('crap'), true);
});
test('testBadWords respects word boundaries (no match inside a larger word)', () => {
// alphanumeric extension on either side prevents a match
assert.equal(testBadWords('tortured'), false);
assert.equal(testBadWords('tortures'), false);
assert.equal(testBadWords('damning'), false);
assert.equal(testBadWords('hello'), false);
assert.equal(testBadWords('shell'), false);
assert.equal(testBadWords('crappy'), false);
});
test('testBadWords does not match a bad word as a substring of an unrelated word', () => {
assert.equal(testBadWords('class'), false);
assert.equal(testBadWords('pass'), false);
assert.equal(testBadWords('grass'), false);
});
test('underscore acts as a word boundary', () => {
// surrounding underscores still allow a match, since `_` is a boundary char
assert.equal(testBadWords('word_torture_word'), true);
// but an alphanumeric char glued directly to the word still blocks it
assert.equal(testBadWords('tor_ture'), false);
});
test('testBadWords matches a bad word embedded in a sentence with spaces', () => {
assert.equal(testBadWords('what the hell is this'), true);
assert.equal(testBadWords('I think this is fine'), false);
});
test('BAD_WORDS_REGEX is a global regex; reuse it via String.match', () => {
// testBadWords lowercases the input before matching
assert.ok('torture'.toLowerCase().match(BAD_WORDS_REGEX));
assert.equal('innocent'.toLowerCase().match(BAD_WORDS_REGEX), null);
});
+39
View File
@@ -0,0 +1,39 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { getEmoticonSearchStr } from './utils';
import { PackImageReader } from './custom-emoji';
import { IEmoji } from './emoji';
test('getEmoticonSearchStr for a PackImageReader with a body returns shortcode + body', () => {
const reader = new PackImageReader('cat', 'mxc://server/cat', { body: 'kitten' });
assert.deepEqual(getEmoticonSearchStr(reader), [':cat:', 'kitten']);
});
test('getEmoticonSearchStr for a PackImageReader without a body returns just the shortcode string', () => {
const reader = new PackImageReader('cat', 'mxc://server/cat', {});
assert.equal(getEmoticonSearchStr(reader), ':cat:');
});
test('getEmoticonSearchStr ignores a non-string body on a PackImageReader', () => {
const reader = new PackImageReader('cat', 'mxc://server/cat', {
body: 123 as unknown as string,
});
assert.equal(getEmoticonSearchStr(reader), ':cat:');
});
test('getEmoticonSearchStr for an IEmoji concats shortcode, label and shortcodes', () => {
const emoji = {
shortcode: 'smile',
label: 'Smiling Face',
shortcodes: ['smile', 'happy'],
} as unknown as IEmoji;
assert.deepEqual(getEmoticonSearchStr(emoji), [':smile:', 'Smiling Face', 'smile', 'happy']);
});
test('getEmoticonSearchStr for an IEmoji without a shortcodes array returns shortcode + label', () => {
const emoji = {
shortcode: 'smile',
label: 'Smiling Face',
} as unknown as IEmoji;
assert.deepEqual(getEmoticonSearchStr(emoji), [':smile:', 'Smiling Face']);
});
+137
View File
@@ -0,0 +1,137 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { Room } from 'matrix-js-sdk';
import { getViaServers } from './via-servers';
type StubEvent =
| {
content: Record<string, unknown>;
sender?: string;
}
| undefined;
type RoomStub = {
members: string[];
create?: StubEvent;
power?: StubEvent;
};
const makeRoom = (opts: RoomStub): Room => {
const stateEvents: Record<string, StubEvent> = {};
if (opts.create) stateEvents['m.room.create'] = opts.create;
if (opts.power) stateEvents['m.room.power_levels'] = opts.power;
const mkEvent = (e: StubEvent) =>
e
? {
getContent: () => e.content,
getSender: () => e.sender,
}
: undefined;
return {
getMembers: () => opts.members.map((userId) => ({ userId })),
getLiveTimeline: () => ({
getState: () => ({
getStateEvents: (type: string) => mkEvent(stateEvents[type]),
}),
}),
} as unknown as Room;
};
test('with no power user it returns the top 3 most-populated servers, descending', () => {
const room = makeRoom({
// s3 x2, s1 x2, s2 x1, s4 x1 — order between equal counts follows insertion
members: ['@a:s1.com', '@b:s1.com', '@c:s2.com', '@d:s3.com', '@e:s3.com', '@f:s4.com'],
});
assert.deepEqual(getViaServers(room), ['s1.com', 's3.com', 's2.com']);
});
test('the highest-power user server is placed first, ahead of the populous servers', () => {
const room = makeRoom({
// s3 is most populous (3), but the power user is on s2
members: ['@a:s1.com', '@b:s1.com', '@c:s2.com', '@d:s3.com', '@e:s3.com', '@f:s3.com'],
power: { content: { users: { '@c:s2.com': 100 }, users_default: 0 } },
});
// power-user server first, then the top populated servers, s2 deduped
assert.deepEqual(getViaServers(room), ['s2.com', 's3.com', 's1.com']);
});
test('a power-user server that is also the most populous is deduped, result capped at 3', () => {
const room = makeRoom({
members: [
'@a:s1.com',
'@b:s1.com',
'@c:s1.com',
'@d:s2.com',
'@e:s3.com',
'@f:s4.com',
'@g:s5.com',
],
power: { content: { users: { '@a:s1.com': 100 }, users_default: 0 } },
});
// s1 (power + most pop) first, then next 2 of the top-3 populated list
assert.deepEqual(getViaServers(room), ['s1.com', 's2.com', 's3.com']);
});
test('picks the user with the strictly highest power level', () => {
const room = makeRoom({
members: ['@low:s1.com', '@high:s2.com', '@c:s3.com'],
power: {
content: {
users: { '@low:s1.com': 50, '@high:s2.com': 100 },
users_default: 0,
},
},
});
assert.equal(getViaServers(room)[0], 's2.com');
});
test('ignores users whose power is not above users_default', () => {
const room = makeRoom({
members: ['@a:s1.com', '@b:s2.com'],
// @a power equals users_default, so it is not treated as a power user
power: { content: { users: { '@a:s1.com': 50 }, users_default: 50 } },
});
// falls back to populated servers only
assert.deepEqual(getViaServers(room), ['s1.com', 's2.com']);
});
test('uses the create-event sender when the room version supports creators', () => {
const room = makeRoom({
members: ['@a:s1.com', '@b:s2.com', '@c:s2.com'],
create: { content: { room_version: '12' }, sender: '@a:s1.com' },
// power levels would point elsewhere, but creatorsSupported short-circuits
power: { content: { users: { '@c:s2.com': 100 }, users_default: 0 } },
});
assert.equal(getViaServers(room)[0], 's1.com');
});
test('falls back to power levels when the room version does not support creators', () => {
const room = makeRoom({
members: ['@a:s1.com', '@b:s2.com', '@c:s2.com'],
create: { content: { room_version: '11' }, sender: '@a:s1.com' },
power: { content: { users: { '@c:s2.com': 100 }, users_default: 0 } },
});
assert.equal(getViaServers(room)[0], 's2.com');
});
test('ignores members with unparseable user ids when counting populations', () => {
const room = makeRoom({
members: ['@a:s1.com', 'broken-id', '@b:s1.com', '@c:s2.com'],
});
assert.deepEqual(getViaServers(room), ['s1.com', 's2.com']);
});
test('returns an empty array for a room with no resolvable servers', () => {
const room = makeRoom({ members: ['broken-id', 'also-broken'] });
assert.deepEqual(getViaServers(room), []);
});
test('result never exceeds three servers', () => {
const room = makeRoom({
members: ['@a:s1.com', '@b:s2.com', '@c:s3.com', '@d:s4.com', '@e:s5.com'],
power: { content: { users: { '@a:s1.com': 100 }, users_default: 0 } },
});
assert.ok(getViaServers(room).length <= 3);
});