From ba89534501e02c5827666db5cc1e0ff2a87bd3bb Mon Sep 17 00:00:00 2001 From: "otoneko." Date: Sun, 23 Aug 2026 22:56:19 +0900 Subject: [PATCH] test: consolidate regex/patterns/findMatches tests into index.test.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The three test files had grown overlapping/redundant coverage: most of regex.test.ts's individual case list (single-pattern checks like "うおw") duplicated what the per-pattern loop in patterns.test.ts already verifies more precisely (tied to the pattern's own id). Consolidated into one test/index.test.ts: - findAll/contains: kept only the scenarios not covered elsewhere (basic smoke check, the (笑)-with-stem regression, multi-pattern findAll matching, and argument validation - the last of these wasn't tested anywhere before) - findMatches: unchanged, moved as-is - パターン別サンプル: the auto-generated per-pattern loop, now also asserting findMatches returns the same pattern id for each sample (so both the combined-regex path and the per-pattern-regex path are exercised) Net effect: 3 files / ~120 tests with real duplication -> 1 file / 140 tests with each one covering something the others don't. --- CONTRIBUTING.md | 2 +- test/findMatches.test.ts | 48 ----------------------- test/index.test.ts | 84 ++++++++++++++++++++++++++++++++++++++++ test/patterns.test.ts | 18 --------- test/regex.test.ts | 77 ------------------------------------ 5 files changed, 85 insertions(+), 144 deletions(-) delete mode 100644 test/findMatches.test.ts create mode 100644 test/index.test.ts delete mode 100644 test/patterns.test.ts delete mode 100644 test/regex.test.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d3d21ea..97f1d4d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ PRに適切なラベルを付与してください - 冷笑のパターンを追加する場合 - [./src/lib/patterns.ts](./src/lib/patterns.ts) の `patterns` 配列に `PatternDefinition` を1つ追加してください(`id` / `strict` / `source` / `samples`) - - `samples` に書いたサンプル文字列は自動でテスト化されます([test/patterns.test.ts](./test/patterns.test.ts))。個別にテストコードを書く必要はありません + - `samples` に書いたサンプル文字列は自動でテスト化されます([test/index.test.ts](./test/index.test.ts))。個別にテストコードを書く必要はありません - 可能であれば **ひらがな** , **カタカナ** , **半角カナ** , **小文字** も同様に含めてください - 追加するパターンが明らかに冷笑な場合は `strict: true` に、冷笑か怪しい場合や文脈によっては冷笑ではないパターンは `strict: false` (relaxedモードでのみ検知)にしてください diff --git a/test/findMatches.test.ts b/test/findMatches.test.ts deleted file mode 100644 index 6f4295e..0000000 --- a/test/findMatches.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { describe, test, expect } from "vitest"; -import { findMatches, patterns } from "../src/index"; - -describe("findMatches", () => { - test("マッチしたパターンのidと位置を返す", () => { - const result = findMatches("うおうおうおw、爆笑爆笑"); - expect(result).toEqual([ - { text: "うおw", index: 4, patternId: "stem-uo", strict: true }, - { - text: "爆笑爆笑", - index: 8, - patternId: "repeat-bakushou", - strict: true, - }, - ]); - }); - - test("見つからなければnullを返す", () => { - expect(findMatches("普通の文章です")).toBeNull(); - }); - - test("strictでは検知できずrelaxedでのみ検知するパターンも含まれる", () => { - const result = findMatches("いや💦", { relaxed: true }); - expect(result).not.toBeNull(); - expect(result?.some((m) => m.patternId === "emoji-sweat-drop")).toBe(true); - expect(result?.every((m) => m.strict === false)).toBe(true); - }); - - test("relaxed指定なしではrelaxed専用パターンは含まれない", () => { - expect(findMatches("いや💦")).toBeNull(); - }); - - test("不正な引数はTypeErrorを投げる", () => { - // @ts-expect-error 意図的に不正な型を渡す - expect(() => findMatches(123)).toThrow(TypeError); - }); -}); - -describe("patterns", () => { - test("全パターンのsamplesが自身のstrict/relaxed分類と整合している", () => { - for (const p of patterns) { - for (const sample of p.samples) { - const matches = findMatches(sample, { relaxed: true }) ?? []; - expect(matches.some((m) => m.patternId === p.id)).toBe(true); - } - } - }); -}); diff --git a/test/index.test.ts b/test/index.test.ts new file mode 100644 index 0000000..f798834 --- /dev/null +++ b/test/index.test.ts @@ -0,0 +1,84 @@ +import { describe, test, expect } from "vitest"; +import { findAll, contains, findMatches, patterns } from "../src/index"; + +describe("findAll / contains", () => { + test("基本的な検出", () => { + expect(contains("うおw")).toBe(true); + expect(contains("普通の文章です")).toBe(false); + }); + + test("relaxedモードでのみ検知するパターン", () => { + expect(contains("いや💦")).toBe(false); + expect(contains("いや💦", { relaxed: true })).toBe(true); + }); + + test("(笑)は語幹込みで一致する(旧実装では語幹が欠落するバグがあった)", () => { + expect(findAll("うお(笑)")).toEqual(["うお(笑)"]); + expect(findAll("どわー(笑)")).toEqual(["どわー(笑)"]); + }); + + test("複数マッチ", () => { + expect(findAll("うおうおうおw、爆笑爆笑")).toEqual(["うおw", "爆笑爆笑"]); + expect(findAll("うおうおうおw、爆笑爆笑", { relaxed: true })).toEqual([ + "うお", + "うお", + "うおw", + "爆笑爆笑", + ]); + }); + + test("不正な引数はTypeErrorを投げる", () => { + // @ts-expect-error 意図的に不正な型を渡す + expect(() => findAll(123)).toThrow(TypeError); + // @ts-expect-error 意図的に不正な型を渡す + expect(() => contains("text", "invalid")).toThrow(TypeError); + }); +}); + +describe("findMatches", () => { + test("マッチしたパターンのidと位置を返す", () => { + expect(findMatches("うおうおうおw、爆笑爆笑")).toEqual([ + { text: "うおw", index: 4, patternId: "stem-uo", strict: true }, + { + text: "爆笑爆笑", + index: 8, + patternId: "repeat-bakushou", + strict: true, + }, + ]); + }); + + test("見つからなければnullを返す", () => { + expect(findMatches("普通の文章です")).toBeNull(); + }); + + test("relaxed指定時のみrelaxed専用パターンが含まれる", () => { + expect(findMatches("いや💦")).toBeNull(); + const result = findMatches("いや💦", { relaxed: true }); + expect( + result?.every((m) => m.patternId === "emoji-sweat-drop" && !m.strict) + ).toBe(true); + }); +}); + +// patterns.ts に PatternDefinition を追加するだけで、そのsamplesが自動的に +// テスト対象になる(strict/relaxedの分類と、findAll側/findMatches側の両方の +// 検出経路を検証する)。 +describe("パターン別サンプル", () => { + for (const p of patterns) { + describe(`[${p.id}]`, () => { + for (const sample of p.samples) { + test(`strict -> ${sample}`, () => { + expect(contains(sample)).toBe(p.strict); + }); + test(`relaxed -> ${sample}`, () => { + expect(contains(sample, { relaxed: true })).toBe(true); + }); + test(`findMatchesにも同じidが含まれる -> ${sample}`, () => { + const matches = findMatches(sample, { relaxed: true }) ?? []; + expect(matches.some((m) => m.patternId === p.id)).toBe(true); + }); + } + }); + } +}); diff --git a/test/patterns.test.ts b/test/patterns.test.ts deleted file mode 100644 index 0798f31..0000000 --- a/test/patterns.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { describe, test, expect } from "vitest"; -import { contains } from "../src/index"; -import { patterns } from "../src/lib/patterns"; - -describe("パターン別サンプル", () => { - for (const p of patterns) { - describe(`[${p.id}]`, () => { - for (const sample of p.samples) { - test(`strict -> ${sample}`, () => { - expect(contains(sample)).toBe(p.strict); - }); - test(`relaxed -> ${sample}`, () => { - expect(contains(sample, { relaxed: true })).toBe(true); - }); - } - }); - } -}); diff --git a/test/regex.test.ts b/test/regex.test.ts deleted file mode 100644 index 729c282..0000000 --- a/test/regex.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { describe, test, expect } from "vitest"; -import { findAll, contains } from "../src/index"; - -type Case = { - // 例文 - content: string; - // 単一 or 複数 - expected: boolean | string[]; - // true: relaxedモード - relaxed?: boolean; -}; - -// テストケース -const cases: Case[] = [ - // 絵文字: Strict に含めるもの - { content: "これは😅です", expected: true }, - { content: "爆笑🤣爆笑", expected: true }, - { content: "本当‼️?", expected: true }, - // 絵文字: 💦 は Relaxed のみ - { content: "いや💦", expected: false }, - { content: "いや💦", expected: true, relaxed: true }, - // 単一 - { content: "爆笑爆笑", expected: true }, - { content: "うおw", expected: true }, - { content: "うおw", expected: true }, - { content: "うおwwwww", expected: true }, - { content: "うおwwwwww", expected: true }, - { content: "うお笑", expected: true }, - { content: "うお笑笑笑笑笑笑笑笑", expected: true }, - { content: "うお(笑)", expected: true }, - { content: "うお(笑)", expected: true }, - { content: "うお爆笑", expected: true }, - { content: "うお爆笑爆笑", expected: true }, - { content: "どわw", expected: true }, - { content: "どわw", expected: true }, - { content: "どわwwww", expected: true }, - { content: "どわwwwww", expected: true }, - { content: "どわーw", expected: true }, - { content: "どわーwwwww", expected: true }, - { content: "どわーw", expected: true }, - { content: "どわーwwwwww", expected: true }, - { content: "どわー笑", expected: true }, - { content: "どわー笑笑笑笑笑笑", expected: true }, - { content: "どわー(笑)", expected: true }, - { content: "どわー(笑)", expected: true }, - { content: "どわー爆笑", expected: true }, - { content: "どわー爆笑爆笑", expected: true }, - { content: "お、おうw", expected: true }, - { content: "きちーw", expected: true }, - { content: "うお(笑)", expected: ["うお(笑)"] }, - { content: "どわー(笑)", expected: ["どわー(笑)"] }, - // 複数マッチ - { content: "うおうおうおw、爆笑爆笑", expected: ["うおw", "爆笑爆笑"] }, - { - content: "うおうおうおw、爆笑爆笑", - expected: ["うお", "うお", "うおw", "爆笑爆笑"], - relaxed: true, - }, -]; - -describe("冷笑検出", () => { - cases.forEach((c, i) => { - const isRelaxed = !!c.relaxed; - if (typeof c.expected === "boolean") { - test(`case ${i} [${isRelaxed ? "relaxed" : "strict"}] contains -> ${c.content}`, () => { - expect(contains(c.content, { relaxed: isRelaxed })).toBe( - c.expected as boolean - ); - }); - } else { - test(`case ${i} [${isRelaxed ? "relaxed" : "strict"}] findAll -> ${c.content}`, () => { - const res = findAll(c.content, { relaxed: isRelaxed }) || []; - expect(res).toEqual(c.expected as string[]); - }); - } - }); -});