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

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 (85) hide show
  1. package/.eslintrc.cjs +1 -1
  2. package/CONTRIBUTING.md +34 -0
  3. package/dist/multi/{141.app-13f840d5.js → 141.app-71d7f77e.js} +1 -1
  4. package/dist/multi/222.app-71d7f77e.js +1 -0
  5. package/dist/multi/335.app-71d7f77e.js +1 -0
  6. package/dist/multi/{34.app-13f840d5.js → 34.app-71d7f77e.js} +1 -1
  7. package/dist/multi/349.app-71d7f77e.js +1 -0
  8. package/dist/multi/378.app-71d7f77e.js +1 -0
  9. package/dist/multi/{406.app-13f840d5.js → 406.app-71d7f77e.js} +1 -1
  10. package/dist/multi/476.app-71d7f77e.js +1 -0
  11. package/dist/multi/{53.app-13f840d5.js → 53.app-71d7f77e.js} +1 -1
  12. package/dist/multi/{584.app-13f840d5.js → 584.app-71d7f77e.js} +1 -1
  13. package/dist/multi/690.app-71d7f77e.js +1 -0
  14. package/dist/multi/{747.app-13f840d5.js → 747.app-71d7f77e.js} +1 -1
  15. package/dist/multi/{767.app-13f840d5.js → 767.app-71d7f77e.js} +1 -1
  16. package/dist/multi/{816.app-13f840d5.js → 816.app-71d7f77e.js} +1 -1
  17. package/dist/multi/83.app-71d7f77e.js +1 -0
  18. package/dist/multi/{873.app-13f840d5.js → 873.app-71d7f77e.js} +1 -1
  19. package/dist/multi/{920.app-13f840d5.js → 920.app-71d7f77e.js} +1 -1
  20. package/dist/multi/{991.app-13f840d5.js → 991.app-71d7f77e.js} +1 -1
  21. package/dist/multi/app-71d7f77e.js +2 -0
  22. package/dist/multi/manifest.json +20 -20
  23. package/dist/multi/{styles-13f840d5.css → styles-71d7f77e.css} +4 -4
  24. package/dist/single/app-7aa8b012.js +2 -0
  25. package/dist/single/manifest.json +1 -1
  26. package/package.json +11 -4
  27. package/src/components/app/ArrowButton/index.tsx +3 -2
  28. package/src/components/app/BaseLayout/index.tsx +5 -5
  29. package/src/components/app/ReportBody/Filters.tsx +12 -10
  30. package/src/components/app/ReportBody/HeaderActions.tsx +3 -3
  31. package/src/components/app/ReportBody/SortBy.tsx +8 -8
  32. package/src/components/app/ReportBody/context.tsx +0 -1
  33. package/src/components/app/Tabs/index.tsx +2 -3
  34. package/src/components/app/TestResult/TestResultDescription/index.tsx +3 -3
  35. package/src/components/app/TestResult/TestResultNavigation/index.tsx +34 -37
  36. package/src/components/app/TestResult/TestResultNavigation/styles.scss +1 -1
  37. package/src/components/app/TestResult/TestResultSteps/attachment.tsx +4 -6
  38. package/src/components/app/Tree/Tree.tsx +54 -101
  39. package/src/components/app/Tree/TreeHeader.tsx +13 -12
  40. package/src/components/app/Tree/TreeItem.tsx +3 -1
  41. package/src/components/app/Tree/index.tsx +31 -7
  42. package/src/components/app/Tree/styles.scss +9 -3
  43. package/src/components/commons/Menu/index.tsx +4 -4
  44. package/src/components/commons/SearchBox/index.tsx +8 -6
  45. package/src/components/commons/Toggle/index.tsx +2 -1
  46. package/src/components/commons/Tooltip/index.tsx +3 -3
  47. package/src/i18n/constants.ts +21 -2
  48. package/src/i18n/locales/am.json +3 -1
  49. package/src/i18n/locales/az.json +3 -1
  50. package/src/i18n/locales/de.json +3 -1
  51. package/src/i18n/locales/en.json +4 -2
  52. package/src/i18n/locales/es.json +3 -0
  53. package/src/i18n/locales/fr.json +3 -1
  54. package/src/i18n/locales/he.json +3 -1
  55. package/src/i18n/locales/it.json +3 -1
  56. package/src/i18n/locales/ja.json +3 -1
  57. package/src/i18n/locales/ka.json +3 -1
  58. package/src/i18n/locales/kr.json +3 -1
  59. package/src/i18n/locales/nl.json +3 -1
  60. package/src/i18n/locales/pl.json +3 -1
  61. package/src/i18n/locales/pt.json +3 -1
  62. package/src/i18n/locales/ru.json +3 -1
  63. package/src/i18n/locales/sv.json +3 -1
  64. package/src/i18n/locales/tr.json +3 -1
  65. package/src/i18n/locales/zh.json +4 -2
  66. package/src/index.html +1 -0
  67. package/src/stores/chart.ts +2 -2
  68. package/src/stores/testResults.ts +26 -4
  69. package/src/stores/tree.ts +98 -4
  70. package/src/types/globals.d.ts +6 -1
  71. package/src/utils/treeFilters.ts +73 -120
  72. package/test/utils/treeFilters.test.ts +424 -0
  73. package/types.d.ts +25 -4
  74. package/vitest.config.ts +12 -0
  75. package/dist/multi/222.app-13f840d5.js +0 -1
  76. package/dist/multi/335.app-13f840d5.js +0 -1
  77. package/dist/multi/349.app-13f840d5.js +0 -1
  78. package/dist/multi/378.app-13f840d5.js +0 -1
  79. package/dist/multi/476.app-13f840d5.js +0 -1
  80. package/dist/multi/690.app-13f840d5.js +0 -1
  81. package/dist/multi/83.app-13f840d5.js +0 -1
  82. package/dist/multi/app-13f840d5.js +0 -2
  83. package/dist/single/app-d31bd53e.js +0 -2
  84. /package/dist/multi/{app-13f840d5.js.LICENSE.txt → app-71d7f77e.js.LICENSE.txt} +0 -0
  85. /package/dist/single/{app-d31bd53e.js.LICENSE.txt → app-7aa8b012.js.LICENSE.txt} +0 -0
