@allurereport/web-awesome 3.0.0-beta.4 → 3.0.0-beta.6

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 (115) hide show
  1. package/.eslintrc.cjs +1 -1
  2. package/CONTRIBUTING.md +34 -0
  3. package/dist/multi/141.app-07a6d2ea.js +1 -0
  4. package/dist/multi/222.app-07a6d2ea.js +1 -0
  5. package/dist/multi/335.app-07a6d2ea.js +1 -0
  6. package/dist/multi/34.app-07a6d2ea.js +1 -0
  7. package/dist/multi/349.app-07a6d2ea.js +1 -0
  8. package/dist/multi/378.app-07a6d2ea.js +1 -0
  9. package/dist/multi/406.app-07a6d2ea.js +1 -0
  10. package/dist/multi/476.app-07a6d2ea.js +1 -0
  11. package/dist/multi/53.app-07a6d2ea.js +1 -0
  12. package/dist/multi/584.app-07a6d2ea.js +1 -0
  13. package/dist/multi/690.app-07a6d2ea.js +1 -0
  14. package/dist/multi/747.app-07a6d2ea.js +1 -0
  15. package/dist/multi/767.app-07a6d2ea.js +1 -0
  16. package/dist/multi/{816.app-13f840d5.js → 816.app-07a6d2ea.js} +1 -1
  17. package/dist/multi/83.app-07a6d2ea.js +1 -0
  18. package/dist/multi/873.app-07a6d2ea.js +1 -0
  19. package/dist/multi/920.app-07a6d2ea.js +1 -0
  20. package/dist/multi/991.app-07a6d2ea.js +1 -0
  21. package/dist/multi/app-07a6d2ea.js +2 -0
  22. package/dist/multi/manifest.json +20 -20
  23. package/dist/multi/{styles-13f840d5.css → styles-07a6d2ea.css} +7 -7
  24. package/dist/single/app-c2627b69.js +2 -0
  25. package/dist/single/manifest.json +1 -1
  26. package/package.json +11 -4
  27. package/src/assets/svg/line-alerts-alert-circle.svg +12 -0
  28. package/src/assets/svg/line-general-eye.svg +7 -0
  29. package/src/assets/svg/line-icon-bomb-2.svg +12 -0
  30. package/src/components/app/ArrowButton/index.tsx +3 -2
  31. package/src/components/app/ArrowButton/styles.scss +3 -0
  32. package/src/components/app/BaseLayout/index.tsx +5 -5
  33. package/src/components/app/ReportBody/Filters.tsx +12 -10
  34. package/src/components/app/ReportBody/HeaderActions.tsx +3 -3
  35. package/src/components/app/ReportBody/SortBy.tsx +8 -8
  36. package/src/components/app/ReportBody/context.tsx +0 -1
  37. package/src/components/app/ReportMetadata/MetadataSummary.tsx +22 -18
  38. package/src/components/app/ReportMetadata/MetadataWithIcon.tsx +2 -2
  39. package/src/components/app/Tabs/index.tsx +2 -3
  40. package/src/components/app/TestResult/TestResultDescription/index.tsx +3 -3
  41. package/src/components/app/TestResult/TestResultError/index.tsx +18 -16
  42. package/src/components/app/TestResult/TestResultError/styles.scss +16 -5
  43. package/src/components/app/TestResult/TestResultHeader/index.tsx +2 -2
  44. package/src/components/app/TestResult/TestResultHistory/TestResultHistoryItem.tsx +21 -10
  45. package/src/components/app/TestResult/TestResultInfo/TestResultInfoStatuses.tsx +31 -0
  46. package/src/components/app/TestResult/TestResultInfo/index.tsx +6 -4
  47. package/src/components/app/TestResult/TestResultInfo/styles.scss +17 -0
  48. package/src/components/app/TestResult/TestResultNavigation/index.tsx +34 -38
  49. package/src/components/app/TestResult/TestResultNavigation/styles.scss +1 -1
  50. package/src/components/app/TestResult/TestResultPrevStatuses/index.tsx +6 -6
  51. package/src/components/app/TestResult/TestResultSteps/attachment.tsx +4 -6
  52. package/src/components/app/TestResult/TestResultTeardown/index.tsx +2 -2
  53. package/src/components/app/TestResult/index.tsx +2 -2
  54. package/src/components/app/Tree/Tree.tsx +54 -101
  55. package/src/components/app/Tree/TreeHeader.tsx +13 -12
  56. package/src/components/app/Tree/TreeItem.tsx +5 -1
  57. package/src/components/app/Tree/index.tsx +31 -7
  58. package/src/components/app/Tree/styles.scss +16 -4
  59. package/src/components/commons/Button/styles.scss +6 -4
  60. package/src/components/commons/Menu/index.tsx +4 -4
  61. package/src/components/commons/SearchBox/index.tsx +8 -6
  62. package/src/components/commons/Toggle/index.tsx +2 -1
  63. package/src/components/commons/Tooltip/index.tsx +3 -3
  64. package/src/i18n/constants.ts +23 -4
  65. package/src/i18n/locales/am.json +5 -2
  66. package/src/i18n/locales/az.json +5 -2
  67. package/src/i18n/locales/de.json +5 -2
  68. package/src/i18n/locales/en.json +6 -3
  69. package/src/i18n/locales/es.json +5 -1
  70. package/src/i18n/locales/fr.json +5 -2
  71. package/src/i18n/locales/he.json +5 -2
  72. package/src/i18n/locales/it.json +5 -2
  73. package/src/i18n/locales/ja.json +5 -2
  74. package/src/i18n/locales/ka.json +5 -2
  75. package/src/i18n/locales/kr.json +5 -2
  76. package/src/i18n/locales/nl.json +5 -2
  77. package/src/i18n/locales/pl.json +5 -2
  78. package/src/i18n/locales/pt.json +5 -2
  79. package/src/i18n/locales/ru.json +5 -2
  80. package/src/i18n/locales/sv.json +5 -2
  81. package/src/i18n/locales/tr.json +5 -2
  82. package/src/i18n/locales/zh.json +6 -3
  83. package/src/index.html +1 -0
  84. package/src/index.tsx +4 -0
  85. package/src/stores/chart.ts +2 -2
  86. package/src/stores/stats.ts +1 -1
  87. package/src/stores/testResults.ts +26 -4
  88. package/src/stores/tree.ts +98 -4
  89. package/src/types/globals.d.ts +6 -1
  90. package/src/utils/copyToClipboard.ts +14 -1
  91. package/src/utils/treeFilters.ts +73 -120
  92. package/test/utils/treeFilters.test.ts +424 -0
  93. package/types.d.ts +25 -4
  94. package/vitest.config.ts +12 -0
  95. package/dist/multi/141.app-13f840d5.js +0 -1
  96. package/dist/multi/222.app-13f840d5.js +0 -1
  97. package/dist/multi/335.app-13f840d5.js +0 -1
  98. package/dist/multi/34.app-13f840d5.js +0 -1
  99. package/dist/multi/349.app-13f840d5.js +0 -1
  100. package/dist/multi/378.app-13f840d5.js +0 -1
  101. package/dist/multi/406.app-13f840d5.js +0 -1
  102. package/dist/multi/476.app-13f840d5.js +0 -1
  103. package/dist/multi/53.app-13f840d5.js +0 -1
  104. package/dist/multi/584.app-13f840d5.js +0 -1
  105. package/dist/multi/690.app-13f840d5.js +0 -1
  106. package/dist/multi/747.app-13f840d5.js +0 -1
  107. package/dist/multi/767.app-13f840d5.js +0 -1
  108. package/dist/multi/83.app-13f840d5.js +0 -1
  109. package/dist/multi/873.app-13f840d5.js +0 -1
  110. package/dist/multi/920.app-13f840d5.js +0 -1
  111. package/dist/multi/991.app-13f840d5.js +0 -1
  112. package/dist/multi/app-13f840d5.js +0 -2
  113. package/dist/single/app-d31bd53e.js +0 -2
  114. /package/dist/multi/{app-13f840d5.js.LICENSE.txt → app-07a6d2ea.js.LICENSE.txt} +0 -0
  115. /package/dist/single/{app-d31bd53e.js.LICENSE.txt → app-c2627b69.js.LICENSE.txt} +0 -0
