Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
5c3fd4cc4d
commit
943763c774
@ -55,6 +55,15 @@ describe("текст события", () => {
|
|||||||
expect(eventText(ev({ kind: "comment", field: "comment", from: null, to: null }))).toBe("комментарий: —"); // M-6
|
expect(eventText(ev({ kind: "comment", field: "comment", from: null, to: null }))).toBe("комментарий: —"); // M-6
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("комментарий в ленте — без вики-разметки (С6)", () => {
|
||||||
|
expect(eventText(ev({ kind: "comment", field: "comment", from: null, to: "h3. Итог\n*Готово*, см. [протокол|https://x.ru/p] и [~ivanov]" })))
|
||||||
|
.toBe("комментарий: Итог Готово, см. протокол и @ivanov");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("пустой комментарий — по-прежнему «—»", () => {
|
||||||
|
expect(eventText(ev({ kind: "comment", field: "comment", from: null, to: " " }))).toBe("комментарий: —");
|
||||||
|
});
|
||||||
|
|
||||||
it("прочие поля — подпись из словаря или имя поля", () => {
|
it("прочие поля — подпись из словаря или имя поля", () => {
|
||||||
expect(eventText(ev({ kind: "other", field: "assignee", from: null, to: "ivanov" }))).toBe("исполнитель: «—» → «ivanov»");
|
expect(eventText(ev({ kind: "other", field: "assignee", from: null, to: "ivanov" }))).toBe("исполнитель: «—» → «ivanov»");
|
||||||
expect(eventText(ev({ kind: "other", field: "Компания", from: "А", to: "Б" }))).toBe("Компания: «А» → «Б»");
|
expect(eventText(ev({ kind: "other", field: "Компания", from: "А", to: "Б" }))).toBe("Компания: «А» → «Б»");
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { ChangeEvent, ChangeKind } from "../api/types.ts";
|
import type { ChangeEvent, ChangeKind } from "../api/types.ts";
|
||||||
|
import { wikiPlain } from "../issues/wiki/plain.ts";
|
||||||
|
|
||||||
// период ленты: границы по местному времени браузера; «Сегодня» — по умолчанию и в адрес не пишется (spec web-app «Лента изменений»)
|
// период ленты: границы по местному времени браузера; «Сегодня» — по умолчанию и в адрес не пишется (spec web-app «Лента изменений»)
|
||||||
export type Period = "2h" | "today" | "yesterday" | "7d";
|
export type Period = "2h" | "today" | "yesterday" | "7d";
|
||||||
@ -70,7 +71,7 @@ export function eventText(e: ChangeEvent): string {
|
|||||||
case "description": return "описание изменено";
|
case "description": return "описание изменено";
|
||||||
case "dates": return `${DATE_LABELS[e.field] ?? e.field} ${day(e.from_value, e.from)} → ${day(e.to_value, e.to)}`;
|
case "dates": return `${DATE_LABELS[e.field] ?? e.field} ${day(e.from_value, e.from)} → ${day(e.to_value, e.to)}`;
|
||||||
case "comment": { // первые 300 символов текста и «…» (M-4); без текста — «—» (M-6)
|
case "comment": { // первые 300 символов текста и «…» (M-4); без текста — «—» (M-6)
|
||||||
const s = oneLine(e.to);
|
const s = oneLine(wikiPlain(e.to ?? ""));
|
||||||
return "комментарий: " + (!s ? "—" : s.length <= 300 ? s : s.slice(0, 300) + "…");
|
return "комментарий: " + (!s ? "—" : s.length <= 300 ? s : s.slice(0, 300) + "…");
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
|||||||
24
web/src/modules/jira/issues/wiki/plain.test.ts
Normal file
24
web/src/modules/jira/issues/wiki/plain.test.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { wikiPlain } from "./plain.ts";
|
||||||
|
|
||||||
|
const norm = (s: string) => s.replace(/\s+/g, " ").trim();
|
||||||
|
|
||||||
|
describe("wikiPlain", () => {
|
||||||
|
it("выделение, ссылки, упоминание, заголовок", () => {
|
||||||
|
expect(norm(wikiPlain("h3. Итог\n*Готово*, см. [протокол|https://x.ru/p] и [~ivanov]"))).toBe("Итог Готово, см. протокол и @ivanov");
|
||||||
|
});
|
||||||
|
it("[адрес] — сам адрес, {{моно}} и {code} — содержимое, ---- — пусто", () => {
|
||||||
|
expect(norm(wikiPlain("[https://x.ru] {{a_b}}\n----\n{code}\nx = 1\n{code}"))).toBe("https://x.ru a_b x = 1");
|
||||||
|
});
|
||||||
|
it("ключ задачи и вложение — текстом", () => {
|
||||||
|
const opts = { projectKey: "AURORA", attachments: [{ id: "1", filename: "скрин.png", created: null }] };
|
||||||
|
expect(wikiPlain("см. AURORA-1 и !скрин.png!", opts).trim()).toBe("см. AURORA-1 и скрин.png");
|
||||||
|
});
|
||||||
|
it("незакрытая и неизвестная разметка — как есть, пустой текст — пусто (Review Focus 3)", () => {
|
||||||
|
expect(wikiPlain("{color:red}важно *без конца").trim()).toBe("{color:red}важно *без конца");
|
||||||
|
expect(wikiPlain("")).toBe("");
|
||||||
|
});
|
||||||
|
it("списки, таблицы, цитаты — содержимое через пробел", () => {
|
||||||
|
expect(norm(wikiPlain("* a\n** b\n# c\n||h1||h2||\n|x|y|\nbq. цит"))).toBe("a b c h1 h2 x y цит");
|
||||||
|
});
|
||||||
|
});
|
||||||
35
web/src/modules/jira/issues/wiki/plain.ts
Normal file
35
web/src/modules/jira/issues/wiki/plain.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Текст вики-разметки Jira без разметки — как читается отрендеренным (лента изменений, С6).
|
||||||
|
// Блоки соединены пробелом; «одну строку» и обрезку делает вызывающий.
|
||||||
|
import type { Inline, WikiOptions } from "./inline.ts";
|
||||||
|
import { type Block, type ListBlock, parseWiki } from "./parse.ts";
|
||||||
|
|
||||||
|
function inline(nodes: Inline[]): string {
|
||||||
|
return nodes.map((n): string => {
|
||||||
|
switch (n.t) {
|
||||||
|
case "text": case "code": return n.text;
|
||||||
|
case "mention": return "@" + n.login;
|
||||||
|
case "br": return " ";
|
||||||
|
case "issue": return n.children.length ? inline(n.children) : n.key;
|
||||||
|
default: return inline(n.children);
|
||||||
|
}
|
||||||
|
}).join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function list(l: ListBlock): string[] {
|
||||||
|
return l.items.flatMap((it) => [inline(it.children), ...it.lists.flatMap(list)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function block(b: Block): string[] {
|
||||||
|
switch (b.t) {
|
||||||
|
case "heading": case "p": return [inline(b.children)];
|
||||||
|
case "list": return list(b);
|
||||||
|
case "table": return b.rows.flatMap((r) => r.map((c) => inline(c.children)));
|
||||||
|
case "pre": return [b.text];
|
||||||
|
case "quote": return b.children.flatMap(block);
|
||||||
|
case "hr": return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wikiPlain(text: string, opts: WikiOptions = {}): string {
|
||||||
|
return parseWiki(text, opts).flatMap(block).join(" ");
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user