test(web): golden-эталоны доски из links.html@3931a88 на синтетических фикстурах (#21)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7c161a675e
commit
7eac72e86f
195
web/scripts/golden-from-links.ts
Normal file
195
web/scripts/golden-from-links.ts
Normal 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);
|
||||
12
web/src/board/__fixtures__/geometry.cases.json
Normal file
12
web/src/board/__fixtures__/geometry.cases.json
Normal 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}]}
|
||||
56
web/src/board/__fixtures__/geometry.expected.json
Normal file
56
web/src/board/__fixtures__/geometry.expected.json
Normal 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"
|
||||
]
|
||||
}
|
||||
29
web/src/board/__fixtures__/hub-basic.board.json
Normal file
29
web/src/board/__fixtures__/hub-basic.board.json
Normal 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_Блокер_Начала"}
|
||||
]
|
||||
}
|
||||
8014
web/src/board/__fixtures__/hub-basic.expected.json
Normal file
8014
web/src/board/__fixtures__/hub-basic.expected.json
Normal file
File diff suppressed because it is too large
Load Diff
1
web/src/board/__fixtures__/hub-empty.board.json
Normal file
1
web/src/board/__fixtures__/hub-empty.board.json
Normal file
@ -0,0 +1 @@
|
||||
{"generated_at": "2026-10-14T10:00:00+00:00", "last_sync": null, "issues": [], "links": []}
|
||||
93
web/src/board/__fixtures__/hub-empty.expected.json
Normal file
93
web/src/board/__fixtures__/hub-empty.expected.json
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
web/src/board/__fixtures__/jira-bad.csv
Normal file
2
web/src/board/__fixtures__/jira-bad.csv
Normal file
@ -0,0 +1,2 @@
|
||||
Номер,Название
|
||||
1,x
|
||||
|
3
web/src/board/__fixtures__/jira-bad.expected.json
Normal file
3
web/src/board/__fixtures__/jira-bad.expected.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"error": "не нашёл колонки «Ключ проблемы» и «Тема»; в файле: Номер, Название…"
|
||||
}
|
||||
7
web/src/board/__fixtures__/jira-export.csv
Normal file
7
web/src/board/__fixtures__/jira-export.csv
Normal 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,мусор,,,,,,,,,,,,,
|
||||
|
228
web/src/board/__fixtures__/jira-export.expected.json
Normal file
228
web/src/board/__fixtures__/jira-export.expected.json
Normal 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",
|
||||
"мусор",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
]
|
||||
]
|
||||
}
|
||||
2
web/src/board/__fixtures__/jira-no-links.csv
Normal file
2
web/src/board/__fixtures__/jira-no-links.csv
Normal file
@ -0,0 +1,2 @@
|
||||
Issue key,Summary,Status
|
||||
AURORA-9001,Тема,Open
|
||||
|
28
web/src/board/__fixtures__/jira-no-links.expected.json
Normal file
28
web/src/board/__fixtures__/jira-no-links.expected.json
Normal 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": {}
|
||||
}
|
||||
}
|
||||
131
web/src/board/__fixtures__/old-board.expected.json
Normal file
131
web/src/board/__fixtures__/old-board.expected.json
Normal 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": "[\"Петров Пётр Петрович\"]"
|
||||
}
|
||||
}
|
||||
11
web/src/board/__fixtures__/old-board.json
Normal file
11
web/src/board/__fixtures__/old-board.json
Normal 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": "связанные"}]}
|
||||
14
web/src/board/__fixtures__/scenarios.json
Normal file
14
web/src/board/__fixtures__/scenarios.json
Normal 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}}}
|
||||
]
|
||||
5
web/src/board/__fixtures__/settings.json
Normal file
5
web/src/board/__fixtures__/settings.json
Normal 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"}
|
||||
}
|
||||
6
web/src/board/__fixtures__/storage.cases.json
Normal file
6
web/src/board/__fixtures__/storage.cases.json
Normal 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"}}
|
||||
]
|
||||
20
web/src/board/__fixtures__/storage.expected.json
Normal file
20
web/src/board/__fixtures__/storage.expected.json
Normal 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"
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user