@@ -1,3 +1,3 @@
1
1
  {
2
- "main.js": "app-d31bd53e.js"
2
+ "main.js": "app-c2627b69.js"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/web-awesome",
3
- "version": "3.0.0-beta.4",
3
+ "version": "3.0.0-beta.6",
4
4
  "description": "The static files for Allure Awesome Report",
5
5
  "keywords": [
6
6
  "allure",
@@ -22,7 +22,8 @@
22
22
  "build:prod:multi": "webpack --mode production",
23
23
  "build:dev:single": "SINGLE_FILE_MODE=1 webpack --mode development",
24
24
  "build:dev:multi": "webpack --mode development",
25
- "lint": "eslint --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
25
+ "lint": "eslint --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
26
+ "test": "vitest run"
26
27
  },
27
28
  "browserslist": [
28
29
  "last 1 version",
@@ -30,8 +31,8 @@
30
31
  "IE 11"
31
32
  ],
32
33
  "dependencies": {
33
- "@allurereport/core-api": "3.0.0-beta.4",
34
- "@allurereport/web-commons": "3.0.0-beta.4",
34
+ "@allurereport/core-api": "3.0.0-beta.6",
35
+ "@allurereport/web-commons": "3.0.0-beta.6",
35
36
  "@preact/signals": "^1.3.0",
36
37
  "clsx": "^2.1.1",
37
38
  "d3-shape": "^3.2.0",
@@ -57,6 +58,10 @@
57
58
  "@types/prismjs": "^1",
58
59
  "@typescript-eslint/eslint-plugin": "^8.0.0",
59
60
  "@typescript-eslint/parser": "^8.0.0",
61
+ "@vitest/runner": "^2.1.8",
62
+ "@vitest/snapshot": "^2.1.8",
63
+ "allure-vitest": "^3.0.7",
64
+ "autoprefixer": "^10.4.20",
60
65
  "babel-loader": "^9.2.1",
61
66
  "babel-plugin-prismjs": "^2.1.0",
62
67
  "css-loader": "^7.1.2",
@@ -78,6 +83,7 @@
78
83
  "html-webpack-plugin": "^5.6.3",
79
84
  "mini-css-extract-plugin": "^2.9.1",
80
85
  "npm-run-all2": "^7.0.1",
86
+ "postcss": "^8.4.49",
81
87
  "rimraf": "^6.0.1",
82
88
  "sass": "^1.79.1",
83
89
  "sass-loader": "^16.0.1",
@@ -85,6 +91,7 @@
85
91
  "svg-sprite-loader": "^6.0.11",
86
92
  "typescript": "^5.6.3",
87
93
  "typescript-eslint": "^8.6.0",
94
+ "vitest": "^2.1.8",
88
95
  "webpack": "^5.94.0",
89
96
  "webpack-cli": "^5.1.4",
90
97
  "webpack-dev-server": "^5.1.0",
@@ -0,0 +1,12 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none">
2
+ <g clip-path="url(#a)">
3
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".56"
4
+ stroke-width="1.5"
5
+ d="M8 5.333V8m0 2.666h.006M14.666 8A6.667 6.667 0 1 1 1.333 8a6.667 6.667 0 0 1 13.333 0Z"/>
6
+ </g>
7
+ <defs>
8
+ <clipPath id="a">
9
+ <path fill="currentColor" d="M0 0h16v16H0z"/>
10
+ </clipPath>
11
+ </defs>
12
+ </svg>
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none">
2
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".56"
3
+ stroke-width="1.5"
4
+ d="M1.613 8.475c-.09-.144-.136-.216-.161-.326a.782.782 0 0 1 0-.298c.025-.111.07-.183.161-.327C2.363 6.336 4.597 3.333 8 3.333c3.404 0 5.637 3.003 6.387 4.191.09.144.136.216.162.327.019.083.019.214 0 .298-.026.11-.071.182-.162.326-.75 1.188-2.983 4.191-6.387 4.191-3.403 0-5.636-3.003-6.387-4.19Z"/>
5
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".56"
6
+ stroke-width="1.5" d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z"/>
7
+ </svg>
@@ -0,0 +1,12 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none">
2
+ <g clip-path="url(#a)">
3
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".56"
4
+ stroke-width="1.5"
5
+ d="m9.566 3.1 1.3-1.3a1.607 1.607 0 0 1 2.267 0L14.2 2.867a1.6 1.6 0 0 1 0 2.266l-1.3 1.3m1.766-5.1-1 1m-.333 6.334a6 6 0 1 1-12 0 6 6 0 0 1 12 0Z"/>
6
+ </g>
7
+ <defs>
8
+ <clipPath id="a">
9
+ <path fill="currentColor" d="M0 0h16v16H0z"/>
10
+ </clipPath>
11
+ </defs>
12
+ </svg>
@@ -1,5 +1,5 @@
1
1
  import { clsx } from "clsx";
2
- import { FunctionalComponent } from "preact";
2
+ import type { FunctionalComponent } from "preact";
3
3
  import lineChevronDown from "@/assets/svg/line-arrows-chevron-down.svg";
4
4
  import { SvgIcon } from "@/components/commons/SvgIcon";
5
5
  import * as styles from "./styles.scss";
@@ -18,9 +18,10 @@ export const ArrowButton: FunctionalComponent<ArrowButtonProps> = ({
18
18
  iconSize = "xs",
19
19
  className,
20
20
  icon,
21
+ ...rest
21
22
  }) => {
22
23
  return (
23
- <button className={clsx(styles["arrow-button"], styles[`arrow-button-${buttonSize}`])}>
24
+ <button className={clsx(styles["arrow-button"], styles[`arrow-button-${buttonSize}`])} {...rest}>
24
25
  <SvgIcon
25
26
  id={icon || lineChevronDown.id}
26
27
  size={iconSize}
@@ -2,10 +2,13 @@
2
2
  background: transparent;
3
3
  border: none;
4
4
  padding: 8px 4px;
5
+ border-radius: 4px;
6
+ cursor: pointer;
5
7
  color: var(--on-icon-secondary);
6
8
 
7
9
  &:hover {
8
10
  background: var(--bg-control-flat-medium);
11
+ color: var(--on-icon-primary);
9
12
  }
10
13
  }
11
14
 
@@ -1,4 +1,4 @@
1
- import { ensureReportDataReady } from "@allurereport/web-commons";
1
+ import { ensureReportDataReady, getReportOptions } from "@allurereport/web-commons";
2
2
  import { useEffect } from "preact/compat";
3
3
  import { Footer } from "@/components/app/Footer";
4
4
  import MainReport from "@/components/app/MainReport";
@@ -6,11 +6,10 @@ import Modal from "@/components/app/Modal";
6
6
  import TestResult from "@/components/app/TestResult";
7
7
  import { Loadable } from "@/components/commons/Loadable";
8
8
  import { PageLoader } from "@/components/commons/PageLoader";
9
- import { fetchStats, getTheme } from "@/stores";
10
- import { getLocale } from "@/stores";
9
+ import { fetchStats, getLocale, getTheme } from "@/stores";
11
10
  import { fetchPieChartData } from "@/stores/chart";
12
11
  import { fetchEnvInfo } from "@/stores/envInfo";
13
- import { fetchTestResult, testResultStore } from "@/stores/testResults";
12
+ import { fetchTestResult, fetchTestResultNav, testResultStore } from "@/stores/testResults";
14
13
  import { fetchTreeData, treeStore } from "@/stores/tree";
15
14
  import * as styles from "./styles.scss";
16
15
 
@@ -23,12 +22,13 @@ export const BaseLayout = ({ testResultId }) => {
23
22
  useEffect(() => {
24
23
  if (testResultId) {
25
24
  fetchTestResult(testResultId);
25
+ fetchTestResultNav();
26
26
  } else {
27
27
  Promise.all([
28
28
  ensureReportDataReady(),
29
29
  fetchStats(),
30
30
  fetchPieChartData(),
31
- fetchTreeData("suites"),
31
+ fetchTreeData(),
32
32
  fetchEnvInfo(),
33
33
  ]);
34
34
  }
@@ -2,18 +2,16 @@ import notificationBoxIcon from "@/assets/svg/line-alerts-notification-box.svg";
2
2
  import refreshIcon from "@/assets/svg/line-arrows-refresh-ccw-1.svg";
3
3
  import settingsIcon from "@/assets/svg/line-general-settings-1.svg";
4
4
  import zapIcon from "@/assets/svg/line-general-zap.svg";
5
- import { useReportContentContext } from "@/components/app/ReportBody/context";
6
5
  import { Button } from "@/components/commons/Button";
7
6
  import { Menu } from "@/components/commons/Menu";
8
7
  import { Toggle } from "@/components/commons/Toggle";
9
8
  import { useI18n } from "@/stores/locale";
9
+ import { setTreeFilter, treeFiltersStore } from "@/stores/tree";
10
10
  import * as styles from "./styles.scss";
11
11
 
12
12
  export const Filters = () => {
13
13
  const { t } = useI18n("filters");
14
- const { filter, setFilter } = useReportContentContext();
15
-
16
- const { flaky, retry, new: isNew } = filter;
14
+ const { flaky, retry, new: isNew } = treeFiltersStore.value.filter;
17
15
  const hasFilter = flaky || retry || isNew;
18
16
 
19
17
  return (
@@ -26,6 +24,7 @@ export const Filters = () => {
26
24
  size="m"
27
25
  style="outline"
28
26
  isActive={isOpened}
27
+ data-testid="filters-button"
29
28
  onClick={onClick}
30
29
  />
31
30
  </div>
@@ -36,7 +35,7 @@ export const Filters = () => {
36
35
  closeMenuOnClick={false}
37
36
  ariaLabel={t("enable-filter", { filter: t("flaky") })}
38
37
  onClick={() => {
39
- setFilter("flaky", !flaky);
38
+ setTreeFilter("flaky", !flaky);
40
39
  }}
41
40
  leadingIcon={zapIcon.id}
42
41
  rightSlot={
@@ -45,7 +44,8 @@ export const Filters = () => {
45
44
  focusable={false}
46
45
  value={flaky}
47
46
  label={t("enable-filter", { filter: t("flaky") })}
48
- onChange={(value) => setFilter("flaky", value)}
47
+ data-testid="flaky-filter"
48
+ onChange={(value) => setTreeFilter("flaky", value)}
49
49
  />
50
50
  </div>
51
51
  }
@@ -55,7 +55,7 @@ export const Filters = () => {
55
55
  <Menu.Item
56
56
  closeMenuOnClick={false}
57
57
  ariaLabel={t("enable-filter", { filter: t("retry") })}
58
- onClick={() => setFilter("retry", !retry)}
58
+ onClick={() => setTreeFilter("retry", !retry)}
59
59
  leadingIcon={refreshIcon.id}
60
60
  rightSlot={
61
61
  <div className={styles.filterToggle}>
@@ -63,7 +63,8 @@ export const Filters = () => {
63
63
  focusable={false}
64
64
  value={retry}
65
65
  label={t("enable-filter", { filter: t("retry") })}
66
- onChange={(value) => setFilter("retry", value)}
66
+ data-testid="retry-filter"
67
+ onChange={(value) => setTreeFilter("retry", value)}
67
68
  />
68
69
  </div>
69
70
  }
@@ -73,7 +74,7 @@ export const Filters = () => {
73
74
  <Menu.Item
74
75
  closeMenuOnClick={false}
75
76
  ariaLabel={t("enable-filter", { filter: t("new") })}
76
- onClick={() => setFilter("new", !isNew)}
77
+ onClick={() => setTreeFilter("new", !isNew)}
77
78
  leadingIcon={notificationBoxIcon.id}
78
79
  rightSlot={
79
80
  <div className={styles.filterToggle}>
@@ -81,7 +82,8 @@ export const Filters = () => {
81
82
  focusable={false}
82
83
  value={isNew}
83
84
  label={t("enable-filter", { filter: t("new") })}
84
- onChange={(value) => setFilter("new", value)}
85
+ data-testid="new-filter"
86
+ onChange={(value) => setTreeFilter("new", value)}
85
87
  />
86
88
  </div>
87
89
  }
@@ -1,14 +1,14 @@
1
1
  import { useI18n } from "@/stores/locale";
2
+ import { setTreeQuery, treeFiltersStore } from "@/stores/tree";
2
3
  import { SearchBox } from "../../commons/SearchBox";
3
4
  import { Filters } from "./Filters";
4
- import { useReportContentContext } from "./context";
5
5
  import * as styles from "./styles.scss";
6
6
 
7
7
  const Search = () => {
8
- const { setQuery, query } = useReportContentContext();
8
+ const { query } = treeFiltersStore.value;
9
9
  const { t } = useI18n("search");
10
10
 
11
- return <SearchBox placeholder={t("search-placeholder")} value={query} onChange={setQuery} />;
11
+ return <SearchBox placeholder={t("search-placeholder")} value={query} onChange={setTreeQuery} />;
12
12
  };
13
13
 
14
14
  export const HeaderActions = () => {
@@ -5,12 +5,12 @@ import sortAscIcon from "@/assets/svg/line-arrows-sort-line-asc.svg";
5
5
  import sortDescIcon from "@/assets/svg/line-arrows-sort-line-desc.svg";
6
6
  import switchVerticalIcon from "@/assets/svg/line-arrows-switch-vertical-1.svg";
7
7
  import { useI18n } from "@/stores/locale";
8
+ import { setTreeDirection, setTreeSortBy, treeFiltersStore } from "@/stores/tree";
8
9
  import { DropdownButton } from "../../commons/Button";
9
10
  import { Link } from "../../commons/Link";
10
11
  import { Menu } from "../../commons/Menu";
11
12
  import { SvgIcon } from "../../commons/SvgIcon";
12
13
  import { Text } from "../../commons/Typography";
13
- import { useReportContentContext } from "./context";
14
14
  import * as styles from "./styles.scss";
15
15
 
16
16
  const BtnWrapper = ({ children }: { children: ComponentChildren }) => {
@@ -21,7 +21,7 @@ export const SortBy = () => {
21
21
  const { t: sortByLocale } = useI18n("sort-by");
22
22
  const { t: sortByValuesLocale } = useI18n("sort-by.values");
23
23
  const { t: sortByDirectionsLocale } = useI18n("sort-by.directions");
24
- const { direction, sortBy, setSortBy, setDirection } = useReportContentContext();
24
+ const { sortBy, direction } = treeFiltersStore.value;
25
25
 
26
26
  const displayedSortByValue = sortByValuesLocale(sortBy);
27
27
  const displayedDirection = sortByDirectionsLocale(`${sortBy}-${direction}-short`);
@@ -72,16 +72,16 @@ export const SortBy = () => {
72
72
  )}
73
73
  >
74
74
  <Menu.Section>
75
- <Menu.ItemWithCheckmark onClick={() => setSortBy("order")} isChecked={sortBy === "order"}>
75
+ <Menu.ItemWithCheckmark onClick={() => setTreeSortBy("order")} isChecked={sortBy === "order"}>
76
76
  {sortByValuesLocale("order")}
77
77
  </Menu.ItemWithCheckmark>
78
- <Menu.ItemWithCheckmark onClick={() => setSortBy("duration")} isChecked={sortBy === "duration"}>
78
+ <Menu.ItemWithCheckmark onClick={() => setTreeSortBy("duration")} isChecked={sortBy === "duration"}>
79
79
  {sortByValuesLocale("duration")}
80
80
  </Menu.ItemWithCheckmark>
81
- <Menu.ItemWithCheckmark onClick={() => setSortBy("status")} isChecked={sortBy === "status"}>
81
+ <Menu.ItemWithCheckmark onClick={() => setTreeSortBy("status")} isChecked={sortBy === "status"}>
82
82
  {sortByValuesLocale("status")}
83
83
  </Menu.ItemWithCheckmark>
84
- <Menu.ItemWithCheckmark onClick={() => setSortBy("alphabet")} isChecked={sortBy === "alphabet"}>
84
+ <Menu.ItemWithCheckmark onClick={() => setTreeSortBy("alphabet")} isChecked={sortBy === "alphabet"}>
85
85
  {sortByValuesLocale("alphabet")}
86
86
  </Menu.ItemWithCheckmark>
87
87
  </Menu.Section>
@@ -111,14 +111,14 @@ export const SortBy = () => {
111
111
  >
112
112
  <Menu.Section>
113
113
  <Menu.ItemWithCheckmark
114
- onClick={() => setDirection("asc")}
114
+ onClick={() => setTreeDirection("asc")}
115
115
  leadingIcon={sortAscIcon.id}
116
116
  isChecked={direction === "asc"}
117
117
  >
118
118
  {sortByDirectionsLocale(`${sortBy}-asc`)}
119
119
  </Menu.ItemWithCheckmark>
120
120
  <Menu.ItemWithCheckmark
121
- onClick={() => setDirection("desc")}
121
+ onClick={() => setTreeDirection("desc")}
122
122
  leadingIcon={sortDescIcon.id}
123
123
  isChecked={direction === "desc"}
124
124
  >
@@ -1,6 +1,5 @@
1
1
  import { type ComponentChildren, createContext } from "preact";
2
2
  import { useCallback, useContext, useReducer } from "preact/hooks";
3
- import { capitalize } from "@/utils/capitalize";
4
3
 
5
4
  export type SortBy = "order" | "duration" | "status" | "alphabet";
6
5
  export type Direction = "asc" | "desc";
@@ -1,7 +1,9 @@
1
+ import type { Statistic } from "@allurereport/core-api";
1
2
  import { statusesList } from "@allurereport/core-api";
2
3
  import { computed } from "@preact/signals";
3
- import { FunctionComponent } from "preact";
4
- import MetadataItem, { MetadataProps } from "@/components/app/ReportMetadata/MetadataItem";
4
+ import type { FunctionComponent } from "preact";
5
+ import type { MetadataProps } from "@/components/app/ReportMetadata/MetadataItem";
6
+ import MetadataItem from "@/components/app/ReportMetadata/MetadataItem";
5
7
  import { MetadataTestType } from "@/components/app/ReportMetadata/MetadataTestType";
6
8
  import { MetadataWithIcon } from "@/components/app/ReportMetadata/MetadataWithIcon";
7
9
  import * as styles from "@/components/app/ReportMetadata/styles.scss";
@@ -12,6 +14,7 @@ import { capitalize } from "@/utils/capitalize";
12
14
 
13
15
  export const MetadataSummary: FunctionComponent = () => {
14
16
  const { t } = useI18n("statuses");
17
+ const { t: testSummary } = useI18n("testSummary");
15
18
 
16
19
  return (
17
20
  <Loadable
@@ -23,20 +26,21 @@ export const MetadataSummary: FunctionComponent = () => {
23
26
  type: "all",
24
27
  count: stats.total,
25
28
  }));
26
- // TODO: https://github.com/qameta/allure3/issues/178
27
- // const metadataStatsKeys: (keyof Statistic)[] = ["flakyTests", "retryTests", "newTests"];
28
- // const metaDataTests = metadataStatsKeys
29
- // .filter((key) => stats[key])
30
- // .map((key) => {
31
- // const title = t[key];
32
- // const props = { title, count: stats[key], type: key };
33
- //
34
- // return (
35
- // <>
36
- // <MetadataItem key={key} props={props} renderComponent={MetadataWithIcon} />
37
- // </>
38
- // );
39
- // });
29
+ const metaDataTests = ["flaky", "retry"]
30
+ .map((key) => {
31
+ if (!stats[key]) {
32
+ return;
33
+ }
34
+ const title = testSummary(key);
35
+ const props = { title, count: stats[key] || 0, type: key };
36
+
37
+ return (
38
+ <div key={key}>
39
+ <MetadataItem key={key} props={props} renderComponent={MetadataWithIcon} />
40
+ </div>
41
+ );
42
+ })
43
+ .filter(Boolean);
40
44
 
41
45
  const metadataStatuses = statusesList
42
46
  .map((status) => ({ status, value: stats[status] }))
@@ -67,8 +71,8 @@ export const MetadataSummary: FunctionComponent = () => {
67
71
  props={allTest.value}
68
72
  renderComponent={MetadataWithIcon}
69
73
  />
70
- {/*<div className={styles["report-metadata-separator"]}></div>*/}
71
- {/*{metaDataTests}*/}
74
+ {Boolean(metaDataTests.length) && <div className={styles["report-metadata-separator"]} />}
75
+ {metaDataTests}
72
76
  </div>
73
77
  <div className={styles["report-metadata-status"]}>{metadataStatuses}</div>
74
78
  </div>
@@ -1,8 +1,8 @@
1
- import { FunctionComponent, h } from "preact";
1
+ import type { FunctionComponent } from "preact";
2
2
  import notifications from "@/assets/svg/line-alerts-notification-box.svg";
3
3
  import refresh from "@/assets/svg/line-arrows-refresh-ccw-1.svg";
4
4
  import lineGeneralZap from "@/assets/svg/line-general-zap.svg";
5
- import { MetadataProps } from "@/components/app/ReportMetadata/MetadataItem";
5
+ import type { MetadataProps } from "@/components/app/ReportMetadata/MetadataItem";
6
6
  import { SvgIcon } from "@/components/commons/SvgIcon";
7
7
  import { Text } from "@/components/commons/Typography";
8
8
  import * as styles from "./styles.scss";
@@ -10,7 +10,7 @@ type TabsContextT = {
10
10
 
11
11
  const TabsContext = createContext<TabsContextT | null>(null);
12
12
 
13
- export function useTabsContext() {
13
+ export const useTabsContext = () => {
14
14
  const context = useContext(TabsContext);
15
15
 
16
16
  if (!context) {
@@ -18,7 +18,7 @@ export function useTabsContext() {
18
18
  }
19
19
 
20
20
  return context;
21
- }
21
+ };
22
22
 
23
23
  export const TabsProvider = (props: { initialTab?: string; children: ComponentChildren }) => {
24
24
  const { children, initialTab } = props;
@@ -39,7 +39,6 @@ export const Tab = (props: { id: string; children: ComponentChildren }) => {
39
39
  const { id, children, ...rest } = props;
40
40
  const { currentTab, setCurrentTab } = useTabsContext();
41
41
  const isCurrentTab = currentTab === id;
42
-
43
42
  const handleTabClick = () => {
44
43
  if (isCurrentTab) {
45
44
  return;
@@ -1,13 +1,13 @@
1
+ import { FunctionalComponent } from "preact";
1
2
  import { useState } from "preact/hooks";
2
3
  import { MetadataButton } from "@/components/app/MetadataButton";
3
4
  import { Text } from "@/components/commons/Typography";
4
- import * as styles from "./styles.scss";
5
- import { FunctionalComponent } from "preact";
6
5
  import { AllureAwesomeTestResult } from "../../../../../types";
6
+ import * as styles from "./styles.scss";
7
7
 
8
8
  export type TestResultDescriptionProps = {
9
9
  description: AllureAwesomeTestResult["description"];
10
- }
10
+ };
11
11
 
12
12
  export const TestResultDescription: FunctionalComponent<TestResultDescriptionProps> = ({ description }) => {
13
13
  const [isOpen, setIsOpen] = useState<boolean>(true);
@@ -1,16 +1,20 @@
1
+ import { sanitizeHtml } from "@allurereport/web-commons";
1
2
  import { useState } from "preact/hooks";
2
3
  import LineGeneralCopy3 from "@/assets/svg/line-general-copy-3.svg";
3
4
  import { IconButton } from "@/components/commons/Button";
5
+ import { TooltipWrapper } from "@/components/commons/Tooltip";
4
6
  import { Code, Text } from "@/components/commons/Typography";
5
7
  import { useI18n } from "@/stores/locale";
6
8
  import { copyToClipboard } from "@/utils/copyToClipboard";
7
9
  import * as styles from "./styles.scss";
8
10
 
9
11
  const TestResultErrorTrace = ({ trace }) => {
12
+ const sanitizedTrace = sanitizeHtml(trace);
13
+
10
14
  return (
11
15
  <div data-testid="test-result-error-trace" className={styles["test-result-error-trace"]}>
12
16
  <Code size={"s"} type={"ui"}>
13
- <pre>{trace}</pre>
17
+ <pre dangerouslySetInnerHTML={{ __html: sanitizedTrace }} />
14
18
  </Code>
15
19
  </div>
16
20
  );
@@ -19,28 +23,26 @@ const TestResultErrorTrace = ({ trace }) => {
19
23
  export const TestResultError = ({ message, trace }) => {
20
24
  const [isOpen, setIsOpen] = useState(false);
21
25
  const { t } = useI18n("ui");
26
+ const { t: tooltip } = useI18n("controls");
22
27
 
23
28
  return (
24
29
  <div data-testid="test-result-error" className={styles["test-result-error"]}>
25
- <div
26
- data-testid="test-result-error-header"
27
- className={styles["test-result-error-header"]}
28
- onClick={() => setIsOpen(!isOpen)}
29
- >
30
+ <div data-testid="test-result-error-header" className={styles["test-result-error-header"]}>
30
31
  <Text tag={"p"} size={"m"} bold className={styles["test-result-error-text"]}>
31
32
  {t("error")}
32
33
  </Text>
33
- <IconButton
34
- style={"ghost"}
35
- size={"s"}
36
- icon={LineGeneralCopy3.id}
37
- onClick={(e) => {
38
- e.stopPropagation();
39
- copyToClipboard(message);
40
- }}
41
- />
34
+ <TooltipWrapper tooltipText={tooltip("clipboard")} tooltipTextAfterClick={tooltip("clipboardSuccess")}>
35
+ <IconButton
36
+ style={"ghost"}
37
+ size={"s"}
38
+ icon={LineGeneralCopy3.id}
39
+ onClick={() => {
40
+ copyToClipboard(message);
41
+ }}
42
+ />
43
+ </TooltipWrapper>
42
44
  </div>
43
- <div onClick={() => setIsOpen(!isOpen)}>
45
+ <div className={styles["test-result-error-message"]} onClick={() => setIsOpen(!isOpen)}>
44
46
  <Code data-testid="test-result-error-message" size={"s"}>
45
47
  {message}
46
48
  </Code>
@@ -1,10 +1,9 @@
1
1
  .test-result-error {
2
- padding: 8px 8px 12px 24px;
2
+ padding: 8px 8px 12px 16px;
3
3
  background-color: var(--bg-alpha-capella);
4
4
  border-radius: 8px;
5
5
  position: relative;
6
6
  overflow: hidden;
7
- cursor: pointer;
8
7
 
9
8
  &:before {
10
9
  content: "";
@@ -26,15 +25,27 @@
26
25
 
27
26
  .test-result-error-text {
28
27
  margin-bottom: 8px;
28
+ padding-left: 8px;
29
29
  color: var(--on-support-capella);
30
30
  }
31
31
 
32
32
  .test-result-error-trace {
33
33
  margin-top: 8px;
34
+ padding-left: 8px;
34
35
 
35
36
  pre {
36
- overflow-wrap: break-word;
37
- word-wrap: break-word;
38
- white-space: pre-wrap;
37
+ overflow: scroll;
38
+ padding-bottom: 24px;
39
+ }
40
+ }
41
+
42
+ .test-result-error-message {
43
+ padding: 8px;
44
+ border-radius: 8px;
45
+ cursor: pointer;
46
+ transition: background-color 300ms;
47
+
48
+ &:hover {
49
+ background: var(--bg-alpha-capella);
39
50
  }
40
51
  }
@@ -1,6 +1,6 @@
1
1
  import clsx from "clsx";
2
- import { FunctionalComponent } from "preact";
3
- import { AllureAwesomeTestResult } from "types";
2
+ import type { FunctionalComponent } from "preact";
3
+ import type { AllureAwesomeTestResult } from "types";
4
4
  import LineArrowsChevronDown from "@/assets/svg/line-arrows-chevron-down.svg";
5
5
  import LineGeneralHomeLine from "@/assets/svg/line-general-home-line.svg";
6
6
  import { LanguagePicker } from "@/components/app/LanguagePicker";
@@ -9,30 +9,41 @@ import TreeItemIcon from "@/components/app/Tree/TreeItemIcon";
9
9
  import { IconButton } from "@/components/commons/Button";
10
10
  import { TooltipWrapper } from "@/components/commons/Tooltip";
11
11
  import { Text } from "@/components/commons/Typography";
12
- import { navigateTo } from "@/index";
12
+ import { navigateTo, openInNewTab } from "@/index";
13
+ import { useI18n } from "@/stores";
13
14
  import { timestampToDate } from "@/utils/time";
14
15
 
15
16
  export const TestResultHistoryItem = ({ testResultItem }) => {
16
- const { status, message, trace, stop, duration, id, uuid } = testResultItem;
17
+ const { status, message, trace, stop, duration, id } = testResultItem;
17
18
  const [isOpened, setIsOpen] = useState(false);
18
19
  const convertedStop = timestampToDate(stop);
19
- const formattedDuration = formatDuration(duration);
20
+ const formattedDuration = formatDuration(duration as number);
21
+ const { t } = useI18n("controls");
20
22
 
21
- const navigateUrl = `/${uuid}/${id}`;
23
+ const navigateUrl = `/testresult/${id}`;
22
24
 
23
25
  return (
24
26
  <div>
25
- <div className={styles["test-result-history-item-header"]} onClick={() => setIsOpen(!isOpened)}>
26
- {Boolean(message) && <ArrowButton isOpened={isOpened} icon={arrowsChevronDown.id} />}
27
- <div className={styles["test-result-history-item-wrap"]}>
27
+ <div className={styles["test-result-history-item-header"]}>
28
+ {Boolean(message) && (
29
+ <span onClick={() => setIsOpen(!isOpened)}>
30
+ <ArrowButton isOpened={isOpened} icon={arrowsChevronDown.id} />
31
+ </span>
32
+ )}
33
+ <div
34
+ className={styles["test-result-history-item-wrap"]}
35
+ onClick={(e) => {
36
+ e.stopPropagation();
37
+ navigateTo(navigateUrl);
38
+ }}
39
+ >
28
40
  <TreeItemIcon status={status} className={styles["test-result-history-item-status"]} />
29
41
  <Text className={styles["test-result-history-item-text"]}>{convertedStop}</Text>
30
42
  <div className={styles["test-result-history-item-info"]}>
31
43
  <Text type="ui" size={"s"} className={styles["item-time"]}>
32
44
  {formattedDuration}
33
45
  </Text>
34
-
35
- <TooltipWrapper tooltipText={"Go to error"}>
46
+ <TooltipWrapper tooltipText={t("openInNewTab")}>
36
47
  <IconButton
37
48
  icon={LineGeneralLinkExternal.id}
38
49
  style={"ghost"}
@@ -40,7 +51,7 @@ export const TestResultHistoryItem = ({ testResultItem }) => {
40
51
  className={styles["test-result-history-item-link"]}
41
52
  onClick={(e) => {
42
53
  e.stopPropagation();
43
- navigateTo(navigateUrl);
54
+ openInNewTab(navigateUrl);
44
55
  }}
45
56
  />
46
57
  </TooltipWrapper>