test(web): golden-эталоны доски из links.html@3931a88 на синтетических фикстурах (#21)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-14 21:33:12 +04:00
parent 7c161a675e
commit 7eac72e86f
19 changed files with 8857 additions and 0 deletions

View File

@ -0,0 +1,195 @@
// разовый генератор golden-эталонов доски: исполняет <script> страницы links.html ревизии 3931a88 в node:vm с заглушкой
// DOM и записывает результаты её функций на фикстурах в src/board/__fixtures__/*.expected.json.
// Запуск: cd web && npm run golden (Node 24 исполняет .ts без сборки). Эталоны коммитятся; скрипт перезапускать только
// при смене фикстур.
import { execFileSync } from "node:child_process";
import { readFileSync, writeFileSync } from "node:fs";
import vm from "node:vm";
import { canon } from "../src/test/canon.ts";
const REV = "3931a88";
const NOW = "2026-10-14T12:00:00.000Z";
const FIX = new URL("../src/board/__fixtures__/", import.meta.url);
const read = (name: string) => readFileSync(new URL(name, FIX), "utf8");
const write = (name: string, v: unknown) => writeFileSync(new URL(name, FIX), JSON.stringify(canon(v), null, 1) + "\n");
const html = execFileSync("git", ["show", `${REV}:src/jira_hub/web/links.html`], { encoding: "utf8" });
const page = html.slice(html.indexOf("<script>") + "<script>".length, html.lastIndexOf("</script>"));
// элемент-заглушка: хранит стили, атрибуты и детей; querySelector возвращает один и тот же элемент на селектор;
// высота карточки 117 px, её заголовка (.title) 38 px, остальных элементов 0
class El {
className = ""; hidden = false; disabled = false; title = ""; value = ""; textContent = ""; open = false; scrollLeft = 0;
style: Record<string, unknown> = {
setProperty(k: string, v: string) { this[k] = v; },
removeProperty(k: string) { delete this[k]; },
};
dataset: Record<string, string> = {};
attrs: Record<string, unknown> = {};
children: El[] = [];
sel = "";
private html = "";
private q = new Map<string, El>();
classList = { add() {}, remove() {}, toggle() {}, contains: () => false };
get innerHTML() { return this.html; }
set innerHTML(v: string) { this.html = v; this.children = []; }
get offsetHeight() { return this.sel === ".title" ? 38 : this.className.startsWith("card") ? 117 : 0; }
get offsetWidth() { return 0; }
get scrollWidth() { return 0; }
get clientWidth() { return 0; }
appendChild(c: El) { this.children.push(c); return c; }
querySelector(s: string) { let e = this.q.get(s); if (!e) { e = new El(); e.sel = s; this.q.set(s, e); } return e; }
// две заглушки: страница деструктурирует кнопки «Выше»/«Ниже» у дорожки
querySelectorAll() { return [new El(), new El()]; }
setAttribute(k: string, v: unknown) { this.attrs[k] = v; }
getAttribute(k: string) { return this.attrs[k] ?? null; }
removeAttribute(k: string) { delete this.attrs[k]; }
addEventListener() {} removeEventListener() {} setPointerCapture() {} focus() {} select() {} click() {} remove() {}
contains() { return false; }
closest() { return null; }
getBoundingClientRect() { return { top: 0, left: 0, width: 0, height: 0 }; }
}
const byId = new Map<string, El>(), bySel = new Map<string, El>();
const document = {
getElementById: (id: string) => { let e = byId.get(id); if (!e) { e = new El(); byId.set(id, e); } return e; },
querySelector: (s: string) => { let e = bySel.get(s); if (!e) { e = new El(); bySel.set(s, e); } return e; },
querySelectorAll: () => [],
createElement: () => new El(),
createElementNS: () => new El(),
addEventListener() {},
documentElement: new El(),
body: new El(),
};
const data = new Map<string, string>();
const localStorage = {
getItem: (k: string) => (data.has(k) ? data.get(k)! : null),
setItem: (k: string, v: unknown) => { data.set(k, String(v)); },
removeItem: (k: string) => { data.delete(k); },
clear: () => data.clear(),
key: (i: number) => [...data.keys()][i] ?? null,
get length() { return data.size; },
};
const sandbox: Record<string, unknown> = {
document, localStorage, console, setTimeout, clearTimeout, URL, Blob, navigator: {},
requestAnimationFrame: (f: () => void) => setTimeout(f, 0),
getComputedStyle: () => ({ position: "static" }),
fetch: () => Promise.reject(new Error("нет сети")),
addEventListener() {}, removeEventListener() {},
};
const ctx = vm.createContext(sandbox);
sandbox.window = vm.runInContext("globalThis", ctx);
// фиксированное «сегодня»: findProblems, колонки дат и exportedAt зависят от new Date()
vm.runInContext(`{ const R = Date; Date = class extends R { constructor(...a) { super(...(a.length ? a : ["${NOW}"])); }
static now() { return R.parse("${NOW}"); } }; }`, ctx);
vm.runInContext(page, ctx, { filename: "links.html@" + REV });
// доступ к функциям и состоянию страницы (let/const скрипта видны следующим скриптам того же контекста)
vm.runInContext(String.raw`
function dump() { const o = {}; for (let i = 0; i < localStorage.length; i++) { const k = localStorage.key(i); o[k] = localStorage.getItem(k); } return o; }
function reset() {
localStorage.clear();
Object.assign(S, { neighbors: false, onlyLinked: false, onlyHand: false, crit: false, byDate: false, snap: true,
sprints: new Set(), who: new Set(), hideKind: new Set(), hideEdge: new Set() });
}
globalThis.__g = {
dicts: () => ({ kindLabels: KIND_LABELS, linkTypes: LINK_TYPES }),
fromHub: (s) => fromHub(s),
fromCsv: (t, n) => fromJiraCsv(t, n),
parseCsv: (t) => parseCsv(t),
fromJson: (t, n) => fromJson(t, n),
facets(model, remembered) {
reset(); MODEL = model;
if (remembered !== undefined) localStorage.setItem(fileKey("sprint"), JSON.stringify(remembered));
fillSprints(); fillWho();
return { sprintItems: sprintItems().items, whoItems: whoItems().items, sprints: S.sprints, who: S.who };
},
scenario(model, sc) {
reset(); MODEL = model;
const f = sc.filters || {};
for (const k of ["neighbors", "onlyLinked", "onlyHand", "crit", "byDate"]) S[k] = !!f[k];
for (const k of ["sprints", "who", "hideKind", "hideEdge"]) S[k] = new Set(f[k] || []);
if (sc.laneOrder) localStorage.setItem(fileKey("lanes"), JSON.stringify(sc.laneOrder));
if (sc.folded) localStorage.setItem(fileKey("fold"), JSON.stringify(sc.folded));
if (sc.saved) localStorage.setItem(fileKey("pos"), JSON.stringify(sc.saved));
if (sc.savedDate) localStorage.setItem(fileKey("posdate"), JSON.stringify(sc.savedDate));
if (sc.colWidths) localStorage.setItem(fileKey("colw"), JSON.stringify(sc.colWidths));
render();
const clamp = {};
Object.keys(cardEls).forEach((k) => { clamp[k] = cardEls[k].querySelector(".title").style.webkitLineClamp; });
return {
visible: VIS,
emptyReason: VIS.nodes.length ? null : emptyReason(),
layout: {
rel, laneOf, colBounds, baseW, pos, clamp,
lanes: lanesGeom.map((g) => ({ name: g.name, y: g.y, h: g.h, minH: g.minH, folded: g.folded })),
board: { width: board.style.width, height: board.style.height },
dateCols: dateEls.map((e) => ({ className: e.className, left: e.style.left, width: e.style.width })),
dateLabels: $("datesTrack").children.filter((e) => e.className.startsWith("dlabel"))
.map((e) => ({ className: e.className, left: e.style.left, title: e.title, innerHTML: e.innerHTML })),
edges: VIS.links.filter((l) => pos[l.src] && pos[l.dst])
.map((l) => ({ src: l.src, dst: l.dst, type: l.type, hand: l.hand, crit: l.crit, d: edgePath(pos[l.src], pos[l.dst]) })),
},
};
},
exportAfter(model, sc) { this.scenario(model, sc); return exportJson(); },
importJson(text, name) {
reset();
const r = fromJson(text, name);
applyJsonSettings(r);
return { import: r, storage: dump(), S: { neighbors: S.neighbors, onlyLinked: S.onlyLinked, onlyHand: S.onlyHand,
crit: S.crit, byDate: S.byDate, hideKind: S.hideKind, hideEdge: S.hideEdge } };
},
storage(before, op) {
localStorage.clear();
Object.entries(before).forEach(([k, v]) => localStorage.setItem(k, v));
if (op === "startup") {
const old = store.get(pref("mode"), null);
if (old !== null) { if (old === "linked") store.set(pref("neighbors"), "1"); if (old === "all") store.set(fileKey("sprint"), "[]"); store.del(pref("mode")); }
}
migrateStore();
return dump();
},
edgePath: (a, b) => edgePath(a, b),
orthoPath: (pts) => orthoPath(pts),
align(posIn, key, r, resizing) {
pos = posIn; guidesSvg.innerHTML = "";
const out = alignEdges(key, r, resizing);
return { out, guides: guidesSvg.children.map((l) => l.attrs) };
},
};`, ctx);
type G = Record<string, (...a: unknown[]) => unknown>;
const g = vm.runInContext("__g", ctx) as G;
const scenarios = JSON.parse(read("scenarios.json")) as { name: string }[];
const basic = g.fromHub(JSON.parse(read("hub-basic.board.json")));
write("hub-basic.expected.json", {
dicts: g.dicts(),
model: basic,
facets: { default: g.facets(basic), rememberedAll: g.facets(basic, []), rememberedSome: g.facets(basic, ["AWPCA_Спринт 3", "нет такого"]) },
scenarios: Object.fromEntries(scenarios.map((sc) => [sc.name, g.scenario(basic, sc)])),
export: g.exportAfter(basic, scenarios[0]),
});
const empty = g.fromHub(JSON.parse(read("hub-empty.board.json")));
write("hub-empty.expected.json", { model: empty, scenario: g.scenario(empty, scenarios[0]) });
const csv = read("jira-export.csv");
write("jira-export.expected.json", { rows: g.parseCsv(csv), model: g.fromCsv(csv, "jira-export.csv") });
write("jira-no-links.expected.json", { model: g.fromCsv(read("jira-no-links.csv"), "jira-no-links.csv") });
let bad: string | null = null;
try { g.fromCsv(read("jira-bad.csv"), "jira-bad.csv"); } catch (e) { bad = (e as Error).message; }
write("jira-bad.expected.json", { error: bad });
write("old-board.expected.json", g.importJson(read("old-board.json"), "old-board.json"));
const geo = JSON.parse(read("geometry.cases.json")) as {
edge: { a: unknown; b: unknown }[]; ortho: unknown[]; align: { pos: unknown; key: string; r: unknown; resizing: boolean }[];
};
write("geometry.expected.json", {
edge: geo.edge.map((c) => g.edgePath(c.a, c.b)),
ortho: geo.ortho.map((p) => g.orthoPath(p)),
align: geo.align.map((c) => g.align(c.pos, c.key, c.r, c.resizing)),
});
const cases = JSON.parse(read("storage.cases.json")) as { name: string; op: string; before: unknown }[];
write("storage.expected.json", Object.fromEntries(cases.map((c) => [c.name, g.storage(c.before, c.op)])));
console.log("эталоны записаны в", FIX.pathname);

View File

@ -0,0 +1,12 @@
{"edge": [
{"a": {"x": 24, "y": 40, "w": 240, "h": 120}, "b": {"x": 320, "y": 40, "w": 240, "h": 120}},
{"a": {"x": 24, "y": 40, "w": 240, "h": 120}, "b": {"x": 320, "y": 200, "w": 240, "h": 96}},
{"a": {"x": 620, "y": 40, "w": 240, "h": 120}, "b": {"x": 24, "y": 300, "w": 240, "h": 120}},
{"a": {"x": 24, "y": 40, "w": 240, "h": 120}, "b": {"x": 100, "y": 100, "w": 240, "h": 120}},
{"a": {"x": 24, "y": 40, "w": 240, "h": 120}, "b": {"x": 30, "y": 400, "w": 240, "h": 120}},
{"a": {"x": 24, "y": 400, "w": 240, "h": 120}, "b": {"x": 24, "y": 40, "w": 240, "h": 120}}],
"ortho": [[{"x": 0, "y": 0}, {"x": 10, "y": 0}], [{"x": 0, "y": 0}, {"x": 1, "y": 0}, {"x": 1, "y": 30}, {"x": 40, "y": 30}]],
"align": [
{"pos": {"A": {"x": 24, "y": 40, "w": 240, "h": 120}, "B": {"x": 320, "y": 200, "w": 240, "h": 96}}, "key": "B", "r": {"x": 28, "y": 203, "w": 240, "h": 96}, "resizing": false},
{"pos": {"A": {"x": 24, "y": 40, "w": 240, "h": 120}, "B": {"x": 320, "y": 200, "w": 240, "h": 96}}, "key": "B", "r": {"x": 320, "y": 200, "w": 262, "h": 98}, "resizing": true},
{"pos": {"A": {"x": 24, "y": 40, "w": 240, "h": 120}}, "key": "B", "r": {"x": 500, "y": 500, "w": 240, "h": 96}, "resizing": false}]}

View File

@ -0,0 +1,56 @@
{
"align": [
{
"guides": [
{
"x1": 24,
"x2": 24,
"y1": 0,
"y2": 0
}
],
"out": {
"h": 96,
"snappedX": true,
"snappedY": false,
"w": 240,
"x": 24,
"y": 203
}
},
{
"guides": [],
"out": {
"h": 98,
"snappedX": false,
"snappedY": false,
"w": 262,
"x": 320,
"y": 200
}
},
{
"guides": [],
"out": {
"h": 96,
"snappedX": false,
"snappedY": false,
"w": 240,
"x": 500,
"y": 500
}
}
],
"edge": [
"M264 100 L320 100",
"M264 100 L282 100 Q292 100 292 110 L292 238 Q292 248 302 248 L320 248",
"M620 100 L452 100 Q442 100 442 110 L442 350 Q442 360 432 360 L264 360",
"M264 100 L358 100 Q368 100 368 110 L368 150 Q368 160 358 160 L340 160",
"M144 160 L144 277 Q144 280 147 280 L147 280 Q150 280 150 283 L150 400",
"M144 400 L144 160"
],
"ortho": [
"M0 0 L10 0",
"M0 0 L1 0 L1 20 Q1 30 11 30 L40 30"
]
}

View File

@ -0,0 +1,29 @@
{
"generated_at": "2026-10-14T10:00:00+00:00",
"last_sync": {"started_at": "2026-10-14T09:00:00+00:00", "finished_at": "2026-10-14T09:01:00+00:00", "issues_count": 10},
"issues": [
{"key": "AURORA-9100", "summary": "Тестовый эпик", "issue_type": "Фича", "status": "В работе", "priority": "Средний", "assignee": null, "sprint": null, "duedate": null, "planned_start": null, "planned_end": null, "epic_key": null, "labels": ["AWPCA"], "neighbor": false, "fact_status": null, "work_kind": null},
{"key": "AURORA-9001", "summary": "Схема свода", "issue_type": "Задача", "status": "В работе", "priority": "Высокий", "assignee": "Иванов Иван Иванович", "sprint": "AWPCA_Спринт 2", "duedate": "2026-10-30", "planned_start": null, "planned_end": "2026-10-20", "epic_key": "AURORA-9100", "labels": ["AWPCA", "kind-dev"], "neighbor": false, "fact_status": "В работе", "work_kind": "разработка"},
{"key": "AURORA-9002", "summary": "API свода", "issue_type": "Задача", "status": "Ожидание разработки", "priority": "Средний", "assignee": "Петров Пётр Петрович", "sprint": "AWPCA_Спринт 2", "duedate": "2026-10-20", "planned_start": null, "planned_end": null, "epic_key": "AURORA-9100", "labels": ["kind-dev"], "neighbor": false, "fact_status": null, "work_kind": null},
{"key": "AURORA-9003", "summary": "Макет экрана", "issue_type": "Задача", "status": "Backlog", "priority": "Критичный", "assignee": null, "sprint": "AWPCA_Спринт 2", "duedate": null, "planned_start": null, "planned_end": "2026-10-16", "epic_key": "AURORA-9100", "labels": ["kind-ui"], "neighbor": false, "fact_status": null, "work_kind": null},
{"key": "AURORA-9004", "summary": "Решение по порогам", "issue_type": "Задача", "status": "К выполнению", "priority": "Наивысший", "assignee": "Сидорова Анна", "sprint": "AWPCA_Спринт 3", "duedate": null, "planned_start": null, "planned_end": "2026-10-25", "epic_key": "AURORA-1359", "labels": ["kind-decision"], "neighbor": false, "fact_status": null, "work_kind": null},
{"key": "AURORA-9005", "summary": "Тест-кейсы", "issue_type": "Задача", "status": "Ожидание", "priority": "Низкий", "assignee": "Петров Пётр Петрович", "sprint": "AWPCA_Спринт 3", "duedate": null, "planned_start": null, "planned_end": null, "epic_key": "AURORA-1359", "labels": ["kind-test"], "neighbor": false, "fact_status": null, "work_kind": null},
{"key": "AURORA-9006", "summary": "Задача без эпика", "issue_type": "Задача", "status": "Готово", "priority": "Средний", "assignee": "Иванов Иван Иванович", "sprint": null, "duedate": "2026-10-10", "planned_start": null, "planned_end": null, "epic_key": null, "labels": [], "neighbor": false, "fact_status": null, "work_kind": null},
{"key": "AURORA-9007", "summary": "Сосед по связям", "issue_type": "Задача", "status": "В работе", "priority": "Средний", "assignee": "Kozlov-AA", "sprint": "AWPCA_Спринт 1", "duedate": null, "planned_start": null, "planned_end": "2026-10-12", "epic_key": "AURORA-9100", "labels": ["kind-analytics"], "neighbor": true, "fact_status": null, "work_kind": null},
{"key": "AURORA-9008", "summary": "Актуализация ТЗ", "issue_type": "Задача", "status": "Ожидание разработки", "priority": "Самый низкий", "assignee": "Иванов Иван Иванович", "sprint": "AWPCA_Спринт 2", "duedate": null, "planned_start": null, "planned_end": "2026-10-18", "epic_key": "AURORA-9999", "labels": ["kind-docs"], "neighbor": false, "fact_status": null, "work_kind": null},
{"key": "AURORA-9009", "summary": "Созвон с заказчиком", "issue_type": "Задача", "status": "", "priority": "Средний", "assignee": "Иванов Иван Иванович", "sprint": "AWPCA_Спринт 2", "duedate": null, "planned_start": null, "planned_end": "2026-10-20", "epic_key": "AURORA-9100", "labels": ["kind-org"], "neighbor": false, "fact_status": null, "work_kind": null}
],
"links": [
{"source": "AURORA-9001", "target": "AURORA-9002", "link_type": "ASUPR_Блокер_Начала"},
{"source": "AURORA-9003", "target": "AURORA-9001", "link_type": "ASUPR_Блокер_Начала"},
{"source": "AURORA-9002", "target": "AURORA-9005", "link_type": "ASUPR_Блокер_Завершения"},
{"source": "AURORA-9004", "target": "AURORA-9002", "link_type": "ASUPR_Блокер_Начала"},
{"source": "AURORA-9001", "target": "AURORA-9008", "link_type": "ASUPR_Связанные"},
{"source": "AURORA-9008", "target": "AURORA-9001", "link_type": "ASUPR_Связанные"},
{"source": "AURORA-9006", "target": "AURORA-9500", "link_type": "ASUPR_Блокер_Начала"},
{"source": "AURORA-9001", "target": "AURORA-9100", "link_type": "ASUPR_Блокер_Начала"},
{"source": "AURORA-9009", "target": "AURORA-9002", "link_type": "Duplicate"},
{"source": "AURORA-9001", "target": "lower-1", "link_type": "ASUPR_Связанные"},
{"source": "AURORA-9007", "target": "AURORA-9009", "link_type": "ASUPR_Блокер_Начала"}
]
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
{"generated_at": "2026-10-14T10:00:00+00:00", "last_sync": null, "issues": [], "links": []}

View File

@ -0,0 +1,93 @@
{
"model": {
"fileName": "Хаб",
"hasSprint": true,
"links": [],
"missing": 0,
"nodes": [],
"problems": [
"в хабе ещё не было полного обновления из Jira — нажмите «Обновить из Jira»"
],
"sprints": {},
"stubLane": "Нет в зеркале"
},
"scenario": {
"emptyReason": "В зеркале хаба нет задач.",
"layout": {
"baseW": 1208,
"board": {
"height": "auto",
"width": "100%"
},
"clamp": {},
"colBounds": {},
"dateCols": [],
"dateLabels": [
{
"className": "dlabel none",
"innerHTML": "без срока<span class=\"n\">1</span>",
"left": "20px",
"title": "плановая дата не задана"
},
{
"className": "dlabel past",
"innerHTML": "10.10<span class=\"n\">1</span>",
"left": "316px",
"title": "срок прошёл"
},
{
"className": "dlabel past",
"innerHTML": "12.10<span class=\"n\">1</span>",
"left": "612px",
"title": "срок прошёл"
},
{
"className": "dlabel",
"innerHTML": "16.10<span class=\"n\">1</span>",
"left": "908px",
"title": "плановая дата окончания"
},
{
"className": "dlabel",
"innerHTML": "18.10<span class=\"n\">1</span>",
"left": "1204px",
"title": "плановая дата окончания"
},
{
"className": "dlabel",
"innerHTML": "20.10<span class=\"n\">3</span>",
"left": "1500px",
"title": "плановая дата окончания"
},
{
"className": "dlabel",
"innerHTML": "25.10<span class=\"n\">1</span>",
"left": "1900px",
"title": "плановая дата окончания"
}
],
"edges": [],
"laneOf": {},
"lanes": [],
"pos": {},
"rel": {}
},
"visible": {
"lanes": [],
"links": [],
"nodes": [],
"problems": {
"dates": [],
"hand": [],
"late": [],
"unassigned": []
},
"removed": {
"core": 0,
"ctx": 0,
"edge": 0,
"loose": 0
}
}
}
}

View File

@ -0,0 +1,2 @@
Номер,Название
1,x
1 Номер Название
2 1 x

View File

@ -0,0 +1,3 @@
{
"error": "не нашёл колонки «Ключ проблемы» и «Тема»; в файле: Номер, Название…"
}

View File

@ -0,0 +1,7 @@
Ключ проблемы,Тема,Статус,Приоритет,Тип задачи,Метки,Метки,Связи Фичи,Плановая дата окончания,Исполнитель,Спринт,Спринт,Внешняя ссылка на проблему (ASUPR_Блокер_Начала),Внутренняя ссылка на проблему (ASUPR_Блокер_Начала),Внешняя ссылка на проблему (ASUPR_Связанные)
AURORA-9100,Тестовый эпик,В работе,Средний,Фича,AWPCA,,,,,,,,,
AURORA-9001,"Схема свода, черновик",В работе,Высокий,Задача,AWPCA,kind-dev,AURORA-9100,20.10.2026,Иванов Иван Иванович,AWPCA_Спринт 1,AWPCA_Спринт 2,AURORA-9002,AURORA-9003,AURORA-9008
AURORA-9002,"API ""свода""",Ожидание разработки,Средний,Задача,kind-dev,,AURORA-9100,20/окт/26,Петров Пётр Петрович,AWPCA_Спринт 2,,,,
AURORA-9003,Макет экрана,Backlog,Критичный,Задача,kind-ui,,AURORA-1359,2026-10-16,,AWPCA_Спринт 2,,AURORA-9001,,
AURORA-9006,Задача без эпика,Готово,Средний,Задача,,,,10/Oct/26 12:00 PM,Иванов Иван Иванович,,,AURORA-9500,,
not-a-key,мусор,,,,,,,,,,,,,
1 Ключ проблемы Тема Статус Приоритет Тип задачи Метки Метки Связи Фичи Плановая дата окончания Исполнитель Спринт Спринт Внешняя ссылка на проблему (ASUPR_Блокер_Начала) Внутренняя ссылка на проблему (ASUPR_Блокер_Начала) Внешняя ссылка на проблему (ASUPR_Связанные)
2 AURORA-9100 Тестовый эпик В работе Средний Фича AWPCA
3 AURORA-9001 Схема свода, черновик В работе Высокий Задача AWPCA kind-dev AURORA-9100 20.10.2026 Иванов Иван Иванович AWPCA_Спринт 1 AWPCA_Спринт 2 AURORA-9002 AURORA-9003 AURORA-9008
4 AURORA-9002 API "свода" Ожидание разработки Средний Задача kind-dev AURORA-9100 20/окт/26 Петров Пётр Петрович AWPCA_Спринт 2
5 AURORA-9003 Макет экрана Backlog Критичный Задача kind-ui AURORA-1359 2026-10-16 AWPCA_Спринт 2 AURORA-9001
6 AURORA-9006 Задача без эпика Готово Средний Задача 10/Oct/26 12:00 PM Иванов Иван Иванович AURORA-9500
7 not-a-key мусор

View File

@ -0,0 +1,228 @@
{
"model": {
"fileName": "jira-export.csv",
"hasSprint": true,
"links": [
{
"dst": "AURORA-9002",
"src": "AURORA-9001",
"type": "блокирует начало"
},
{
"dst": "AURORA-9001",
"src": "AURORA-9003",
"type": "блокирует начало"
},
{
"dst": "AURORA-9008",
"src": "AURORA-9001",
"type": "связанные"
},
{
"dst": "AURORA-9500",
"src": "AURORA-9006",
"type": "блокирует начало"
}
],
"missing": 2,
"nodes": [
{
"due": "2026-10-20",
"epic": "Тестовый эпик",
"key": "AURORA-9001",
"kind": "разработка",
"prio": "Высокий",
"sprint": "AWPCA_Спринт 2",
"status": "В работе",
"stub": false,
"title": "Схема свода, черновик",
"who": "Иванов Иван Иванович"
},
{
"due": "2026-10-20",
"epic": "Тестовый эпик",
"key": "AURORA-9002",
"kind": "разработка",
"prio": "Средний",
"sprint": "AWPCA_Спринт 2",
"status": "Ожидание разработки",
"stub": false,
"title": "API \"свода\"",
"who": "Петров Пётр Петрович"
},
{
"due": "2026-10-16",
"epic": "Гудвилл",
"key": "AURORA-9003",
"kind": "UI",
"prio": "Критичный",
"sprint": "AWPCA_Спринт 2",
"status": "Backlog",
"stub": false,
"title": "Макет экрана",
"who": ""
},
{
"due": "2026-10-10",
"epic": "Без эпика",
"key": "AURORA-9006",
"kind": "Задача",
"prio": "Средний",
"sprint": "",
"status": "Готово",
"stub": false,
"title": "Задача без эпика",
"who": "Иванов Иван Иванович"
},
{
"due": "",
"epic": "Нет в файле",
"key": "AURORA-9008",
"kind": "",
"prio": "",
"sprint": "",
"status": "",
"stub": true,
"title": "нет в файле",
"who": ""
},
{
"due": "",
"epic": "Нет в файле",
"key": "AURORA-9500",
"kind": "",
"prio": "",
"sprint": "",
"status": "",
"stub": true,
"title": "нет в файле",
"who": ""
}
],
"problems": [],
"sprints": {
"AWPCA_Спринт 2": 3
}
},
"rows": [
[
"Ключ проблемы",
"Тема",
"Статус",
"Приоритет",
"Тип задачи",
"Метки",
"Метки",
"Связи Фичи",
"Плановая дата окончания",
"Исполнитель",
"Спринт",
"Спринт",
"Внешняя ссылка на проблему (ASUPR_Блокер_Начала)",
"Внутренняя ссылка на проблему (ASUPR_Блокер_Начала)",
"Внешняя ссылка на проблему (ASUPR_Связанные)"
],
[
"AURORA-9100",
"Тестовый эпик",
"В работе",
"Средний",
"Фича",
"AWPCA",
"",
"",
"",
"",
"",
"",
"",
"",
""
],
[
"AURORA-9001",
"Схема свода, черновик",
"В работе",
"Высокий",
"Задача",
"AWPCA",
"kind-dev",
"AURORA-9100",
"20.10.2026",
"Иванов Иван Иванович",
"AWPCA_Спринт 1",
"AWPCA_Спринт 2",
"AURORA-9002",
"AURORA-9003",
"AURORA-9008"
],
[
"AURORA-9002",
"API \"свода\"",
"Ожидание разработки",
"Средний",
"Задача",
"kind-dev",
"",
"AURORA-9100",
"20/окт/26",
"Петров Пётр Петрович",
"AWPCA_Спринт 2",
"",
"",
"",
""
],
[
"AURORA-9003",
"Макет экрана",
"Backlog",
"Критичный",
"Задача",
"kind-ui",
"",
"AURORA-1359",
"2026-10-16",
"",
"AWPCA_Спринт 2",
"",
"AURORA-9001",
"",
""
],
[
"AURORA-9006",
"Задача без эпика",
"Готово",
"Средний",
"Задача",
"",
"",
"",
"10/Oct/26 12:00 PM",
"Иванов Иван Иванович",
"",
"",
"AURORA-9500",
"",
""
],
[
"not-a-key",
"мусор",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
""
]
]
}

View File

@ -0,0 +1,2 @@
Issue key,Summary,Status
AURORA-9001,Тема,Open
1 Issue key Summary Status
2 AURORA-9001 Тема Open

View File

@ -0,0 +1,28 @@
{
"model": {
"fileName": "jira-no-links.csv",
"hasSprint": false,
"links": [],
"missing": 0,
"nodes": [
{
"due": "",
"epic": "Без эпика",
"key": "AURORA-9001",
"kind": "",
"prio": "",
"sprint": "",
"status": "Open",
"stub": false,
"title": "Тема",
"who": ""
}
],
"problems": [
"в файле нет колонок связей — в фильтре Jira добавьте колонку «Связанные задачи» или экспортируйте «все поля»",
"в файле нет колонки «Спринт» — выбор спринта недоступен, все задачи считаются выбранными",
"нет колонки «Связи Фичи» — дорожки по эпикам не построить"
],
"sprints": {}
}
}

View File

@ -0,0 +1,131 @@
{
"S": {
"byDate": true,
"crit": false,
"hideEdge": [],
"hideKind": [
"org"
],
"neighbors": true,
"onlyHand": true,
"onlyLinked": false
},
"import": {
"collapsedLanes": [
"Без эпика"
],
"dateColumns": {
"2026-10-20": 360
},
"filters": {
"assignees": [
"Петров Пётр Петрович"
],
"byDate": true,
"criticalPath": false,
"hiddenEdges": [],
"hiddenKinds": [
"org"
],
"mode": "linked",
"neighbors": true,
"onlyHand": true,
"sprints": [
"AWPCA_Спринт 2"
]
},
"laneOrder": [
"Гудвилл",
"Тестовый эпик"
],
"layout": {
"AURORA-9001": {
"ry": 40,
"w": 240,
"x": 24
}
},
"layoutByDate": {
"AURORA-9001": {
"h": 144,
"ry": 40,
"w": 240,
"x": 6
}
},
"model": {
"fileName": "sprint2.csv",
"hasSprint": true,
"links": [
{
"dst": "AURORA-9002",
"src": "AURORA-9001",
"type": "блокирует начало"
},
{
"dst": "AURORA-9777",
"src": "AURORA-9002",
"type": "блокирует начало"
}
],
"missing": 1,
"nodes": [
{
"due": "2026-10-20",
"epic": "Тестовый эпик",
"key": "AURORA-9001",
"kind": "разработка",
"prio": "Высокий",
"sprint": "AWPCA_Спринт 2",
"status": "В работе",
"stub": false,
"title": "Схема свода",
"who": "Иванов Иван Иванович"
},
{
"due": "2026-10-20",
"epic": "Тестовый эпик",
"key": "AURORA-9002",
"kind": "разработка",
"prio": "Средний",
"sprint": "AWPCA_Спринт 2",
"status": "Ожидание разработки",
"stub": false,
"title": "API свода",
"who": "Петров Пётр Петрович"
},
{
"due": "",
"epic": "Нет в файле",
"key": "AURORA-9777",
"kind": "",
"prio": "",
"sprint": "",
"status": "",
"stub": true,
"title": "нет в файле",
"who": ""
}
],
"problems": [],
"sprints": {
"AWPCA_Спринт 2": 2
}
}
},
"storage": {
"graph-bydate": "1",
"graph-colw:board": "{\"2026-10-20\":360}",
"graph-crit": "0",
"graph-fold:board": "[\"Без эпика\"]",
"graph-hideedge": "[]",
"graph-hidekind": "[\"org\"]",
"graph-lanes:board": "[\"Гудвилл\",\"Тестовый эпик\"]",
"graph-neighbors": "1",
"graph-onlyhand": "1",
"graph-pos:board": "{\"AURORA-9001\":{\"x\":24,\"ry\":40,\"w\":240}}",
"graph-posdate:board": "{\"AURORA-9001\":{\"x\":6,\"ry\":40,\"w\":240,\"h\":144}}",
"graph-sprint:board": "[\"AWPCA_Спринт 2\"]",
"graph-who:board": "[\"Петров Пётр Петрович\"]"
}
}

View File

@ -0,0 +1,11 @@
{"format": "arm-ka-links/1", "exportedAt": "2026-10-11T08:00:00.000Z", "source": "sprint2.csv",
"filters": {"mode": "linked", "sprints": ["AWPCA_Спринт 2"], "assignees": ["Петров Пётр Петрович"], "onlyHand": true, "criticalPath": false, "byDate": true, "hiddenKinds": ["org"], "hiddenEdges": []},
"laneOrder": ["Гудвилл", "Тестовый эпик"], "collapsedLanes": ["Без эпика"], "dateColumns": {"2026-10-20": 360},
"nodes": [
{"key": "AURORA-9001", "title": "Схема свода", "status": "В работе", "priority": "Высокий", "kind": "разработка", "due": "2026-10-20", "assignee": "Иванов Иван Иванович", "sprint": "AWPCA_Спринт 2", "epic": "Тестовый эпик", "layout": {"x": 24, "ry": 40, "w": 240}, "layoutByDate": {"x": 6, "ry": 40, "w": 240, "h": 144}},
{"key": "AURORA-9002", "title": "API свода", "status": "Ожидание разработки", "priority": "Средний", "kind": "разработка", "due": "20.10.2026", "assignee": "Петров Пётр Петрович", "sprint": "AWPCA_Спринт 2", "epic": "Тестовый эпик", "layout": {"x": "плохо", "ry": 40}},
{"key": "AURORA-9001", "title": "дубль", "epic": "x"},
{"key": "AURORA-9500", "stub": true},
{"key": "bad key"}
],
"links": [{"from": "AURORA-9001", "to": "AURORA-9002", "type": "блокирует начало"}, {"from": "AURORA-9002", "to": "AURORA-9777", "type": "неизвестный"}, {"from": "x", "to": "AURORA-9001", "type": "связанные"}]}

View File

@ -0,0 +1,14 @@
[
{"name": "default", "filters": {}},
{"name": "sprint-2", "filters": {"sprints": ["AWPCA_Спринт 2"]}},
{"name": "sprint-2-neighbors", "filters": {"sprints": ["AWPCA_Спринт 2"], "neighbors": true}},
{"name": "who-ivanov", "filters": {"who": ["Иванов Иван Иванович"], "neighbors": true}},
{"name": "hand-linked", "filters": {"onlyHand": true, "onlyLinked": true}},
{"name": "crit", "filters": {"crit": true}},
{"name": "hide", "filters": {"hideKind": ["dev"], "hideEdge": ["rel"]}},
{"name": "empty", "filters": {"who": ["Никто Никтович"]}},
{"name": "lane-order", "filters": {}, "laneOrder": ["Гудвилл", "Без эпика", "Тестовый эпик"]},
{"name": "by-date", "filters": {"byDate": true}},
{"name": "saved-folded", "filters": {}, "saved": {"AURORA-9002": {"x": 600, "ry": 80, "w": 280, "h": 160}, "AURORA-9003": {"x": 30, "ry": 50}}, "folded": ["Гудвилл"]},
{"name": "by-date-colw", "filters": {"byDate": true}, "colWidths": {"2026-10-20": 400}, "savedDate": {"AURORA-9001": {"x": 10, "ry": 60, "w": 250}}}
]

View File

@ -0,0 +1,5 @@
{
"kind_labels": {"аналитика": "kind-analytics", "решение": "kind-decision", "актуализация": "kind-docs", "разработка": "kind-dev", "UI": "kind-ui", "тестирование": "kind-test", "организационная": "kind-org"},
"link_types": {"блокирует начало": "ASUPR_Блокер_Начала", "блокирует завершение": "ASUPR_Блокер_Завершения", "связанные": "ASUPR_Связанные"},
"status_labels": {"Готово — закрыть": "fact-done", "В работе": "fact-in-progress", "Ожидание разработки": "fact-open"}
}

View File

@ -0,0 +1,6 @@
[
{"name": "migrate-largest", "op": "migrate", "before": {"graph-pos:old.csv": "{\"AURORA-9001\":{\"x\":1,\"ry\":40}}", "graph-pos:new.csv": "{\"AURORA-9001\":{\"x\":1,\"ry\":40},\"AURORA-9002\":{\"x\":300,\"ry\":40}}", "graph-lanes:new.csv": "[\"Гудвилл\"]", "graph-fold:old.csv": "[\"x\"]"}},
{"name": "already-migrated", "op": "migrate", "before": {"graph-pos:board": "{}", "graph-pos:new.csv": "{\"AURORA-9001\":{\"x\":1,\"ry\":40}}"}},
{"name": "mode-linked", "op": "startup", "before": {"graph-mode": "linked"}},
{"name": "mode-all", "op": "startup", "before": {"graph-mode": "all"}}
]

View File

@ -0,0 +1,20 @@
{
"already-migrated": {
"graph-pos:board": "{}",
"graph-pos:new.csv": "{\"AURORA-9001\":{\"x\":1,\"ry\":40}}"
},
"migrate-largest": {
"graph-fold:old.csv": "[\"x\"]",
"graph-lanes:board": "[\"Гудвилл\"]",
"graph-lanes:new.csv": "[\"Гудвилл\"]",
"graph-pos:board": "{\"AURORA-9001\":{\"x\":1,\"ry\":40},\"AURORA-9002\":{\"x\":300,\"ry\":40}}",
"graph-pos:new.csv": "{\"AURORA-9001\":{\"x\":1,\"ry\":40},\"AURORA-9002\":{\"x\":300,\"ry\":40}}",
"graph-pos:old.csv": "{\"AURORA-9001\":{\"x\":1,\"ry\":40}}"
},
"mode-all": {
"graph-sprint:board": "[]"
},
"mode-linked": {
"graph-neighbors": "1"
}
}