diff --git a/packages/frontend/src/scripts/check-word-mute.ts b/packages/frontend/src/scripts/check-word-mute.ts index 94916aacda..9ddbc5362e 100644 --- a/packages/frontend/src/scripts/check-word-mute.ts +++ b/packages/frontend/src/scripts/check-word-mute.ts @@ -18,7 +18,7 @@ type GlobalMisskeyWordMute = { hard: WordMuteInfo; }; -function createWordMuteInfo(mutedWords: Array) : WordMuteInfo { +export function createWordMuteInfo(mutedWords: Array) : WordMuteInfo { if (mutedWords.length <= 0) return false; const normalTexts: string[] = []; const andTexts: string[][] = []; diff --git a/packages/frontend/test/mocks.ts b/packages/frontend/test/mocks.ts new file mode 100644 index 0000000000..fca3080880 --- /dev/null +++ b/packages/frontend/test/mocks.ts @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import type * as Misskey from 'misskey-js'; +import { vi } from 'vitest'; + +export const UserLiteMock = vi.fn(() => { + return { + id: 'xxxxxxxx', + username: 'ai', + host: null, + name: '藍', + avatarUrl: null, + avatarBlurhash: null, + avatarDecorations: [], + emojis: {}, + onlineStatus: 'online', + } as Misskey.entities.UserLite; +}); + +export const NoteMock = vi.fn((options?: { + text?: string, + cw?: string, +}) => { + const user = new UserLiteMock(); + return { + id: 'xxxxxxxx', + // 2025/01/01 00:00:00 UTC on Unix time + createdAt: '1767225600000', + text: options?.text ?? 'Hello, Misskey!', + cw: options?.cw, + userId: user.id, + user: user, + visibility: 'public', + reactionAcceptance: null, + reactionEmojis: {}, + reactions: {}, + reactionCount: 0, + renoteCount: 0, + repliesCount: 0, + } as Misskey.entities.Note; +}); diff --git a/packages/frontend/test/word-mute.test.ts b/packages/frontend/test/word-mute.test.ts new file mode 100644 index 0000000000..f0c65bb1a4 --- /dev/null +++ b/packages/frontend/test/word-mute.test.ts @@ -0,0 +1,76 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { describe, test, assert } from 'vitest'; +import { createWordMuteInfo, checkWordMute } from '@/scripts/check-word-mute.js'; +import { NoteMock } from './mocks.js'; + +type TestCases = { + text: string, + cw?: string, + mutedWords: Array, + result: false | Array, +} + +describe('check-word-mute', () => { + const cases:Array = [ + { + text: 'Hello, Misskey!', + mutedWords: ['Misskey'], + result: [['Misskey']], + }, + // cw + { + text: 'Hello, Misskey!', + cw: 'ai', + mutedWords: ['ai'], + result: [['ai']], + }, + // nothing + { + text: 'Hello, Misskey!', + mutedWords: [], + result: false, + }, + // surrogate pair + { + text: '𠮟る', + mutedWords: ['𠮟'], + result: [['𠮟']], + }, + // grapheme cluster + { + text: '👩‍❤️‍👨', + mutedWords: ['👩'], + result: false, + }, + // regex + { + text: 'Hello, Misskey!', + mutedWords: ['/M[isk]*ey/'], + result: ['/M[isk]*ey/'], + }, + // multi wordas + { + text: 'Hello, Misskey!', + mutedWords: [['Hello', 'Misskey'], ['Mi']], + result: [['Mi'],['Hello', 'Misskey']], + }, + ] + + cases.forEach((c) => { + test(`text: ${c.text}, cw: ${c.cw}, mutedWords: ${c.mutedWords}` , async () => { + // initWordMuteInfoが実行されないので代わりにここで初期化 + (globalThis as any)._misskeyWordMute = { + soft: createWordMuteInfo(c.mutedWords), + hard: createWordMuteInfo([]), + } + + const note = NoteMock({ text: c.text, cw: c.cw }); + const result = checkWordMute(note, null, 'soft'); + assert.deepEqual(result, c.result); + }); + }); +});