@allurereport/web-awesome 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/multi/173.app-d0210ed2e64d38a2ee8e.js +1 -0
  2. package/dist/multi/174.app-d0210ed2e64d38a2ee8e.js +1 -0
  3. package/dist/multi/252.app-d0210ed2e64d38a2ee8e.js +1 -0
  4. package/dist/multi/282.app-d0210ed2e64d38a2ee8e.js +1 -0
  5. package/dist/multi/29.app-d0210ed2e64d38a2ee8e.js +1 -0
  6. package/dist/multi/416.app-d0210ed2e64d38a2ee8e.js +1 -0
  7. package/dist/multi/527.app-d0210ed2e64d38a2ee8e.js +1 -0
  8. package/dist/multi/600.app-d0210ed2e64d38a2ee8e.js +1 -0
  9. package/dist/multi/605.app-d0210ed2e64d38a2ee8e.js +1 -0
  10. package/dist/multi/638.app-d0210ed2e64d38a2ee8e.js +1 -0
  11. package/dist/multi/672.app-d0210ed2e64d38a2ee8e.js +1 -0
  12. package/dist/multi/686.app-d0210ed2e64d38a2ee8e.js +1 -0
  13. package/dist/multi/725.app-d0210ed2e64d38a2ee8e.js +1 -0
  14. package/dist/multi/741.app-d0210ed2e64d38a2ee8e.js +1 -0
  15. package/dist/multi/749.app-d0210ed2e64d38a2ee8e.js +1 -0
  16. package/dist/multi/755.app-d0210ed2e64d38a2ee8e.js +1 -0
  17. package/dist/multi/894.app-d0210ed2e64d38a2ee8e.js +1 -0
  18. package/dist/multi/943.app-d0210ed2e64d38a2ee8e.js +1 -0
  19. package/dist/multi/980.app-d0210ed2e64d38a2ee8e.js +1 -0
  20. package/dist/multi/app-d0210ed2e64d38a2ee8e.js +2 -0
  21. package/dist/multi/manifest.json +21 -21
  22. package/dist/multi/{styles-9e390bad7ce54a807a8e.css → styles-13107bbe6906beabc50f.css} +5 -5
  23. package/dist/single/app-01fed10ad5f9083fd39c.js +2 -0
  24. package/dist/single/manifest.json +1 -1
  25. package/package.json +8 -10
  26. package/src/components/Charts/index.tsx +5 -5
  27. package/src/components/MainReport/index.tsx +52 -47
  28. package/src/components/Metadata/index.tsx +75 -1
  29. package/src/components/Metadata/styles.scss +10 -0
  30. package/src/components/ReportQualityGateResults/index.tsx +77 -19
  31. package/src/components/ReportQualityGateResults/styles.scss +13 -0
  32. package/src/components/TestResult/TrDescription/index.tsx +60 -10
  33. package/src/components/TestResult/TrDescription/styles.scss +4 -0
  34. package/src/components/TestResult/TrError/TrDiff.tsx +2 -6
  35. package/src/components/TestResult/TrError/styles.scss +4 -0
  36. package/src/components/TestResult/TrLinks/index.tsx +4 -4
  37. package/src/components/TestResult/TrOverview.tsx +3 -2
  38. package/src/components/Timeline/index.tsx +2 -5
  39. package/src/locales/az.json +3 -2
  40. package/src/locales/de.json +3 -2
  41. package/src/locales/en.json +3 -2
  42. package/src/locales/es.json +3 -2
  43. package/src/locales/fr.json +3 -2
  44. package/src/locales/he.json +3 -2
  45. package/src/locales/hy.json +3 -2
  46. package/src/locales/it.json +3 -2
  47. package/src/locales/ja.json +3 -2
  48. package/src/locales/ka.json +3 -2
  49. package/src/locales/kr.json +3 -2
  50. package/src/locales/nl.json +3 -2
  51. package/src/locales/pl.json +3 -2
  52. package/src/locales/pt.json +3 -2
  53. package/src/locales/ru.json +3 -2
  54. package/src/locales/sv.json +3 -2
  55. package/src/locales/tr.json +3 -2
  56. package/src/locales/{ua.json → uk.json} +3 -2
  57. package/src/locales/zh.json +3 -2
  58. package/src/stores/locale.ts +69 -37
  59. package/src/stores/qualityGate.ts +2 -2
  60. package/src/stores/timeline.ts +5 -2
  61. package/src/utils/ownerAddress.ts +92 -0
  62. package/src/utils/time.ts +16 -2
  63. package/test/utils/ownerAddress.test.ts +89 -0
  64. package/types.d.ts +1 -1
  65. package/dist/multi/173.app-79c65c7bff941abcbc51.js +0 -1
  66. package/dist/multi/174.app-79c65c7bff941abcbc51.js +0 -1
  67. package/dist/multi/252.app-79c65c7bff941abcbc51.js +0 -1
  68. package/dist/multi/282.app-79c65c7bff941abcbc51.js +0 -1
  69. package/dist/multi/29.app-79c65c7bff941abcbc51.js +0 -1
  70. package/dist/multi/416.app-79c65c7bff941abcbc51.js +0 -1
  71. package/dist/multi/527.app-79c65c7bff941abcbc51.js +0 -1
  72. package/dist/multi/600.app-79c65c7bff941abcbc51.js +0 -1
  73. package/dist/multi/605.app-79c65c7bff941abcbc51.js +0 -1
  74. package/dist/multi/638.app-79c65c7bff941abcbc51.js +0 -1
  75. package/dist/multi/672.app-79c65c7bff941abcbc51.js +0 -1
  76. package/dist/multi/686.app-79c65c7bff941abcbc51.js +0 -1
  77. package/dist/multi/725.app-79c65c7bff941abcbc51.js +0 -1
  78. package/dist/multi/741.app-79c65c7bff941abcbc51.js +0 -1
  79. package/dist/multi/755.app-79c65c7bff941abcbc51.js +0 -1
  80. package/dist/multi/894.app-79c65c7bff941abcbc51.js +0 -1
  81. package/dist/multi/91.app-79c65c7bff941abcbc51.js +0 -1
  82. package/dist/multi/943.app-79c65c7bff941abcbc51.js +0 -1
  83. package/dist/multi/980.app-79c65c7bff941abcbc51.js +0 -1
  84. package/dist/multi/app-79c65c7bff941abcbc51.js +0 -2
  85. package/dist/single/app-3ca67f29d0f1166c08ca.js +0 -2
  86. /package/dist/multi/{app-79c65c7bff941abcbc51.js.LICENSE.txt → app-d0210ed2e64d38a2ee8e.js.LICENSE.txt} +0 -0
  87. /package/dist/single/{app-3ca67f29d0f1166c08ca.js.LICENSE.txt → app-01fed10ad5f9083fd39c.js.LICENSE.txt} +0 -0
