61 lines
2.4 KiB
TypeScript
61 lines
2.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import { findTagMatches } from "@/utils/tag-grammar";
|
||
|
||
const values = (source: string) => findTagMatches(source).map((match) => match.value);
|
||
|
||
describe("tag scanner", () => {
|
||
it.each([
|
||
["#tag", ["tag"]],
|
||
["hello#tag", ["tag"]],
|
||
["#标签", ["标签"]],
|
||
["#2026", ["2026"]],
|
||
["#C++", ["C++"]],
|
||
["#R&D", ["R&D"]],
|
||
["#-foo #foo- #--- #&&", ["-foo", "foo-", "---", "&&"]],
|
||
["#work/notes", ["work/notes"]],
|
||
["#book/", ["book"]],
|
||
["#/book", []],
|
||
["#book//fiction", ["book"]],
|
||
["#book/fiction/", ["book/fiction"]],
|
||
["#l·l #foo‿bar", ["l·l", "foo‿bar"]],
|
||
["#tag's #сім'я #O'Brien #O’Brien #OʼBrien", ["tag's", "сім'я", "O'Brien", "O’Brien", "OʼBrien"]],
|
||
["#café's", ["café's"]],
|
||
["'#tag' #users' #'missing #rock''roll", ["tag", "users", "rock"]],
|
||
["#O‘Brien #foo-'bar #foo'1️⃣ #A'B", ["O", "foo-", "foo", "A"]],
|
||
["#foo,bar #price€ #€budget #v²", ["foo", "price", "v"]],
|
||
["#first#second", ["first", "second"]],
|
||
["##tag", ["tag"]],
|
||
["#tag ﹟tag", []],
|
||
])("scans %s", (source, expected) => {
|
||
expect(values(source)).toEqual(expected);
|
||
});
|
||
|
||
it("emits ignored source code points only in the source span", () => {
|
||
const source = "#AB #foo #AB #foo #A️B #́foo #café #foo/́bar";
|
||
expect(values(source)).toEqual(["AB", "foo", "AB", "foo", "AB", "foo", "café", "foo/bar"]);
|
||
expect(findTagMatches("#AB")[0]).toMatchObject({ from: 0, to: 4, source: "AB", value: "AB" });
|
||
expect(values("# #́")).toEqual([]);
|
||
});
|
||
|
||
it("matches fully-qualified emoji atomically and excludes components", () => {
|
||
expect(values("#*️⃣ #‼️ #♥ #♥️ #🏻")).toEqual(["*️⃣", "‼️", "♥️"]);
|
||
expect(values("#️⃣ ##️⃣ #first#️⃣")).toEqual(["#️⃣", "first#️⃣"]);
|
||
});
|
||
|
||
it("uses the checked-in Unicode 17 and Emoji 17 repertoire", () => {
|
||
expect(values("# #")).toEqual(["", ""]);
|
||
});
|
||
|
||
it("has no tag-specific length limit", () => {
|
||
const tag = "a".repeat(101);
|
||
expect(values(`#${tag}`)).toEqual([tag]);
|
||
});
|
||
|
||
it("does not match an emoji across the requested source limit", () => {
|
||
expect(findTagMatches("#😀", 0, 2)).toEqual([]);
|
||
});
|
||
|
||
it("does not join an apostrophe across the requested source limit", () => {
|
||
expect(findTagMatches("#O'B", 0, 3).map((match) => match.value)).toEqual(["O"]);
|
||
});
|
||
});
|