@allurereport/web-awesome 3.8.2 → 3.10.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.
- package/README.md +112 -0
- package/allurerc-dev.mjs +10 -0
- package/dist/multi/121.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/173.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/174.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/252.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/282.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/29.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/310.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/416.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/507.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/527.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/600.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/605.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/638.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/672.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/686.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/725.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/741.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/749.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/755.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/779.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/894.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/943.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/980.app-b18cce138691927e8759.js +1 -0
- package/dist/multi/app-b18cce138691927e8759.js +2 -0
- package/dist/multi/manifest.json +26 -23
- package/dist/multi/styles-212da6c68fa0beb4c6c5.css +1 -0
- package/dist/multi/styles-5c882b14b6f3112e40c4.css +1 -0
- package/dist/multi/styles-a4f65de86208f79dd2be.css +58 -0
- package/dist/single/app-733f473da7b51f98876d.js +2 -0
- package/dist/single/manifest.json +1 -1
- package/package.json +19 -14
- package/src/assets/scss/_common.scss +2 -2
- package/src/assets/scss/index.scss +8 -6
- package/src/components/BaseLayout/index.tsx +14 -2
- package/src/components/BaseLayout/styles.scss +5 -5
- package/src/components/Categories/CategoryHeaderItem/styles.scss +2 -2
- package/src/components/Categories/CategoryTreeItem/styles.scss +2 -2
- package/src/components/Categories/GroupTreeItem/styles.scss +4 -5
- package/src/components/Categories/HistoryTreeItem/styles.scss +2 -2
- package/src/components/Categories/LabelTreeItem/styles.scss +2 -2
- package/src/components/Categories/MessageTreeItem/index.tsx +1 -1
- package/src/components/Categories/MessageTreeItem/styles.scss +18 -18
- package/src/components/Categories/sticky.ts +1 -1
- package/src/components/Footer/FooterVersion.tsx +5 -10
- package/src/components/Footer/index.tsx +7 -1
- package/src/components/Footer/styles.scss +8 -2
- package/src/components/Header/CiInfo/index.tsx +17 -13
- package/src/components/Header/CiInfo/styles.scss +1 -1
- package/src/components/Header/styles.scss +2 -2
- package/src/components/HeaderControls/index.tsx +1 -3
- package/src/components/HotkeysProvider/index.tsx +556 -0
- package/src/components/KeyboardShortcuts/index.tsx +73 -0
- package/src/components/KeyboardShortcuts/shortcutsConfig.ts +91 -0
- package/src/components/KeyboardShortcuts/styles.scss +69 -0
- package/src/components/MainReport/index.tsx +89 -72
- package/src/components/MainReport/styles.scss +20 -5
- package/src/components/Metadata/index.tsx +27 -6
- package/src/components/Metadata/styles.scss +21 -9
- package/src/components/MetadataButton/index.tsx +2 -0
- package/src/components/MetadataButton/styles.scss +1 -1
- package/src/components/NavTabs/styles.scss +8 -8
- package/src/components/ReportBody/styles.scss +3 -4
- package/src/components/ReportCategories/styles.scss +1 -1
- package/src/components/ReportFilters/styles.scss +1 -1
- package/src/components/ReportGlobalAttachments/styles.scss +1 -1
- package/src/components/ReportGlobalErrors/styles.scss +1 -1
- package/src/components/ReportHeader/index.tsx +25 -13
- package/src/components/ReportHeader/styles.scss +2 -2
- package/src/components/ReportMetadata/index.tsx +44 -15
- package/src/components/ReportMetadata/styles.scss +6 -6
- package/src/components/ReportQualityGateResults/styles.scss +2 -2
- package/src/components/ReportSearch/index.tsx +1 -5
- package/src/components/ReportTabs/styles.scss +9 -9
- package/src/components/SectionSwitcher/index.tsx +87 -10
- package/src/components/SideBySide/index.tsx +20 -2
- package/src/components/SideBySide/styles.scss +9 -1
- package/src/components/SplitLayout/index.tsx +11 -2
- package/src/components/SplitLayout/styles.scss +23 -4
- package/src/components/TestResult/TestStepsEmpty/styles.scss +1 -1
- package/src/components/TestResult/TrDescription/styles.scss +1 -1
- package/src/components/TestResult/TrDropdown/index.tsx +2 -2
- package/src/components/TestResult/TrDropdown/styles.scss +1 -1
- package/src/components/TestResult/TrEmpty/styles.scss +1 -1
- package/src/components/TestResult/TrEnvironmentItem/styles.scss +4 -4
- package/src/components/TestResult/TrError/index.tsx +32 -7
- package/src/components/TestResult/TrError/styles.scss +23 -23
- package/src/components/TestResult/TrHeader/styles.scss +2 -2
- package/src/components/TestResult/TrHistory/styles.scss +6 -6
- package/src/components/TestResult/TrInfo/styles.scss +8 -8
- package/src/components/TestResult/TrLinks/index.tsx +2 -2
- package/src/components/TestResult/TrLinks/styles.scss +2 -2
- package/src/components/TestResult/TrMetadata/index.tsx +1 -1
- package/src/components/TestResult/TrMetadata/styles.scss +1 -1
- package/src/components/TestResult/TrNavigation/index.tsx +1 -1
- package/src/components/TestResult/TrNavigation/styles.scss +2 -2
- package/src/components/TestResult/TrOverview.tsx +2 -0
- package/src/components/TestResult/TrParameters/index.tsx +1 -1
- package/src/components/TestResult/TrParameters/styles.scss +1 -1
- package/src/components/TestResult/TrPrevStatuses/styles.scss +8 -8
- package/src/components/TestResult/TrPwTraces/styles.scss +1 -1
- package/src/components/TestResult/TrRetriesView/TrRetriesItem.tsx +27 -1
- package/src/components/TestResult/TrRetriesView/styles.scss +20 -10
- package/src/components/TestResult/TrSetup/index.tsx +10 -4
- package/src/components/TestResult/TrSeverity/styles.scss +7 -7
- package/src/components/TestResult/TrStatus/styles.scss +2 -35
- package/src/components/TestResult/TrSteps/TrAttachment.tsx +79 -43
- package/src/components/TestResult/TrSteps/TrAttachmentInfo.tsx +44 -17
- package/src/components/TestResult/TrSteps/TrBodyItems.tsx +5 -2
- package/src/components/TestResult/TrSteps/TrErrorStep.tsx +3 -0
- package/src/components/TestResult/TrSteps/TrStep.tsx +15 -6
- package/src/components/TestResult/TrSteps/TrStepHeader.tsx +8 -5
- package/src/components/TestResult/TrSteps/index.tsx +7 -5
- package/src/components/TestResult/TrSteps/stepTreeExpansion.ts +27 -9
- package/src/components/TestResult/TrSteps/styles.scss +80 -20
- package/src/components/TestResult/TrTeardown/index.tsx +10 -4
- package/src/components/TestResult/bodyItems.ts +1 -1
- package/src/components/TestResult/index.tsx +8 -2
- package/src/components/TestResult/styles.scss +10 -1
- package/src/components/TestResult/trOverviewFocus.scss +4 -0
- package/src/components/Timeline/styles.scss +6 -6
- package/src/components/Tree/index.tsx +79 -5
- package/src/components/Tree/styles.scss +55 -35
- package/src/hooks/useTestResultOverviewFocusScroll.ts +23 -0
- package/src/index.html +30 -33
- package/src/index.tsx +12 -6
- package/src/locales/ar.json +62 -1
- package/src/locales/az.json +62 -1
- package/src/locales/de.json +62 -1
- package/src/locales/en.json +62 -1
- package/src/locales/es.json +62 -1
- package/src/locales/fr.json +62 -1
- package/src/locales/he.json +62 -1
- package/src/locales/hy.json +62 -1
- package/src/locales/it.json +62 -1
- package/src/locales/ja.json +62 -1
- package/src/locales/ka.json +62 -1
- package/src/locales/kr.json +62 -1
- package/src/locales/nl.json +62 -1
- package/src/locales/pl.json +62 -1
- package/src/locales/pt.json +62 -1
- package/src/locales/ru.json +62 -1
- package/src/locales/sv.json +62 -1
- package/src/locales/tr.json +62 -1
- package/src/locales/uk.json +62 -1
- package/src/locales/zh-TW.json +62 -1
- package/src/locales/zh.json +62 -1
- package/src/stores/keyboard.ts +371 -0
- package/src/stores/keyboardActions.ts +769 -0
- package/src/stores/locale.ts +5 -2
- package/src/stores/reportEnvSections.ts +6 -0
- package/src/stores/reportRootTabs.ts +95 -0
- package/src/stores/search.ts +147 -0
- package/src/stores/testResultOverviewNav.ts +119 -0
- package/src/stores/testResultTabs.ts +62 -0
- package/src/stores/timeline.ts +1 -1
- package/src/stores/tree.ts +42 -4
- package/src/stores/treeFilters/store.ts +3 -36
- package/src/stores/treeSort.ts +7 -1
- package/src/styles/_pane-active.scss +8 -0
- package/src/styles.scss +1 -1
- package/src/utils/atSeparator.ts +4 -0
- package/src/utils/flattenTestResultOverview.ts +182 -0
- package/src/utils/time.ts +2 -1
- package/src/utils/trOverviewFocus.ts +18 -0
- package/src/utils/treeFilters.ts +15 -4
- package/test/components/EnvironmentPicker.test.tsx +21 -3
- package/test/components/Footer.test.tsx +26 -0
- package/test/components/Header/CiInfo.test.tsx +56 -0
- package/test/components/Header.test.tsx +8 -0
- package/test/components/HeaderControls.test.tsx +28 -0
- package/test/components/ReportGlobals.test.tsx +9 -1
- package/test/components/ReportHeader.test.tsx +77 -0
- package/test/components/ReportMetadata.test.tsx +131 -0
- package/test/components/TestResult/PwTraceButton.test.tsx +8 -0
- package/test/components/TestResult/TrErrorStep.test.tsx +8 -0
- package/test/components/TestResult/TrOverview.test.tsx +30 -10
- package/test/components/TestResult/TrRetriesItem.test.tsx +163 -0
- package/test/components/TestResult/TrSteps.test.tsx +108 -0
- package/test/components/TestResult/bodyItems.test.ts +9 -1
- package/test/components/TestResult/openPwTraceInNewTab.test.ts +8 -0
- package/test/components/TestResult/stepTreeExpansion.test.ts +10 -2
- package/test/components/Timeline.test.tsx +15 -7
- package/test/stores/keyboard/keyboardActions.test.ts +615 -0
- package/test/stores/search.test.ts +143 -0
- package/test/stores/treeFilters/actions.test.ts +8 -0
- package/test/stores/treeSort.test.ts +58 -0
- package/test/utils/flattenTestResultOverview.test.ts +57 -0
- package/test/utils/ownerAddress.test.ts +9 -1
- package/test/utils/time.test.ts +52 -0
- package/test/utils/treeFilters.test.ts +113 -1
- package/types.d.ts +39 -0
- package/webpack.config.js +12 -7
- package/CONTRIBUTING.md +0 -34
- package/dist/multi/173.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/174.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/252.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/282.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/29.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/310.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/416.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/507.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/527.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/600.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/605.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/638.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/672.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/686.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/725.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/741.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/749.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/755.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/894.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/943.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/980.app-f008fb8342025f2b1ace.js +0 -1
- package/dist/multi/app-f008fb8342025f2b1ace.js +0 -2
- package/dist/multi/styles-9f7a23a0c8b79fa76981.css +0 -58
- package/dist/single/app-07332238da9897064301.js +0 -2
- package/src/assets/scss/day.scss +0 -53
- package/src/assets/scss/fonts.scss +0 -3
- package/src/assets/scss/night.scss +0 -63
- package/src/assets/scss/palette.scss +0 -393
- package/src/assets/scss/theme.scss +0 -330
- package/src/assets/scss/vars.scss +0 -11
- /package/dist/multi/{app-f008fb8342025f2b1ace.js.LICENSE.txt → app-b18cce138691927e8759.js.LICENSE.txt} +0 -0
- /package/dist/single/{app-07332238da9897064301.js.LICENSE.txt → app-733f473da7b51f98876d.js.LICENSE.txt} +0 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import {
|
|
2
|
+
flattenVisibleTree,
|
|
3
|
+
moveFocus,
|
|
4
|
+
router,
|
|
5
|
+
type FlatTreeNode,
|
|
6
|
+
type MoveDirection,
|
|
7
|
+
type MoveFocusResult,
|
|
8
|
+
type SubtreeToggleState,
|
|
9
|
+
} from "@allurereport/web-commons";
|
|
10
|
+
import type { RecursiveTree } from "@allurereport/web-components/global";
|
|
11
|
+
import { computed, effect, signal } from "@preact/signals";
|
|
12
|
+
|
|
13
|
+
import { statsByEnvStore } from "@/stores";
|
|
14
|
+
import { collapsedEnvironments, currentEnvironment, environmentsStore } from "@/stores/env";
|
|
15
|
+
import { isSplitMode } from "@/stores/layout";
|
|
16
|
+
import { rootTabRoute, testResultRoute } from "@/stores/router";
|
|
17
|
+
import { currentSection } from "@/stores/sections";
|
|
18
|
+
import { currentTrId } from "@/stores/testResult";
|
|
19
|
+
import {
|
|
20
|
+
collapsedTrees,
|
|
21
|
+
expandedTrees,
|
|
22
|
+
filteredTree,
|
|
23
|
+
isTreeOpened,
|
|
24
|
+
noTests,
|
|
25
|
+
noTestsFound,
|
|
26
|
+
setTreeOpened,
|
|
27
|
+
} from "@/stores/tree";
|
|
28
|
+
|
|
29
|
+
export type ActivePane = "tree" | "testResult";
|
|
30
|
+
|
|
31
|
+
export const activePane = signal<ActivePane>("tree");
|
|
32
|
+
export const treeFocusId = signal<string | undefined>(undefined);
|
|
33
|
+
export const hotkeysHelpOpen = signal(false);
|
|
34
|
+
export const pendingVimKey = signal<string | null>(null);
|
|
35
|
+
|
|
36
|
+
/** Last subtree cycle action per focused group/step (matches header chevron button memory). */
|
|
37
|
+
export const lastSubtreeToggleByScope = signal<Record<string, SubtreeToggleState>>({});
|
|
38
|
+
/** When true, the next tree focus scroll snaps the list pane to scrollTop 0 (Home / gg / zt). */
|
|
39
|
+
export const treeScrollPaneToTopPending = signal(false);
|
|
40
|
+
|
|
41
|
+
export const focusTreePane = () => {
|
|
42
|
+
activePane.value = "tree";
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const focusTestResultPane = () => {
|
|
46
|
+
activePane.value = "testResult";
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
effect(() => {
|
|
50
|
+
document.documentElement.setAttribute("data-active-pane", activePane.value);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export const toggleHotkeysHelp = () => {
|
|
54
|
+
hotkeysHelpOpen.value = !hotkeysHelpOpen.value;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const isSearchInput = (element: Element | null): element is HTMLInputElement =>
|
|
58
|
+
element instanceof HTMLInputElement && (element.name === "search" || element.dataset.testid === "search-input");
|
|
59
|
+
|
|
60
|
+
const releaseDetachedFocus = () => {
|
|
61
|
+
const active = document.activeElement;
|
|
62
|
+
|
|
63
|
+
if (isSearchInput(active)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (active instanceof HTMLElement && active !== document.body) {
|
|
68
|
+
active.blur();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/** Main report tabs (Results, Categories, …) are on screen and hotkeys may switch them. */
|
|
73
|
+
export const isReportRootTabsContext = (): boolean => {
|
|
74
|
+
if (currentSection.value !== "default") {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (isSplitMode.value) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (testResultRoute.value.matches) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return true;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/** Test-results tree is visible (Results tab or split left pane), not Categories/other tabs. */
|
|
90
|
+
export const isReportResultsTreeVisible = (): boolean => {
|
|
91
|
+
if (currentSection.value !== "default") {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (testResultRoute.value.matches && !isSplitMode.value) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (rootTabRoute.value.matches) {
|
|
100
|
+
const rootTab = rootTabRoute.value.params.rootTab;
|
|
101
|
+
|
|
102
|
+
return rootTab === "results";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return true;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const isTreeNavigationContext = (): boolean => {
|
|
109
|
+
if (!isReportResultsTreeVisible()) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (isSplitMode.value) {
|
|
114
|
+
return activePane.value === "tree";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return true;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const isTestResultHotkeysContext = (): boolean => {
|
|
121
|
+
if (!currentTrId.value) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (isSplitMode.value) {
|
|
126
|
+
return activePane.value === "testResult";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return testResultRoute.value.matches || Boolean(rootTabRoute.value.params.testResultId);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export const syncKeyboardStateFromRoute = () => {
|
|
133
|
+
pendingVimKey.value = null;
|
|
134
|
+
releaseDetachedFocus();
|
|
135
|
+
|
|
136
|
+
if (currentSection.value !== "default") {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
ensureTreeFocusId();
|
|
141
|
+
|
|
142
|
+
const hasTest = Boolean(currentTrId.value);
|
|
143
|
+
const split = isSplitMode.value;
|
|
144
|
+
const fullPageTest = testResultRoute.value.matches && !split;
|
|
145
|
+
|
|
146
|
+
if (fullPageTest) {
|
|
147
|
+
focusTestResultPane();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (split && !hasTest) {
|
|
152
|
+
focusTreePane();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!split && !hasTest) {
|
|
157
|
+
focusTreePane();
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export const isHotkeyScopeActive = (scope: "global" | "tree" | "testResult"): boolean => {
|
|
162
|
+
if (isSplitMode.value) {
|
|
163
|
+
return scope === "global" || scope === activePane.value;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (scope === "tree") {
|
|
167
|
+
return isReportRootTabsContext() && (isSplitMode.value ? activePane.value === "tree" : true);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (scope === "testResult") {
|
|
171
|
+
return isTestResultHotkeysContext();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return true;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const buildEnvSections = () => {
|
|
178
|
+
const envs = environmentsStore.value.data;
|
|
179
|
+
|
|
180
|
+
return Object.entries(filteredTree.value)
|
|
181
|
+
.map(([envId, tree]) => {
|
|
182
|
+
const stats = statsByEnvStore.value.data[envId];
|
|
183
|
+
|
|
184
|
+
if ((stats?.total ?? 0) === 0) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
id: envId,
|
|
190
|
+
opened: !collapsedEnvironments.value.includes(envId),
|
|
191
|
+
tree: tree as RecursiveTree,
|
|
192
|
+
statistic: stats,
|
|
193
|
+
};
|
|
194
|
+
})
|
|
195
|
+
.filter((section): section is NonNullable<typeof section> => section !== null);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const flattenTreeForKeyboard = (options: {
|
|
199
|
+
tree: Parameters<typeof flattenVisibleTree>[0]["tree"];
|
|
200
|
+
isRoot?: boolean;
|
|
201
|
+
rootStatistic?: Parameters<typeof flattenVisibleTree>[0]["rootStatistic"];
|
|
202
|
+
envSections?: Parameters<typeof flattenVisibleTree>[0]["envSections"];
|
|
203
|
+
}) => {
|
|
204
|
+
collapsedTrees.value;
|
|
205
|
+
expandedTrees.value;
|
|
206
|
+
|
|
207
|
+
return flattenVisibleTree({
|
|
208
|
+
collapsedTrees: collapsedTrees.value,
|
|
209
|
+
isGroupOpened: (scopedNodeId, openedByDefault) => isTreeOpened(scopedNodeId, openedByDefault),
|
|
210
|
+
...options,
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export const flatTree = computed((): FlatTreeNode[] => {
|
|
215
|
+
if (noTests.value || noTestsFound.value) {
|
|
216
|
+
return [];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const envs = environmentsStore.value.data;
|
|
220
|
+
const trees = filteredTree.value;
|
|
221
|
+
|
|
222
|
+
if (envs.length === 1) {
|
|
223
|
+
const soleId = envs[0]!.id;
|
|
224
|
+
const tree = trees[soleId];
|
|
225
|
+
|
|
226
|
+
if (!tree) {
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return flattenTreeForKeyboard({
|
|
231
|
+
tree,
|
|
232
|
+
isRoot: true,
|
|
233
|
+
rootStatistic: statsByEnvStore.value.data[soleId],
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const currentTree = currentEnvironment.value ? trees[currentEnvironment.value] : undefined;
|
|
238
|
+
|
|
239
|
+
if (currentTree) {
|
|
240
|
+
return flattenTreeForKeyboard({
|
|
241
|
+
tree: currentTree,
|
|
242
|
+
isRoot: true,
|
|
243
|
+
rootStatistic: statsByEnvStore.value.data[currentEnvironment.value],
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return flattenTreeForKeyboard({
|
|
248
|
+
envSections: buildEnvSections(),
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
export const getFlatTreeNode = (id: string | undefined) => flatTree.value.find((node) => node.id === id);
|
|
253
|
+
|
|
254
|
+
export const moveTreeFocus = (direction: MoveDirection): MoveFocusResult => {
|
|
255
|
+
return moveFocus(flatTree.value, treeFocusId.value, direction);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
export const setTreeFocusId = (id: string | undefined) => {
|
|
259
|
+
treeFocusId.value = id;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
export const ensureTreeFocusId = () => {
|
|
263
|
+
const flat = flatTree.value;
|
|
264
|
+
|
|
265
|
+
if (flat.length === 0) {
|
|
266
|
+
treeFocusId.value = undefined;
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Use peek() so that changes to treeFocusId itself do not re-trigger this effect —
|
|
271
|
+
// treeFocusId is the *output* here, not the trigger.
|
|
272
|
+
const currentId = treeFocusId.peek();
|
|
273
|
+
const currentExists = currentId ? flat.some((node) => node.id === currentId) : false;
|
|
274
|
+
|
|
275
|
+
if (currentExists) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const routeId = currentTrId.value;
|
|
280
|
+
const routeNode = routeId ? flat.find((node) => node.testResultId === routeId || node.id === routeId) : undefined;
|
|
281
|
+
|
|
282
|
+
const firstLeaf = flat.find((node) => node.kind === "leaf");
|
|
283
|
+
treeFocusId.value = routeNode?.id ?? firstLeaf?.id ?? flat[0]?.id;
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
effect(() => {
|
|
287
|
+
flatTree.value;
|
|
288
|
+
ensureTreeFocusId();
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
effect(() => {
|
|
292
|
+
currentSection.value;
|
|
293
|
+
currentTrId.value;
|
|
294
|
+
router.value.path;
|
|
295
|
+
isSplitMode.value;
|
|
296
|
+
syncKeyboardStateFromRoute();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
const expandPathToLeaf = (tree: RecursiveTree, targetNodeId: string, prefix: string | undefined): boolean => {
|
|
300
|
+
for (const leaf of tree.leaves) {
|
|
301
|
+
if (leaf.nodeId === targetNodeId) {
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
for (const sub of tree.trees) {
|
|
307
|
+
if (expandPathToLeaf(sub, targetNodeId, prefix)) {
|
|
308
|
+
const scopedId = prefix ? `${prefix}${sub.nodeId}` : sub.nodeId;
|
|
309
|
+
const openedByDefault = !sub.statistic || Boolean(sub.statistic.failed || sub.statistic.broken);
|
|
310
|
+
const isOpen = openedByDefault ? !collapsedTrees.peek().has(scopedId) : expandedTrees.peek().has(scopedId);
|
|
311
|
+
|
|
312
|
+
if (!isOpen) {
|
|
313
|
+
setTreeOpened(scopedId, true, openedByDefault);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return false;
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const expandAndFocusCurrentTest = () => {
|
|
324
|
+
const testResultId = currentTrId.peek();
|
|
325
|
+
|
|
326
|
+
if (!testResultId) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const flat = flatTree.peek();
|
|
331
|
+
const existing = flat.find((n) => n.kind === "leaf" && n.testResultId === testResultId);
|
|
332
|
+
|
|
333
|
+
if (existing) {
|
|
334
|
+
treeFocusId.value = existing.id;
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const envs = environmentsStore.peek().data ?? [];
|
|
339
|
+
const trees = filteredTree.peek();
|
|
340
|
+
const curEnv = currentEnvironment.peek();
|
|
341
|
+
const usePrefix = envs.length > 1 && !curEnv;
|
|
342
|
+
|
|
343
|
+
for (const env of envs) {
|
|
344
|
+
const envTree = trees[env.id];
|
|
345
|
+
|
|
346
|
+
if (!envTree) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const prefix = usePrefix ? `${env.id}:` : undefined;
|
|
351
|
+
|
|
352
|
+
if (expandPathToLeaf(envTree, testResultId, prefix)) {
|
|
353
|
+
treeFocusId.value = prefix ? `${prefix}${testResultId}` : testResultId;
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
let prevIsSplitMode = isSplitMode.peek();
|
|
360
|
+
|
|
361
|
+
effect(() => {
|
|
362
|
+
const nowSplit = isSplitMode.value;
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
if (nowSplit && !prevIsSplitMode) {
|
|
366
|
+
expandAndFocusCurrentTest();
|
|
367
|
+
}
|
|
368
|
+
} finally {
|
|
369
|
+
prevIsSplitMode = nowSplit;
|
|
370
|
+
}
|
|
371
|
+
});
|