@@ -7,12 +7,9 @@ import type { TimlineTr } from "@/stores/timeline";
7
7
  import { fetchTimelineData, timelineStore } from "@/stores/timeline";
8
8
  import * as styles from "./styles.scss";
9
9
 
10
- const getHosts = (tests: TimlineTr[]) => [
11
- ...new Set(tests.map((test) => test.labels.find((label) => label.name === "host")?.value).filter(Boolean)),
12
- ];
10
+ const getHosts = (tests: TimlineTr[]) => [...new Set(tests.map((test) => test.host))];
13
11
 
14
- const filterTestsByHost = (tests: TimlineTr[], host: string) =>
15
- tests.filter((test) => test.labels.find((label) => label.name === "host")?.value === host);
12
+ const filterTestsByHost = (tests: TimlineTr[], host: string) => tests.filter((test) => test.host === host);
16
13
 
17
14
  const currentTimelineData = computed(() => {
18
15
  const tests = timelineStore.value.data ?? [];
@@ -128,6 +128,7 @@
128
128
  "showLess": "Daha az göstər",
129
129
  "showMore": "Daha çox göstər",
130
130
  "copy": "Kopyala",
131
+ "copy-email": "E-poçtu kopyala",
131
132
  "attempt": "Cəhd {{attempt}} / {{total}}",
132
133
  "at": "üçün",
133
134
  "variables": "Dəyişənlər",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "FBSU yaş piramidası",
310
+ "statusAgePyramid": {
311
+ "title": "Statusa görə yaş piramidası",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "Weniger anzeigen",
129
129
  "showMore": "Mehr anzeigen",
130
130
  "copy": "Kopieren",
131
+ "copy-email": "E-Mail kopieren",
131
132
  "attempt": "Versuch {{attempt}} von {{total}}",
132
133
  "at": "bei",
133
134
  "variables": "Variablen",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "FBSU-Alterspyramide",
310
+ "statusAgePyramid": {
311
+ "title": "Alterspyramide nach Status",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "Show less",
129
129
  "showMore": "Show more",
130
130
  "copy": "Copy",
131
+ "copy-email": "Copy email",
131
132
  "attempt": "Attempt {{attempt}} of {{total}}",
132
133
  "at": "at",
133
134
  "variables": "Variables",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "FBSU age pyramid",
310
+ "statusAgePyramid": {
311
+ "title": "Status age pyramid",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "Mostrar menos",
129
129
  "showMore": "Mostrar más",
130
130
  "copy": "Copiar",
131
+ "copy-email": "Copiar correo",
131
132
  "attempt": "Intento {{attempt}} de {{total}}",
132
133
  "at": "a las",
133
134
  "variables": "Variables",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "Pirámide de edad FBSU",
310
+ "statusAgePyramid": {
311
+ "title": "Pirámide de edad por estado",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "Montrer moins",
129
129
  "showMore": "Montrer plus",
130
130
  "copy": "Copier",
131
+ "copy-email": "Copier l'e-mail",
131
132
  "attempt": "Tentative {{attempt}} sur {{total}}",
132
133
  "at": "à",
133
134
  "variables": "Variables",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "Pyramide des âges FBSU",
310
+ "statusAgePyramid": {
311
+ "title": "Pyramide des âges par statut",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "הצג פחות",
129
129
  "showMore": "הצג עוד",
130
130
  "copy": "העתק",
131
+ "copy-email": "העתק אימייל",
131
132
  "attempt": "ניסיון {{attempt}} מתוך {{total}}",
132
133
  "at": "ב-",
133
134
  "variables": "משתנים",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "פירמידת גילאים FBSU",
310
+ "statusAgePyramid": {
311
+ "title": "פירמידת גילאים לפי סטטוס",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "Ցուցադրել պակաս",
129
129
  "showMore": "Ցուցադրել ավելին",
130
130
  "copy": "Պատճենել",
131
+ "copy-email": "Պատճենել էլ. փոստը",
131
132
  "attempt": "Փորձ {{attempt}}-ից {{total}}-ը",
132
133
  "at": "է",
133
134
  "variables": "Փոփոխականներ",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "FBSU տdelays delaysdelays",
310
+ "statusAgePyramid": {
311
+ "title": "Կարգավիճակի տարիքային պիրամիդ",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "Mostra meno",
129
129
  "showMore": "Mostra di più",
130
130
  "copy": "Copia",
131
+ "copy-email": "Copia email",
131
132
  "attempt": "Tentativo {{attempt}} di {{total}}",
132
133
  "at": "a",
133
134
  "variables": "Variabili",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "Piramide delle età FBSU",
310
+ "statusAgePyramid": {
311
+ "title": "Piramide delle età per stato",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "表示を減らす",
129
129
  "showMore": "もっと見る",
130
130
  "copy": "コピー",
131
+ "copy-email": "メールをコピー",
131
132
  "attempt": "試行 {{attempt}} 回目(全 {{total}} 回)",
132
133
  "at": "時",
133
134
  "variables": "変数",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "FBSU年齢ピラミッド",
310
+ "statusAgePyramid": {
311
+ "title": "ステータス年齢ピラミッド",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "ნაკლების ჩვენება",
129
129
  "showMore": "მეტის ჩვენება",
130
130
  "copy": "კოპირება",
131
+ "copy-email": "ელფოსტის კოპირება",
131
132
  "attempt": "მცდელობა {{attempt}} {{total}}-დან",
132
133
  "at": "ზე",
133
134
  "variables": "ცვლადები",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "FBSU ასაკობრივი პირამიდა",
310
+ "statusAgePyramid": {
311
+ "title": "ასაკობრივი პირამიდა სტატუსის მიხედვით",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "덜 보기",
129
129
  "showMore": "더 보기",
130
130
  "copy": "복사",
131
+ "copy-email": "이메일 복사",
131
132
  "attempt": "시도 {{attempt}}/{{total}}",
132
133
  "at": "에서",
133
134
  "variables": "변수",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "FBSU 연령 피라미드",
310
+ "statusAgePyramid": {
311
+ "title": "상태 연령 피라미드",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "Minder weergeven",
129
129
  "showMore": "Meer weergeven",
130
130
  "copy": "Kopiëren",
131
+ "copy-email": "E-mail kopiëren",
131
132
  "attempt": "Poging {{attempt}} van {{total}}",
132
133
  "at": "om",
133
134
  "variables": "Variabelen",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "FBSU leeftijdspiramide",
310
+ "statusAgePyramid": {
311
+ "title": "Leeftijdspiramide per status",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "Pokaż mniej",
129
129
  "showMore": "Pokaż więcej",
130
130
  "copy": "Skopuj",
131
+ "copy-email": "Kopiuj e-mail",
131
132
  "attempt": "Próba {{attempt}} z {{total}}",
132
133
  "at": "w",
133
134
  "variables": "Zmienne",
@@ -314,8 +315,8 @@
314
315
  "history": "{{timestamp, timestamp_date}}"
315
316
  }
316
317
  },
317
- "fbsuAgePyramid": {
318
- "title": "Piramida wiekowa FBSU",
318
+ "statusAgePyramid": {
319
+ "title": "Piramida wieku według statusu",
319
320
  "status": {
320
321
  "passed": "$t(statuses:passed, capitalize)",
321
322
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "Mostrar menos",
129
129
  "showMore": "Mostrar mais",
130
130
  "copy": "Copiar",
131
+ "copy-email": "Copiar e-mail",
131
132
  "attempt": "Tentativa {{attempt}} de {{total}}",
132
133
  "at": "em",
133
134
  "variables": "Variáveis",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "Pirâmide etária FBSU",
310
+ "statusAgePyramid": {
311
+ "title": "Pirâmide etária por status",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "Показать меньше",
129
129
  "showMore": "Показать больше",
130
130
  "copy": "Скопировать",
131
+ "copy-email": "Скопировать email",
131
132
  "attempt": "Попытка {{attempt}} из {{total}}",
132
133
  "at": "в",
133
134
  "variables": "Переменные",
@@ -314,8 +315,8 @@
314
315
  "history": "{{timestamp, timestamp_date}}"
315
316
  }
316
317
  },
317
- "fbsuAgePyramid": {
318
- "title": "FBSU пирамида возраста",
318
+ "statusAgePyramid": {
319
+ "title": "Пирамида возраста по статусу",
319
320
  "status": {
320
321
  "passed": "$t(statuses:passed, capitalize)",
321
322
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "Visa mindre",
129
129
  "showMore": "Visa mer",
130
130
  "copy": "Kopiera",
131
+ "copy-email": "Kopiera e-post",
131
132
  "attempt": "Försök {{attempt}} av {{total}}",
132
133
  "at": "vid",
133
134
  "variables": "Variabler",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "FBSU ålderspyramid",
310
+ "statusAgePyramid": {
311
+ "title": "Ålderspyramid per status",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "Daha az göster",
129
129
  "showMore": "Daha fazla göster",
130
130
  "copy": "Kopyala",
131
+ "copy-email": "E-postayı kopyala",
131
132
  "attempt": "Deneme {{attempt}} / {{total}}",
132
133
  "at": "tarihinde",
133
134
  "variables": "Değişkenler",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "FBSU yaş piramidi",
310
+ "statusAgePyramid": {
311
+ "title": "Durum yaş piramidi",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "Показати менше",
129
129
  "showMore": "Показати більше",
130
130
  "copy": "Скопіювати",
131
+ "copy-email": "Скопіювати email",
131
132
  "attempt": "Спроба {{attempt}} з {{total}}",
132
133
  "at": "в",
133
134
  "variables": "Змінні",
@@ -257,8 +258,8 @@
257
258
  "history": "{{timestamp, timestamp_date}}"
258
259
  }
259
260
  },
260
- "fbsuAgePyramid": {
261
- "title": "FBSU вікова піраміда",
261
+ "statusAgePyramid": {
262
+ "title": "Вікова піраміда за статусом",
262
263
  "status": {
263
264
  "passed": "$t(statuses:passed, capitalize)",
264
265
  "failed": "$t(statuses:failed, capitalize)",
@@ -128,6 +128,7 @@
128
128
  "showLess": "显示更少",
129
129
  "showMore": "显示更多",
130
130
  "copy": "复制",
131
+ "copy-email": "复制邮箱",
131
132
  "attempt": "第 {{attempt}} 次尝试,共 {{total}} 次",
132
133
  "at": "在",
133
134
  "variables": "变量",
@@ -306,8 +307,8 @@
306
307
  "history": "{{timestamp, timestamp_date}}"
307
308
  }
308
309
  },
309
- "fbsuAgePyramid": {
310
- "title": "FBSU年龄金字塔",
310
+ "statusAgePyramid": {
311
+ "title": "状态年龄金字塔",
311
312
  "status": {
312
313
  "passed": "$t(statuses:passed, capitalize)",
313
314
  "failed": "$t(statuses:failed, capitalize)",
@@ -1,5 +1,11 @@
1
1
  import { formatDuration } from "@allurereport/core-api";
2
- import { DEFAULT_LOCALE, LANG_LOCALE, type LangLocale, getReportOptions } from "@allurereport/web-commons";
2
+ import {
3
+ DEFAULT_LOCALE,
4
+ LANG_LOCALE,
5
+ type LangLocale,
6
+ getLocaleDateTimeOverride,
7
+ getReportOptions,
8
+ } from "@allurereport/web-commons";
3
9
  import { computed, signal } from "@preact/signals";
4
10
  import i18next, { type TOptions } from "i18next";
5
11
  import type { AwesomeReportOptions } from "types";
@@ -50,7 +56,8 @@ export const waitForI18next = i18next
50
56
  namespace: string,
51
57
  callback: (errorValue: unknown, translations: null) => void,
52
58
  ) => {
53
- await import(`@/locales/${language}.json`)
59
+ const loadLocale = language === "en-iso" ? "en" : language;
60
+ await import(`@/locales/${loadLocale}.json`)
54
61
  .then((resources: Record<string, null>) => {
55
62
  callback(null, resources[namespace]);
56
63
  })
@@ -66,43 +73,68 @@ export const waitForI18next = i18next
66
73
  interpolation: { escapeValue: false },
67
74
  })
68
75
  .then(() => {
69
- i18next.services.formatter.add("capitalize", (value) => {
76
+ i18next.services.formatter.add("capitalize", (value: string) => {
70
77
  return value.charAt(0).toLocaleUpperCase() + value.slice(1);
71
78
  });
72
- i18next.services.formatter.add("timestamp_date", (value: number, lng, options) => {
73
- const formatter = new Intl.DateTimeFormat(lng, {
74
- ...options,
75
- month: "numeric",
76
- day: "numeric",
77
- year: "numeric",
78
- });
79
- return formatter.format(value);
80
- });
81
- i18next.services.formatter.add("timestamp_long", (value: number, lng, options) => {
82
- const formatter = new Intl.DateTimeFormat(lng, {
83
- ...options,
84
- month: "numeric",
85
- day: "numeric",
86
- year: "numeric",
87
- hour: "numeric",
88
- minute: "numeric",
89
- second: "numeric",
90
- hour12: false,
91
- });
92
- return formatter.format(value).replace(",", ` ${i18next.t("ui:at")}`);
93
- });
94
- i18next.services.formatter.add("timestamp_long_no_seconds", (value: number, lng, options) => {
95
- const formatter = new Intl.DateTimeFormat(lng, {
96
- ...options,
97
- month: "numeric",
98
- day: "numeric",
99
- year: "numeric",
100
- hour: "numeric",
101
- minute: "numeric",
102
- hour12: false,
103
- });
104
- return formatter.format(value).replace(",", ` ${i18next.t("ui:at")}`);
105
- });
79
+ i18next.services.formatter.add(
80
+ "timestamp_date",
81
+ (value: number, lng: string, options?: Intl.DateTimeFormatOptions) => {
82
+ const override = getLocaleDateTimeOverride(lng, "date");
83
+ const formatter = new Intl.DateTimeFormat(override?.locale ?? lng, {
84
+ ...options,
85
+ month: "numeric",
86
+ day: "numeric",
87
+ year: "numeric",
88
+ ...(override?.options ?? {}),
89
+ });
90
+ const formatted = formatter.format(value);
91
+ return override?.stripComma ? formatted.replace(",", "") : formatted;
92
+ },
93
+ );
94
+
95
+ i18next.services.formatter.add(
96
+ "timestamp_long",
97
+ (value: number, lng: string, options?: Intl.DateTimeFormatOptions) => {
98
+ const override = getLocaleDateTimeOverride(lng, "dateTime");
99
+ const formatter = new Intl.DateTimeFormat(override?.locale ?? lng, {
100
+ ...options,
101
+ month: "numeric",
102
+ day: "numeric",
103
+ year: "numeric",
104
+ hour: "numeric",
105
+ minute: "numeric",
106
+ second: "numeric",
107
+ hour12: false,
108
+ ...(override?.options ?? {}),
109
+ });
110
+ const formatted = formatter.format(value);
111
+ if (override?.includeAtSeparator === false || override?.stripComma) {
112
+ return formatted.replace(",", "");
113
+ }
114
+ return formatted.replace(",", ` ${i18next.t("ui:at")}`);
115
+ },
116
+ );
117
+ i18next.services.formatter.add(
118
+ "timestamp_long_no_seconds",
119
+ (value: number, lng: string, options?: Intl.DateTimeFormatOptions) => {
120
+ const override = getLocaleDateTimeOverride(lng, "dateTimeNoSeconds");
121
+ const formatter = new Intl.DateTimeFormat(override?.locale ?? lng, {
122
+ ...options,
123
+ month: "numeric",
124
+ day: "numeric",
125
+ year: "numeric",
126
+ hour: "numeric",
127
+ minute: "numeric",
128
+ hour12: false,
129
+ ...(override?.options ?? {}),
130
+ });
131
+ const formatted = formatter.format(value);
132
+ if (override?.includeAtSeparator === false || override?.stripComma) {
133
+ return formatted.replace(",", "");
134
+ }
135
+ return formatted.replace(",", ` ${i18next.t("ui:at")}`);
136
+ },
137
+ );
106
138
  i18next.services.formatter.add("format_duration", (value: number) => {
107
139
  return formatDuration(value);
108
140
  });
@@ -3,7 +3,7 @@ import { fetchReportJsonData } from "@allurereport/web-commons";
3
3
  import { signal } from "@preact/signals";
4
4
  import { type StoreSignalState } from "./types";
5
5
 
6
- export const qualityGateStore = signal<StoreSignalState<QualityGateValidationResult[]>>({
6
+ export const qualityGateStore = signal<StoreSignalState<Record<string, QualityGateValidationResult[]>>>({
7
7
  loading: true,
8
8
  error: undefined,
9
9
  data: undefined,
@@ -11,7 +11,7 @@ export const qualityGateStore = signal<StoreSignalState<QualityGateValidationRes
11
11
 
12
12
  export const fetchQualityGateResults = async () => {
13
13
  try {
14
- const data = await fetchReportJsonData<QualityGateValidationResult[]>("widgets/quality-gate.json");
14
+ const data = await fetchReportJsonData<Record<string, QualityGateValidationResult[]>>("widgets/quality-gate.json");
15
15
 
16
16
  qualityGateStore.value = {
17
17
  data,
@@ -5,8 +5,11 @@ import type { StoreSignalState } from "@/stores/types";
5
5
 
6
6
  export type TimlineTr = Pick<
7
7
  TestResult,
8
- "id" | "name" | "status" | "flaky" | "hidden" | "labels" | "environment" | "start" | "stop" | "duration"
9
- >;
8
+ "id" | "name" | "status" | "flaky" | "hidden" | "environment" | "start" | "duration"
9
+ > & {
10
+ host: string;
11
+ thread: string;
12
+ };
10
13
 
11
14
  export const timelineStore = signal<StoreSignalState<TimlineTr[]>>({
12
15
  loading: true,
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Parses owner string per RFC 2822 (Internet Message Format).
3
+ * Supports: "Display Name <email@example.com>", "Display Name <https://url>",
4
+ * plain "email@example.com", plain "https://url", or plain text.
5
+ */
6
+ const RFC2822_ADDRESS = /^([^<>]+)\s+<\s*(\S*)\s*>$/;
7
+
8
+ type UrlOwnerAddress = {
9
+ displayName: string;
10
+ url: string;
11
+ type: "url";
12
+ };
13
+
14
+ type EmailOwnerAddress = {
15
+ displayName: string;
16
+ email: string;
17
+ type: "email";
18
+ };
19
+
20
+ export type OwnerAddress =
21
+ | UrlOwnerAddress
22
+ | EmailOwnerAddress
23
+ | {
24
+ displayName: string;
25
+ type: "none";
26
+ };
27
+
28
+ const isValidUrl = (value: string): boolean => {
29
+ try {
30
+ const u = new URL(value);
31
+ return u.protocol === "http:" || u.protocol === "https:";
32
+ } catch {
33
+ return false;
34
+ }
35
+ };
36
+
37
+ /**
38
+ * Simple email validation: local@domain.tld, domain part must not start with dot.
39
+ * Rejects invalid forms like "user@.example.com".
40
+ */
41
+ const isValidEmail = (value: string): boolean => {
42
+ const simpleEmail = /^[^\s@]+@[^\s.@][^\s@]*\.[^\s@]+$/;
43
+ return simpleEmail.test(value);
44
+ };
45
+
46
+ /**
47
+ * Quick check to avoid regex on plain text (no @, :, or <).
48
+ */
49
+ const isLikelyAddress = (input: string): boolean => {
50
+ return input.includes("@") || input.includes(":") || input.includes("<");
51
+ };
52
+
53
+ /**
54
+ * Parses a single owner value into display name and optional link (mailto or URL).
55
+ * Returns null for null/empty input.
56
+ */
57
+ export const parseOwnerAddress = (maybeAddress: string): OwnerAddress => {
58
+ if (!maybeAddress || maybeAddress.trim() === "") {
59
+ return { displayName: "", type: "none" };
60
+ }
61
+
62
+ if (!isLikelyAddress(maybeAddress)) {
63
+ return { displayName: maybeAddress, type: "none" };
64
+ }
65
+
66
+ let displayName = maybeAddress;
67
+ let urlOrEmail = maybeAddress;
68
+
69
+ const match = maybeAddress.match(RFC2822_ADDRESS);
70
+ if (match) {
71
+ displayName = match[1].trim();
72
+ urlOrEmail = match[2];
73
+ }
74
+
75
+ // e.g.: John Doe <>
76
+ if (urlOrEmail === "") {
77
+ return { displayName, type: "none" };
78
+ }
79
+
80
+ // e.g.: John Doe <https://example.com> or plain https://...
81
+ if (isValidUrl(urlOrEmail)) {
82
+ return { displayName, url: urlOrEmail, type: "url" };
83
+ }
84
+
85
+ // e.g.: John Doe <mail@example.com> or plain mail@...
86
+ if (isValidEmail(urlOrEmail)) {
87
+ return { displayName, email: urlOrEmail, type: "email" };
88
+ }
89
+
90
+ // Non-compliant: treat as plain text
91
+ return { displayName: maybeAddress, type: "none" };
92
+ };