@@ -1,14 +1,108 @@
1
1
  import { fetchReportJsonData } from "@allurereport/web-commons";
2
- import { signal } from "@preact/signals";
2
+ import { computed, signal } from "@preact/signals";
3
3
  import type { StoreSignalState } from "@/stores/types";
4
+ import { createRecursiveTree, isRecursiveTreeEmpty } from "@/utils/treeFilters";
5
+ import type {AllureAwesomeStatus, AllureAwesomeTree, AllureAwesomeTreeGroup} from "../../types";
4
6
 
5
- export const treeStore = signal<StoreSignalState<any>>({
7
+ export type TreeSortBy = "order" | "duration" | "status" | "alphabet";
8
+ export type TreeDirection = "asc" | "desc";
9
+ export type TreeFilters = "flaky" | "retry" | "new";
10
+ export type TreeFiltersState = {
11
+ query: string;
12
+ status: AllureAwesomeStatus;
13
+ filter: Record<TreeFilters, boolean>;
14
+ sortBy: TreeSortBy;
15
+ direction: TreeDirection;
16
+ };
17
+
18
+ export const treeStore = signal<StoreSignalState<AllureAwesomeTree>>({
6
19
  loading: true,
7
20
  error: undefined,
8
21
  data: undefined,
9
22
  });
10
23
 
11
- export const fetchTreeData = async (treeName: string) => {
24
+ export const noTests = computed(() => !Object.keys(treeStore?.value?.data?.leavesById).length);
25
+
26
+ export const treeFiltersStore = signal<TreeFiltersState>({
27
+ query: "",
28
+ status: "total",
29
+ filter: {
30
+ flaky: false,
31
+ retry: false,
32
+ new: false,
33
+ },
34
+ sortBy: "order",
35
+ direction: "asc",
36
+ });
37
+
38
+ export const filteredTree = computed(() => {
39
+ const { root, leavesById, groupsById } = treeStore.value.data;
40
+
41
+ return createRecursiveTree({
42
+ group: root as AllureAwesomeTreeGroup,
43
+ leavesById,
44
+ groupsById,
45
+ filterOptions: treeFiltersStore.value,
46
+ });
47
+ });
48
+
49
+ export const noTestsFound = computed(() => {
50
+ return isRecursiveTreeEmpty(filteredTree.value);
51
+ });
52
+
53
+ export const clearTreeFilters = () => {
54
+ treeFiltersStore.value = {
55
+ query: "",
56
+ status: "total",
57
+ filter: {
58
+ flaky: false,
59
+ retry: false,
60
+ new: false,
61
+ },
62
+ sortBy: "order",
63
+ direction: "asc",
64
+ };
65
+ };
66
+
67
+ export const setTreeQuery = (query: string) => {
68
+ treeFiltersStore.value = {
69
+ ...treeFiltersStore.value,
70
+ query,
71
+ };
72
+ };
73
+
74
+ export const setTreeStatus = (status: AllureAwesomeStatus) => {
75
+ treeFiltersStore.value = {
76
+ ...treeFiltersStore.value,
77
+ status,
78
+ };
79
+ };
80
+
81
+ export const setTreeSortBy = (sortBy: TreeSortBy) => {
82
+ treeFiltersStore.value = {
83
+ ...treeFiltersStore.value,
84
+ sortBy,
85
+ };
86
+ };
87
+
88
+ export const setTreeDirection = (direction: TreeDirection) => {
89
+ treeFiltersStore.value = {
90
+ ...treeFiltersStore.value,
91
+ direction,
92
+ };
93
+ };
94
+
95
+ export const setTreeFilter = (filterKey: TreeFilters, value: boolean) => {
96
+ treeFiltersStore.value = {
97
+ ...treeFiltersStore.value,
98
+ filter: {
99
+ ...treeFiltersStore.value.filter,
100
+ [filterKey]: value,
101
+ },
102
+ };
103
+ };
104
+
105
+ export const fetchTreeData = async () => {
12
106
  treeStore.value = {
13
107
  ...treeStore.value,
14
108
  loading: true,
@@ -16,7 +110,7 @@ export const fetchTreeData = async (treeName: string) => {
16
110
  };
17
111
 
18
112
  try {
19
- const res = await fetchReportJsonData(`widgets/${treeName}.json`);
113
+ const res = await fetchReportJsonData<AllureAwesomeTree>("widgets/tree.json");
20
114
 
21
115
  treeStore.value = {
22
116
  data: res,
@@ -2,7 +2,12 @@ declare module "*.svg" {
2
2
  const content: {
3
3
  id: string;
4
4
  };
5
+
5
6
  export default content;
6
7
  }
7
8
 
8
- declare module "*.scss";
9
+ declare module "*.scss" {
10
+ const content: Record<string, string>;
11
+
12
+ export = content;
13
+ }
@@ -1,5 +1,5 @@
1
- import type { DefaultTreeData } from "@allurereport/core-api";
2
- import type { ReportContentContextValue } from "@/components/app/ReportBody/context";
1
+ import type { TreeFiltersState } from "@/stores/tree";
2
+ import type { AllureAwesomeRecursiveTree, AllureAwesomeTree, AllureAwesomeTreeGroup } from "../../types";
3
3
 
4
4
  const statusOrder = {
5
5
  failed: 1,
@@ -9,142 +9,95 @@ const statusOrder = {
9
9
  unknown: 5,
10
10
  };
11
11
 
12
- const filterByQuery = ({
13
- leaves,
14
- leavesById,
15
- query,
16
- }: {
17
- leaves: string[];
18
- leavesById: DefaultTreeData["leavesById"];
19
- query: string;
20
- }) =>
21
- leaves.filter((leafId) => {
22
- const lowerCaseQuery = query.toLocaleLowerCase();
23
- const foundById = leavesById[leafId].nodeId.toLowerCase().includes(lowerCaseQuery);
24
- const foundByName = leavesById[leafId].name.toLowerCase().includes(lowerCaseQuery);
12
+ export const filterLeaves = (
13
+ leaves: string[] = [],
14
+ leavesById: AllureAwesomeTree["leavesById"],
15
+ filterOptions?: TreeFiltersState,
16
+ ) => {
17
+ const filteredLeaves = [...leaves]
18
+ .map((leafId) => leavesById[leafId])
19
+ .filter((leaf) => {
20
+ const queryMatched = !filterOptions?.query || leaf.name.toLowerCase().includes(filterOptions.query.toLowerCase());
21
+ const statusMatched =
22
+ !filterOptions?.status || filterOptions?.status === "total" || leaf.status === filterOptions.status;
23
+ const flakyMatched = !filterOptions?.filter?.flaky || leaf.flaky;
24
+ const retryMatched = !filterOptions?.filter?.retry || leaf.retry;
25
+ // TODO: at this moment we don't have a new field implementation even in the generator
26
+ // const newMatched = !filterOptions?.filter?.new || leaf.new;
27
+
28
+ return [queryMatched, statusMatched, flakyMatched, retryMatched].every(Boolean);
29
+ });
25
30
 
26
- return foundById || foundByName;
27
- });
31
+ if (!filterOptions) {
32
+ return filteredLeaves;
33
+ }
28
34
 
29
- const sortedLeaves = ({
30
- leavesFiltered,
31
- leavesById,
32
- filterOptions,
33
- }: {
34
- leavesFiltered: string[];
35
- leavesById: DefaultTreeData["leavesById"];
36
- filterOptions: ReportContentContextValue;
37
- }) => {
38
- leavesFiltered.sort((a, b) => {
39
- const leafA = leavesById[a];
40
- const leafB = leavesById[b];
41
- const filterDirection = filterOptions.direction === "asc";
35
+ return filteredLeaves.sort((a, b) => {
36
+ const asc = filterOptions.direction === "asc";
42
37
 
43
38
  switch (filterOptions.sortBy) {
39
+ case "order":
40
+ return asc ? a.groupOrder - b.groupOrder : b.groupOrder - a.groupOrder;
44
41
  case "duration":
45
- return filterDirection
46
- ? (leafA.duration || 0) - (leafB.duration || 0)
47
- : (leafB.duration || 0) - (leafA.duration || 0);
42
+ return asc ? (a.duration || 0) - (b.duration || 0) : (b.duration || 0) - (a.duration || 0);
48
43
  case "alphabet":
49
- return filterDirection ? leafA.name.localeCompare(leafB.name) : leafB.name.localeCompare(leafA.name);
44
+ return asc ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name);
50
45
  case "status": {
51
- const statusA = statusOrder[leafA.status] || statusOrder.unknown;
52
- const statusB = statusOrder[leafB.status] || statusOrder.unknown;
53
- return filterDirection ? statusA - statusB : statusB - statusA;
46
+ const statusA = statusOrder[a.status] || statusOrder.unknown;
47
+ const statusB = statusOrder[b.status] || statusOrder.unknown;
48
+
49
+ return asc ? statusA - statusB : statusB - statusA;
54
50
  }
55
51
  default:
56
52
  return 0;
57
53
  }
58
54
  });
59
-
60
- if (filterOptions.direction === "desc" && ["order"].includes(filterOptions.sortBy as string)) {
61
- leavesFiltered.reverse();
62
- }
63
-
64
- return leavesFiltered;
65
55
  };
66
56
 
67
- const filterByTestType = ({
68
- leaves,
69
- leavesById,
70
- filterTypes,
71
- }: {
72
- leaves: string[];
73
- leavesById: DefaultTreeData["leavesById"];
74
- filterTypes: ReportContentContextValue["filter"];
75
- }) => {
76
- return leaves.filter((leafId) => {
77
- const leaf = leavesById[leafId];
78
- return (!filterTypes.flaky || leaf.flaky) && (!filterTypes.retry || leaf.retry) && (!filterTypes.new || leaf.new);
79
- });
80
- };
81
-
82
- export const filterLeaves = (
83
- leaves: string[],
84
- leavesById: DefaultTreeData["leavesById"],
85
- statusFilter?: string,
86
- filterOptions?: ReportContentContextValue,
87
- ): string[] => {
88
- let leavesFiltered =
89
- statusFilter === "total" || !statusFilter
90
- ? filterByQuery({
91
- leaves,
57
+ /**
58
+ * Fills the given tree from generator and returns recursive tree which includes leaves data instead of their IDs
59
+ * Filters leaves when `filterOptions` property is provided
60
+ * @param payload
61
+ */
62
+ export const createRecursiveTree = (payload: {
63
+ group: AllureAwesomeTreeGroup;
64
+ groupsById: AllureAwesomeTree["groupsById"];
65
+ leavesById: AllureAwesomeTree["leavesById"];
66
+ filterOptions?: TreeFiltersState;
67
+ }): AllureAwesomeRecursiveTree => {
68
+ const { group, groupsById, leavesById, filterOptions } = payload;
69
+ const groupLeaves = group.leaves ?? [];
70
+
71
+ return {
72
+ ...group,
73
+ // FIXME: don't have any idea, why eslint marks next line as unsafe because it actually has a correct type
74
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
75
+ leaves: filterLeaves(groupLeaves, leavesById, filterOptions),
76
+ trees: group?.groups
77
+ ?.filter((groupId) => {
78
+ const subGroup = groupsById[groupId];
79
+
80
+ return subGroup?.leaves?.length || subGroup?.groups?.length;
81
+ })
82
+ ?.map((groupId) =>
83
+ createRecursiveTree({
84
+ group: groupsById[groupId],
85
+ groupsById,
92
86
  leavesById,
93
- query: filterOptions.query,
94
- })
95
- : leaves.filter((leafId) => leavesById[leafId].status === statusFilter);
96
-
97
- leavesFiltered = filterByQuery({
98
- leaves: leavesFiltered,
99
- leavesById,
100
- query: filterOptions.query,
101
- });
102
-
103
- leavesFiltered = filterByTestType({ leaves: leavesFiltered, leavesById, filterTypes: filterOptions.filter });
104
-
105
- leavesFiltered = sortedLeaves({ leavesFiltered, leavesById, filterOptions });
106
-
107
- return leavesFiltered;
87
+ filterOptions,
88
+ }),
89
+ ),
90
+ };
108
91
  };
109
92
 
110
- export const filterGroups = (
111
- groups: string[],
112
- groupsById: DefaultTreeData["groupsById"],
113
- leavesById: DefaultTreeData["leavesById"],
114
- statusFilter?: string,
115
- filterOptions?: ReportContentContextValue,
116
- ): string[] => {
117
- const gro = groups.filter((groupId) => {
118
- const group = groupsById?.[groupId];
119
- const groupLeaves = filterLeaves((group?.leaves as string[]) || [], leavesById, statusFilter, filterOptions);
120
- const filteredSubGroups = group?.groups?.filter((subGroupId: number) => {
121
- const subGroup = groupsById?.[subGroupId] as { leaves: string[]; groups: string[] };
122
- return (
123
- filterLeaves(subGroup?.leaves || [], leavesById, statusFilter, filterOptions).length > 0 ||
124
- filterGroups(subGroup?.groups || [], groupsById, leavesById, statusFilter, filterOptions).length > 0
125
- );
126
- });
127
-
128
- return groupLeaves.length > 0 || (filteredSubGroups && filteredSubGroups.length > 0);
129
- });
130
-
131
- const sortedGroups = gro.sort((a, b) => {
132
- const leafA = groupsById[a];
133
- const leafB = groupsById[b];
134
- const filterDirection = filterOptions.direction === "asc";
135
-
136
- switch (filterOptions.sortBy) {
137
- case "alphabet": {
138
- return filterDirection ? leafA.name.localeCompare(leafB.name) : leafB.name.localeCompare(leafA.name);
139
- }
140
- default:
141
- return 0;
142
- }
143
- });
93
+ export const isRecursiveTreeEmpty = (tree: AllureAwesomeRecursiveTree) => {
94
+ if (!tree.trees?.length && !tree.leaves?.length) {
95
+ return true;
96
+ }
144
97
 
145
- if (filterOptions.direction === "desc" && ["order"].includes(filterOptions.sortBy as string)) {
146
- sortedGroups.reverse();
98
+ if (tree.leaves?.length) {
99
+ return false;
147
100
  }
148
101
 
149
- return sortedGroups;
102
+ return tree.trees?.every((subTree) => isRecursiveTreeEmpty(subTree));
150
103
  };