test(web): сценарий saved-short для обрезки заголовков, canon без локали, тесты хранилища и util (T2.1, T5.1, T6.3, T7.1, T9.1, T9.3, T9.4, #29)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a002812f12
commit
dd95e85a25
@ -6312,6 +6312,788 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"saved-short": {
|
||||
"emptyReason": null,
|
||||
"layout": {
|
||||
"baseW": 1208,
|
||||
"board": {
|
||||
"height": "1124px",
|
||||
"width": "1208px"
|
||||
},
|
||||
"clamp": {
|
||||
"AURORA-9001": "2",
|
||||
"AURORA-9002": "3",
|
||||
"AURORA-9003": "unset",
|
||||
"AURORA-9004": "6",
|
||||
"AURORA-9005": "unset",
|
||||
"AURORA-9006": "unset",
|
||||
"AURORA-9007": "unset",
|
||||
"AURORA-9008": "12",
|
||||
"AURORA-9009": "unset"
|
||||
},
|
||||
"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": [
|
||||
{
|
||||
"crit": false,
|
||||
"d": "M270 372 L291 372 Q295 372 295 376 L295 376 Q295 380 299 380 L320 380",
|
||||
"dst": "AURORA-9002",
|
||||
"hand": true,
|
||||
"src": "AURORA-9001",
|
||||
"type": "блокирует начало"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"d": "M264 498 L288 498 Q298 498 298 488 L298 382 Q298 372 288 372 L270 372",
|
||||
"dst": "AURORA-9001",
|
||||
"hand": true,
|
||||
"src": "AURORA-9003",
|
||||
"type": "блокирует начало"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"d": "M560 380 L726 380 Q736 380 736 370 L736 110 Q736 100 746 100 L912 100",
|
||||
"dst": "AURORA-9005",
|
||||
"hand": false,
|
||||
"src": "AURORA-9002",
|
||||
"type": "блокирует завершение"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"d": "M270 150 L285 150 Q295 150 295 160 L295 370 Q295 380 305 380 L320 380",
|
||||
"dst": "AURORA-9002",
|
||||
"hand": true,
|
||||
"src": "AURORA-9004",
|
||||
"type": "блокирует начало"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"d": "M150 432 L150 516 Q150 526 160 526 L160 526 Q170 526 170 536 L170 620",
|
||||
"dst": "AURORA-9008",
|
||||
"hand": false,
|
||||
"src": "AURORA-9001",
|
||||
"type": "связанные"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"d": "M264 362 L320 362",
|
||||
"dst": "AURORA-9009",
|
||||
"hand": true,
|
||||
"src": "AURORA-9007",
|
||||
"type": "блокирует начало"
|
||||
}
|
||||
],
|
||||
"laneOf": {
|
||||
"AURORA-9001": 1,
|
||||
"AURORA-9002": 1,
|
||||
"AURORA-9003": 1,
|
||||
"AURORA-9004": 0,
|
||||
"AURORA-9005": 0,
|
||||
"AURORA-9006": 3,
|
||||
"AURORA-9007": 1,
|
||||
"AURORA-9008": 2,
|
||||
"AURORA-9009": 1
|
||||
},
|
||||
"lanes": [
|
||||
{
|
||||
"folded": false,
|
||||
"h": 262,
|
||||
"minH": 144,
|
||||
"name": "Гудвилл",
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"folded": false,
|
||||
"h": 308,
|
||||
"minH": 144,
|
||||
"name": "Тестовый эпик",
|
||||
"y": 262
|
||||
},
|
||||
{
|
||||
"folded": false,
|
||||
"h": 382,
|
||||
"minH": 144,
|
||||
"name": "AURORA-9999",
|
||||
"y": 570
|
||||
},
|
||||
{
|
||||
"folded": false,
|
||||
"h": 172,
|
||||
"minH": 144,
|
||||
"name": "Без эпика",
|
||||
"y": 952
|
||||
}
|
||||
],
|
||||
"pos": {
|
||||
"AURORA-9001": {
|
||||
"h": 120,
|
||||
"w": 240,
|
||||
"x": 30,
|
||||
"y": 312
|
||||
},
|
||||
"AURORA-9002": {
|
||||
"h": 136,
|
||||
"w": 240,
|
||||
"x": 320,
|
||||
"y": 312
|
||||
},
|
||||
"AURORA-9003": {
|
||||
"h": 120,
|
||||
"w": 240,
|
||||
"x": 24,
|
||||
"y": 438
|
||||
},
|
||||
"AURORA-9004": {
|
||||
"h": 200,
|
||||
"w": 240,
|
||||
"x": 30,
|
||||
"y": 50
|
||||
},
|
||||
"AURORA-9005": {
|
||||
"h": 120,
|
||||
"w": 240,
|
||||
"x": 912,
|
||||
"y": 40
|
||||
},
|
||||
"AURORA-9006": {
|
||||
"h": 120,
|
||||
"w": 240,
|
||||
"x": 24,
|
||||
"y": 992
|
||||
},
|
||||
"AURORA-9007": {
|
||||
"h": 120,
|
||||
"w": 240,
|
||||
"x": 24,
|
||||
"y": 302
|
||||
},
|
||||
"AURORA-9008": {
|
||||
"h": 320,
|
||||
"w": 280,
|
||||
"x": 30,
|
||||
"y": 620
|
||||
},
|
||||
"AURORA-9009": {
|
||||
"h": 120,
|
||||
"w": 240,
|
||||
"x": 320,
|
||||
"y": 302
|
||||
}
|
||||
},
|
||||
"rel": {
|
||||
"AURORA-9001": {
|
||||
"auto": false,
|
||||
"h": 120,
|
||||
"other": 79,
|
||||
"ry": 50,
|
||||
"w": 240,
|
||||
"x": 30
|
||||
},
|
||||
"AURORA-9002": {
|
||||
"auto": false,
|
||||
"h": 136,
|
||||
"other": 79,
|
||||
"ry": 50,
|
||||
"w": 240,
|
||||
"x": 320
|
||||
},
|
||||
"AURORA-9003": {
|
||||
"auto": true,
|
||||
"h": 120,
|
||||
"other": 79,
|
||||
"ry": 176,
|
||||
"w": 240,
|
||||
"x": 24
|
||||
},
|
||||
"AURORA-9004": {
|
||||
"auto": false,
|
||||
"h": 200,
|
||||
"other": 79,
|
||||
"ry": 50,
|
||||
"w": 240,
|
||||
"x": 30
|
||||
},
|
||||
"AURORA-9005": {
|
||||
"auto": true,
|
||||
"h": 120,
|
||||
"other": 79,
|
||||
"ry": 40,
|
||||
"w": 240,
|
||||
"x": 912
|
||||
},
|
||||
"AURORA-9006": {
|
||||
"auto": true,
|
||||
"h": 120,
|
||||
"other": 79,
|
||||
"ry": 40,
|
||||
"w": 240,
|
||||
"x": 24
|
||||
},
|
||||
"AURORA-9007": {
|
||||
"auto": true,
|
||||
"h": 120,
|
||||
"other": 79,
|
||||
"ry": 40,
|
||||
"w": 240,
|
||||
"x": 24
|
||||
},
|
||||
"AURORA-9008": {
|
||||
"auto": false,
|
||||
"h": 320,
|
||||
"other": 79,
|
||||
"ry": 50,
|
||||
"w": 280,
|
||||
"x": 30
|
||||
},
|
||||
"AURORA-9009": {
|
||||
"auto": true,
|
||||
"h": 120,
|
||||
"other": 79,
|
||||
"ry": 40,
|
||||
"w": 240,
|
||||
"x": 320
|
||||
}
|
||||
}
|
||||
},
|
||||
"visible": {
|
||||
"lanes": [
|
||||
{
|
||||
"keys": [
|
||||
"AURORA-9004",
|
||||
"AURORA-9005"
|
||||
],
|
||||
"name": "Гудвилл"
|
||||
},
|
||||
{
|
||||
"keys": [
|
||||
"AURORA-9001",
|
||||
"AURORA-9002",
|
||||
"AURORA-9003",
|
||||
"AURORA-9007",
|
||||
"AURORA-9009"
|
||||
],
|
||||
"name": "Тестовый эпик"
|
||||
},
|
||||
{
|
||||
"keys": [
|
||||
"AURORA-9008"
|
||||
],
|
||||
"name": "AURORA-9999"
|
||||
},
|
||||
{
|
||||
"keys": [
|
||||
"AURORA-9006"
|
||||
],
|
||||
"name": "Без эпика"
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"crit": false,
|
||||
"dst": "AURORA-9002",
|
||||
"hand": true,
|
||||
"src": "AURORA-9001",
|
||||
"type": "блокирует начало"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"dst": "AURORA-9001",
|
||||
"hand": true,
|
||||
"src": "AURORA-9003",
|
||||
"type": "блокирует начало"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"dst": "AURORA-9005",
|
||||
"hand": false,
|
||||
"src": "AURORA-9002",
|
||||
"type": "блокирует завершение"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"dst": "AURORA-9002",
|
||||
"hand": true,
|
||||
"src": "AURORA-9004",
|
||||
"type": "блокирует начало"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"dst": "AURORA-9008",
|
||||
"hand": false,
|
||||
"src": "AURORA-9001",
|
||||
"type": "связанные"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"dst": "AURORA-9009",
|
||||
"hand": true,
|
||||
"src": "AURORA-9007",
|
||||
"type": "блокирует начало"
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-20",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9001",
|
||||
"kind": "разработка",
|
||||
"prio": "Высокий",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "В работе",
|
||||
"stub": false,
|
||||
"title": "Схема свода",
|
||||
"who": "Иванов Иван Иванович"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-20",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9002",
|
||||
"kind": "разработка",
|
||||
"prio": "Средний",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "Ожидание разработки",
|
||||
"stub": false,
|
||||
"title": "API свода",
|
||||
"who": "Петров Пётр Петрович"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-16",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9003",
|
||||
"kind": "UI",
|
||||
"prio": "Критичный",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "Backlog",
|
||||
"stub": false,
|
||||
"title": "Макет экрана",
|
||||
"who": ""
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-25",
|
||||
"epic": "Гудвилл",
|
||||
"focus": false,
|
||||
"key": "AURORA-9004",
|
||||
"kind": "решение",
|
||||
"prio": "Наивысший",
|
||||
"sprint": "AWPCA_Спринт 3",
|
||||
"status": "К выполнению",
|
||||
"stub": false,
|
||||
"title": "Решение по порогам",
|
||||
"who": "Сидорова Анна"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "",
|
||||
"epic": "Гудвилл",
|
||||
"focus": false,
|
||||
"key": "AURORA-9005",
|
||||
"kind": "тестирование",
|
||||
"prio": "Низкий",
|
||||
"sprint": "AWPCA_Спринт 3",
|
||||
"status": "Ожидание",
|
||||
"stub": false,
|
||||
"title": "Тест-кейсы",
|
||||
"who": "Петров Пётр Петрович"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-10",
|
||||
"epic": "Без эпика",
|
||||
"focus": false,
|
||||
"key": "AURORA-9006",
|
||||
"kind": "Задача",
|
||||
"prio": "Средний",
|
||||
"sprint": "",
|
||||
"status": "Готово",
|
||||
"stub": false,
|
||||
"title": "Задача без эпика",
|
||||
"who": "Иванов Иван Иванович"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-12",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9007",
|
||||
"kind": "аналитика",
|
||||
"prio": "Средний",
|
||||
"sprint": "AWPCA_Спринт 1",
|
||||
"status": "В работе",
|
||||
"stub": false,
|
||||
"title": "Сосед по связям",
|
||||
"who": "Kozlov-AA"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-18",
|
||||
"epic": "AURORA-9999",
|
||||
"focus": false,
|
||||
"key": "AURORA-9008",
|
||||
"kind": "актуализация",
|
||||
"prio": "Самый низкий",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "Ожидание разработки",
|
||||
"stub": false,
|
||||
"title": "Актуализация ТЗ",
|
||||
"who": "Иванов Иван Иванович"
|
||||
},
|
||||
{
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-20",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9009",
|
||||
"kind": "организационная",
|
||||
"prio": "Средний",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "",
|
||||
"stub": false,
|
||||
"title": "Созвон с заказчиком",
|
||||
"who": "Иванов Иван Иванович"
|
||||
}
|
||||
],
|
||||
"problems": {
|
||||
"dates": [
|
||||
{
|
||||
"a": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-20",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9001",
|
||||
"kind": "разработка",
|
||||
"prio": "Высокий",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "В работе",
|
||||
"stub": false,
|
||||
"title": "Схема свода",
|
||||
"who": "Иванов Иван Иванович"
|
||||
},
|
||||
"b": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-20",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9002",
|
||||
"kind": "разработка",
|
||||
"prio": "Средний",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "Ожидание разработки",
|
||||
"stub": false,
|
||||
"title": "API свода",
|
||||
"who": "Петров Пётр Петрович"
|
||||
},
|
||||
"text": "сроки совпадают (20.10): запаса нет"
|
||||
},
|
||||
{
|
||||
"a": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-25",
|
||||
"epic": "Гудвилл",
|
||||
"focus": false,
|
||||
"key": "AURORA-9004",
|
||||
"kind": "решение",
|
||||
"prio": "Наивысший",
|
||||
"sprint": "AWPCA_Спринт 3",
|
||||
"status": "К выполнению",
|
||||
"stub": false,
|
||||
"title": "Решение по порогам",
|
||||
"who": "Сидорова Анна"
|
||||
},
|
||||
"b": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-20",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9002",
|
||||
"kind": "разработка",
|
||||
"prio": "Средний",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "Ожидание разработки",
|
||||
"stub": false,
|
||||
"title": "API свода",
|
||||
"who": "Петров Пётр Петрович"
|
||||
},
|
||||
"text": "блокер должен закончиться 25.10, а зависимая — уже 20.10"
|
||||
}
|
||||
],
|
||||
"hand": [
|
||||
{
|
||||
"a": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-20",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9001",
|
||||
"kind": "разработка",
|
||||
"prio": "Высокий",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "В работе",
|
||||
"stub": false,
|
||||
"title": "Схема свода",
|
||||
"who": "Иванов Иван Иванович"
|
||||
},
|
||||
"b": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-20",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9002",
|
||||
"kind": "разработка",
|
||||
"prio": "Средний",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "Ожидание разработки",
|
||||
"stub": false,
|
||||
"title": "API свода",
|
||||
"who": "Петров Пётр Петрович"
|
||||
},
|
||||
"text": "Петров П. П. ждёт Иванов И. И."
|
||||
},
|
||||
{
|
||||
"a": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-25",
|
||||
"epic": "Гудвилл",
|
||||
"focus": false,
|
||||
"key": "AURORA-9004",
|
||||
"kind": "решение",
|
||||
"prio": "Наивысший",
|
||||
"sprint": "AWPCA_Спринт 3",
|
||||
"status": "К выполнению",
|
||||
"stub": false,
|
||||
"title": "Решение по порогам",
|
||||
"who": "Сидорова Анна"
|
||||
},
|
||||
"b": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-20",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9002",
|
||||
"kind": "разработка",
|
||||
"prio": "Средний",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "Ожидание разработки",
|
||||
"stub": false,
|
||||
"title": "API свода",
|
||||
"who": "Петров Пётр Петрович"
|
||||
},
|
||||
"text": "Петров П. П. ждёт Сидорова А."
|
||||
},
|
||||
{
|
||||
"a": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-12",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9007",
|
||||
"kind": "аналитика",
|
||||
"prio": "Средний",
|
||||
"sprint": "AWPCA_Спринт 1",
|
||||
"status": "В работе",
|
||||
"stub": false,
|
||||
"title": "Сосед по связям",
|
||||
"who": "Kozlov-AA"
|
||||
},
|
||||
"b": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-20",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9009",
|
||||
"kind": "организационная",
|
||||
"prio": "Средний",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "",
|
||||
"stub": false,
|
||||
"title": "Созвон с заказчиком",
|
||||
"who": "Иванов Иван Иванович"
|
||||
},
|
||||
"text": "Иванов И. И. ждёт Kozlov-AA"
|
||||
}
|
||||
],
|
||||
"late": [
|
||||
{
|
||||
"a": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-16",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9003",
|
||||
"kind": "UI",
|
||||
"prio": "Критичный",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "Backlog",
|
||||
"stub": false,
|
||||
"title": "Макет экрана",
|
||||
"who": ""
|
||||
},
|
||||
"b": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-20",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9001",
|
||||
"kind": "разработка",
|
||||
"prio": "Высокий",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "В работе",
|
||||
"stub": false,
|
||||
"title": "Схема свода",
|
||||
"who": "Иванов Иван Иванович"
|
||||
},
|
||||
"text": "блокер в статусе «Backlog», а до срока зависимой 6 дн."
|
||||
},
|
||||
{
|
||||
"a": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-25",
|
||||
"epic": "Гудвилл",
|
||||
"focus": false,
|
||||
"key": "AURORA-9004",
|
||||
"kind": "решение",
|
||||
"prio": "Наивысший",
|
||||
"sprint": "AWPCA_Спринт 3",
|
||||
"status": "К выполнению",
|
||||
"stub": false,
|
||||
"title": "Решение по порогам",
|
||||
"who": "Сидорова Анна"
|
||||
},
|
||||
"b": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-20",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9002",
|
||||
"kind": "разработка",
|
||||
"prio": "Средний",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "Ожидание разработки",
|
||||
"stub": false,
|
||||
"title": "API свода",
|
||||
"who": "Петров Пётр Петрович"
|
||||
},
|
||||
"text": "блокер в статусе «К выполнению», а до срока зависимой 6 дн."
|
||||
}
|
||||
],
|
||||
"unassigned": [
|
||||
{
|
||||
"a": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-16",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9003",
|
||||
"kind": "UI",
|
||||
"prio": "Критичный",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "Backlog",
|
||||
"stub": false,
|
||||
"title": "Макет экрана",
|
||||
"who": ""
|
||||
},
|
||||
"b": {
|
||||
"crit": false,
|
||||
"dim": false,
|
||||
"due": "2026-10-20",
|
||||
"epic": "Тестовый эпик",
|
||||
"focus": false,
|
||||
"key": "AURORA-9001",
|
||||
"kind": "разработка",
|
||||
"prio": "Высокий",
|
||||
"sprint": "AWPCA_Спринт 2",
|
||||
"status": "В работе",
|
||||
"stub": false,
|
||||
"title": "Схема свода",
|
||||
"who": "Иванов Иван Иванович"
|
||||
},
|
||||
"text": "Иванов И. И. ждёт задачу без исполнителя"
|
||||
}
|
||||
]
|
||||
},
|
||||
"removed": {
|
||||
"core": 9,
|
||||
"ctx": 0,
|
||||
"edge": 0,
|
||||
"loose": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"sprint-2": {
|
||||
"emptyReason": null,
|
||||
"layout": {
|
||||
|
||||
@ -10,5 +10,6 @@
|
||||
{"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}}}
|
||||
{"name": "by-date-colw", "filters": {"byDate": true}, "colWidths": {"2026-10-20": 400}, "savedDate": {"AURORA-9001": {"x": 10, "ry": 60, "w": 250}}},
|
||||
{"name": "saved-short", "filters": {}, "saved": {"AURORA-9001": {"x": 30, "ry": 50, "w": 240, "h": 120}, "AURORA-9002": {"x": 320, "ry": 50, "w": 240, "h": 136}, "AURORA-9004": {"x": 30, "ry": 50, "w": 240, "h": 200}, "AURORA-9008": {"x": 30, "ry": 50, "w": 280, "h": 320}}}
|
||||
]
|
||||
|
||||
@ -56,7 +56,9 @@ export function initialSprints(model: BoardModel, f: Filters, remembered: unknow
|
||||
}
|
||||
// список исполнителей: чистый фильтр, на доске только их задачи; соседей добавляет кнопка «+ соседи по связям»
|
||||
export function initialWho(model: BoardModel, f: Filters, remembered: unknown): Set<string> {
|
||||
if (!model.nodes.length || whoBox(model, f).kind !== "multi") return new Set();
|
||||
if (!model.nodes.length) return new Set();
|
||||
const { total } = whoItems(model, f);
|
||||
const names = Object.keys(total);
|
||||
if (!names.length || (names.length === 1 && names[0] === "")) return new Set();
|
||||
return new Set(strList(remembered).filter((w) => total[w] !== undefined));
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { CW, KEY_RE, NO_EPIC } from "../constants.ts";
|
||||
import { strList } from "../storage.ts";
|
||||
import type { BoardNode, Dicts, JsonImport, Link, SavedBox } from "../types.ts";
|
||||
import { addLink, addStubs } from "./links.ts";
|
||||
import { strList } from "../storage.ts";
|
||||
import { sortableDate } from "./util.ts";
|
||||
|
||||
type Raw = Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any -- внешний JSON произвольной формы
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { dayDiff, fmtDate, plural, shortName, sortableDate } from "./util.ts";
|
||||
import { dayDiff, emptyModel, fmtDate, fmtStamp, notStarted, plural, shortName, sortableDate, unassigned, whoKey } from "./util.ts";
|
||||
|
||||
describe("util", () => {
|
||||
it("sortableDate приводит даты Jira к ГГГГ-ММ-ДД", () => {
|
||||
@ -19,4 +19,17 @@ describe("util", () => {
|
||||
expect(dayDiff("2026-10-14", "2026-10-20")).toBe(6);
|
||||
expect(dayDiff("", "2026-10-20")).toBeNull();
|
||||
});
|
||||
it("notStarted, unassigned, whoKey, fmtStamp, emptyModel", () => {
|
||||
expect(notStarted("Backlog")).toBe(true);
|
||||
expect(notStarted("К выполнению")).toBe(true);
|
||||
expect(notStarted("В работе")).toBe(false);
|
||||
expect(unassigned("")).toBe(true);
|
||||
expect(unassigned("Не назначен")).toBe(true);
|
||||
expect(unassigned("Иванов")).toBe(false);
|
||||
expect(whoKey(" Иванов ")).toBe("Иванов");
|
||||
expect(whoKey("unassigned")).toBe("");
|
||||
expect(fmtStamp("не дата")).toBe("не дата");
|
||||
expect(fmtStamp("2026-10-14T09:05:00")).toMatch(/^14\.10 09:05$/);
|
||||
expect(emptyModel("пусто")).toMatchObject({ nodes: [], links: [], emptyText: "пусто", missing: 0 });
|
||||
});
|
||||
});
|
||||
|
||||
@ -5,13 +5,14 @@ import type { BoardSnapshot } from "../api/types.ts";
|
||||
import { canon } from "../test/canon.ts";
|
||||
import { computeVisible } from "./analysis/visible.ts";
|
||||
import { dictsFromSettings } from "./dicts.ts";
|
||||
import { exportJson } from "./exportJson.ts";
|
||||
import { exportFileName, exportJson } from "./exportJson.ts";
|
||||
import settings from "./__fixtures__/settings.json";
|
||||
import storageCases from "./__fixtures__/storage.cases.json";
|
||||
import { computeLayout, measuredHeight } from "./layout/layout.ts";
|
||||
import { fromHub } from "./model/fromHub.ts";
|
||||
import { fromJson } from "./model/fromJson.ts";
|
||||
import { applyJsonSettings, migrateMode, migrateStore, readPrefs, saveLayout, savedLayout, setColWidth, writeFlag } from "./storage.ts";
|
||||
import { applyJsonSettings, colWidths, foldedSet, laneOrder, migrateMode, migrateStore, moveLane, readPrefs, resetLayout, saveLayout, savedLayout, setColWidth, toggleLane, writeFlag } from "./storage.ts";
|
||||
import type { BoardModel, JsonImport } from "./types.ts";
|
||||
import { noFilters } from "./types.ts";
|
||||
|
||||
const fx = (name: string) => readFileSync(join(import.meta.dirname, "__fixtures__", name), "utf8");
|
||||
@ -80,3 +81,48 @@ describe("хранилище = links.html@3931a88", () => {
|
||||
expect(savedLayout(false)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("операции хранилища", () => {
|
||||
beforeEach(() => localStorage.clear());
|
||||
it("colWidths пропускает только конечные числа (T9.1)", () => {
|
||||
localStorage.setItem("graph-colw:board", JSON.stringify({ "2026-10-20": 400, "2026-10-21": "x", "2026-10-22": null }));
|
||||
expect(colWidths()).toEqual({ "2026-10-20": 400 });
|
||||
});
|
||||
it("toggleLane сворачивает и разворачивает", () => {
|
||||
toggleLane("Гудвилл");
|
||||
expect(foldedSet()).toEqual(new Set(["Гудвилл"]));
|
||||
toggleLane("Гудвилл");
|
||||
expect(foldedSet()).toEqual(new Set());
|
||||
});
|
||||
it("moveLane переставляет и сохраняет имена вне текущей выборки", () => {
|
||||
localStorage.setItem("graph-lanes:board", JSON.stringify(["A", "B", "C", "Скрытая"]));
|
||||
expect(moveLane(["A", "B", "C"], "A", 2)).toBe(true);
|
||||
expect(laneOrder()).toEqual(["B", "C", "A", "Скрытая"]);
|
||||
expect(moveLane(["A", "B", "C"], "A", 5)).toBe(false);
|
||||
expect(moveLane(["A", "B", "C"], "нет", 0)).toBe(false);
|
||||
});
|
||||
it("resetLayout: обычный режим не трогает «по датам», режим дат чистит и ширины колонок", () => {
|
||||
for (const k of ["pos", "posdate", "lanes", "fold", "colw"]) localStorage.setItem(`graph-${k}:board`, "{}");
|
||||
resetLayout(false);
|
||||
expect(Object.keys(localStorage).sort()).toEqual(["graph-colw:board", "graph-posdate:board"]);
|
||||
resetLayout(true);
|
||||
expect(Object.keys(localStorage)).toEqual([]);
|
||||
});
|
||||
it("exportFileName: имя файла без .csv и недопустимых символов, дата из now", () => {
|
||||
expect(exportFileName({ fileName: "Jira (1).csv" } as BoardModel, "2026-10-14T12:00:00Z")).toBe("links-Jira_1_-2026-10-14.json");
|
||||
expect(exportFileName({ fileName: "" } as BoardModel, "2026-10-14T12:00:00Z")).toBe("links-board-2026-10-14.json");
|
||||
});
|
||||
it("applyJsonSettings: mode=all — пустой выбор спринтов", () => {
|
||||
applyJsonSettings({ layout: {}, layoutByDate: {}, laneOrder: [], collapsedLanes: null, dateColumns: null,
|
||||
filters: { mode: "all", sprints: ["S 2"] } } as unknown as JsonImport);
|
||||
expect(localStorage.getItem("graph-sprint:board")).toBe("[]");
|
||||
});
|
||||
it("закрытое хранилище: чтение — значения по умолчанию, запись и удаление без исключений", () => {
|
||||
const boom = () => { throw new DOMException("closed", "SecurityError"); };
|
||||
vi.spyOn(Storage.prototype, "getItem").mockImplementation(boom);
|
||||
vi.spyOn(Storage.prototype, "setItem").mockImplementation(boom);
|
||||
vi.spyOn(Storage.prototype, "removeItem").mockImplementation(boom);
|
||||
expect(laneOrder()).toEqual([]);
|
||||
expect(() => { toggleLane("A"); resetLayout(true); setColWidth("2026-10-20", 400); }).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@ -48,7 +48,8 @@ export const laneOrder = (): string[] => strList(store.json(fileKey("lanes"), []
|
||||
export const foldedSet = (): Set<string> => new Set(strList(store.json(fileKey("fold"), [])));
|
||||
export function colWidths(): Record<string, number> {
|
||||
const v = store.json<unknown>(fileKey("colw"), {});
|
||||
return isObj(v) ? v as Record<string, number> : {};
|
||||
if (!isObj(v)) return {};
|
||||
return Object.fromEntries(Object.entries(v).filter(([, w]) => num(w))) as Record<string, number>; // T9.1
|
||||
}
|
||||
|
||||
const layoutKey = (byDate: boolean): string => fileKey(byDate ? "posdate" : "pos"); // у обычной раскладки и раскладки «по датам» свои хранилища
|
||||
|
||||
@ -11,4 +11,7 @@ describe("canon", () => {
|
||||
expect(canon(foreign)).toEqual(["a", "b"]);
|
||||
expect(canon(new Set([{ k: 2 }, { k: 1 }]))).toEqual([{ k: 1 }, { k: 2 }]);
|
||||
});
|
||||
it("порядок Set не зависит от локали: сравнение по кодам символов", () => {
|
||||
expect(canon(new Set(["б", "Б", "b", "B"]))).toEqual(["B", "b", "Б", "б"]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
const isSet = (v: unknown): v is Set<unknown> => Object.prototype.toString.call(v) === "[object Set]";
|
||||
|
||||
export function canon(v: unknown): unknown {
|
||||
if (isSet(v)) return [...v].map(canon).sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
|
||||
if (isSet(v)) return [...v].map(canon).sort((a, b) => { const x = JSON.stringify(a), y = JSON.stringify(b); return x < y ? -1 : x > y ? 1 : 0; });
|
||||
if (Array.isArray(v)) return v.map(canon);
|
||||
if (v && typeof v === "object") {
|
||||
const o = v as Record<string, unknown>;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user