@allurereport/web-awesome 3.6.0 → 3.6.1
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/dist/multi/app-1c928f385beb78e2c2e8.js +2 -0
- package/dist/multi/{app-15cc636581486c011867.js.LICENSE.txt → app-1c928f385beb78e2c2e8.js.LICENSE.txt} +1 -1
- package/dist/multi/manifest.json +23 -23
- package/dist/multi/{styles-fd12e72f7e024e668bb4.css → styles-3515d3bdd45651cd4e66.css} +8 -8
- package/dist/single/app-16786ab64ac3e094685f.js +2 -0
- package/dist/single/{app-bbd6e33664f6d94cfaac.js.LICENSE.txt → app-16786ab64ac3e094685f.js.LICENSE.txt} +1 -1
- package/dist/single/manifest.json +1 -1
- package/package.json +6 -6
- package/src/components/Header/styles.scss +2 -1
- package/src/components/SectionSwitcher/styles.scss +1 -1
- package/src/components/SideBySide/index.tsx +1 -1
- package/src/components/SideBySide/styles.scss +6 -5
- package/src/components/TestResult/TrDropdown/index.tsx +7 -1
- package/src/components/TestResult/TrDropdown/styles.scss +7 -0
- package/src/components/TestResult/TrHeader/styles.scss +3 -0
- package/src/components/TestResult/TrRetriesView/TrRetriesItem.tsx +4 -3
- package/src/components/TestResult/TrRetriesView/index.tsx +2 -2
- package/src/components/TestResult/TrSetup/index.tsx +6 -16
- package/src/components/TestResult/TrSteps/TrAttachment.tsx +3 -3
- package/src/components/TestResult/TrSteps/TrErrorStep.tsx +9 -3
- package/src/components/TestResult/TrSteps/TrStep.tsx +113 -8
- package/src/components/TestResult/TrSteps/TrStepHeader.tsx +3 -0
- package/src/components/TestResult/TrSteps/index.tsx +90 -5
- package/src/components/TestResult/TrSteps/stepTreeExpansion.ts +101 -0
- package/src/components/TestResult/TrSteps/styles.scss +12 -0
- package/src/components/TestResult/TrTeardown/index.tsx +6 -16
- package/src/components/TestResult/bodyItems.ts +26 -1
- package/src/components/Tree/index.tsx +7 -2
- package/src/components/Tree/styles.scss +1 -1
- package/src/index.tsx +13 -2
- package/src/stores/env.ts +6 -3
- package/src/stores/envInfo.ts +2 -2
- package/src/stores/sections.ts +3 -1
- package/src/stores/stats.ts +3 -3
- package/src/stores/tree.ts +40 -8
- package/test/components/TestResult/bodyItems.test.ts +25 -2
- package/test/components/TestResult/stepTreeExpansion.test.ts +179 -0
- package/types.d.ts +2 -0
- package/webpack.config.js +15 -3
- package/dist/multi/app-15cc636581486c011867.js +0 -2
- package/dist/single/app-bbd6e33664f6d94cfaac.js +0 -2
- /package/dist/multi/{173.app-15cc636581486c011867.js → 173.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{174.app-15cc636581486c011867.js → 174.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{252.app-15cc636581486c011867.js → 252.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{282.app-15cc636581486c011867.js → 282.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{29.app-15cc636581486c011867.js → 29.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{310.app-15cc636581486c011867.js → 310.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{416.app-15cc636581486c011867.js → 416.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{507.app-15cc636581486c011867.js → 507.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{527.app-15cc636581486c011867.js → 527.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{600.app-15cc636581486c011867.js → 600.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{605.app-15cc636581486c011867.js → 605.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{638.app-15cc636581486c011867.js → 638.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{672.app-15cc636581486c011867.js → 672.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{686.app-15cc636581486c011867.js → 686.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{725.app-15cc636581486c011867.js → 725.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{741.app-15cc636581486c011867.js → 741.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{749.app-15cc636581486c011867.js → 749.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{755.app-15cc636581486c011867.js → 755.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{894.app-15cc636581486c011867.js → 894.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{943.app-15cc636581486c011867.js → 943.app-1c928f385beb78e2c2e8.js} +0 -0
- /package/dist/multi/{980.app-15cc636581486c011867.js → 980.app-1c928f385beb78e2c2e8.js} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @license DOMPurify 3.
|
|
1
|
+
/*! @license DOMPurify 3.4.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.0/LICENSE */
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Prism: Lightweight, robust, elegant syntax highlighting
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allurereport/web-awesome",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.1",
|
|
4
4
|
"description": "The static files for Allure Awesome Report",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"allure",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"lint:fix": "oxlint --import-plugin --fix src test features stories"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@allurereport/charts-api": "3.6.
|
|
31
|
-
"@allurereport/core-api": "3.6.
|
|
32
|
-
"@allurereport/plugin-api": "3.6.
|
|
33
|
-
"@allurereport/web-commons": "3.6.
|
|
34
|
-
"@allurereport/web-components": "3.6.
|
|
30
|
+
"@allurereport/charts-api": "3.6.1",
|
|
31
|
+
"@allurereport/core-api": "3.6.1",
|
|
32
|
+
"@allurereport/plugin-api": "3.6.1",
|
|
33
|
+
"@allurereport/web-commons": "3.6.1",
|
|
34
|
+
"@allurereport/web-components": "3.6.1",
|
|
35
35
|
"@preact/signals": "^2.6.1",
|
|
36
36
|
"clsx": "^2.1.1",
|
|
37
37
|
"d3-shape": "^3.2.0",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
.above {
|
|
2
|
-
margin-bottom: 12px;
|
|
3
2
|
display: flex;
|
|
4
3
|
width: 100%;
|
|
4
|
+
min-width: 0;
|
|
5
5
|
justify-content: space-between;
|
|
6
6
|
align-items: center;
|
|
7
7
|
gap: 12px;
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
.right {
|
|
30
30
|
margin-left: auto;
|
|
31
|
+
flex: none;
|
|
31
32
|
display: flex;
|
|
32
33
|
gap: 4px;
|
|
33
34
|
align-items: center;
|
|
@@ -15,7 +15,7 @@ const SideBySide = ({ left, right }: { left: JSX.Element; right: JSX.Element })
|
|
|
15
15
|
|
|
16
16
|
const splitter = Split([`.${styles["side-left"]}`, `.${styles["side-right"]}`], {
|
|
17
17
|
sizes,
|
|
18
|
-
gutterSize:
|
|
18
|
+
gutterSize: 4,
|
|
19
19
|
gutter: (): HTMLElement => {
|
|
20
20
|
const gutter = document.createElement("div");
|
|
21
21
|
gutter.className = `${styles.gutter}`;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
.side {
|
|
2
2
|
width: 100%;
|
|
3
3
|
margin: 0;
|
|
4
|
-
overflow: hidden;
|
|
5
4
|
display: flex;
|
|
6
5
|
max-width: 1920px;
|
|
7
6
|
justify-content: space-between;
|
|
@@ -17,15 +16,17 @@
|
|
|
17
16
|
|
|
18
17
|
.side-left {
|
|
19
18
|
margin-right: auto;
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
border-radius: 12px 0 0 12px;
|
|
20
|
+
box-shadow: var(--shadow-small);
|
|
21
|
+
overflow: hidden;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
.side-right {
|
|
25
25
|
flex: 1 1 auto;
|
|
26
26
|
margin-left: auto;
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
border-radius: 0 12px 12px 0;
|
|
28
|
+
box-shadow: var(--shadow-small);
|
|
29
|
+
overflow: hidden;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
[dir="ltr"] {
|
|
@@ -11,8 +11,9 @@ export const TrDropdown: FunctionalComponent<{
|
|
|
11
11
|
title: string;
|
|
12
12
|
icon: string;
|
|
13
13
|
counter: number;
|
|
14
|
+
actions?: preact.ComponentChildren;
|
|
14
15
|
className?: ClassValue;
|
|
15
|
-
}> = ({ isOpened, setIsOpen, title, icon, counter, className }) => {
|
|
16
|
+
}> = ({ isOpened, setIsOpen, title, icon, counter, actions, className }) => {
|
|
16
17
|
return (
|
|
17
18
|
<div className={clsx(styles["test-result-dropdown"], className)} onClick={() => setIsOpen(!isOpened)}>
|
|
18
19
|
<ArrowButton isOpened={isOpened} icon={allureIcons.arrowsChevronDown} />
|
|
@@ -20,6 +21,11 @@ export const TrDropdown: FunctionalComponent<{
|
|
|
20
21
|
<SvgIcon id={icon} />
|
|
21
22
|
<Text bold>{title}</Text>
|
|
22
23
|
<Counter count={counter} size="s" />
|
|
24
|
+
{actions ? (
|
|
25
|
+
<div className={styles["test-result-dropdown-actions"]} onClick={(event) => event.stopPropagation()}>
|
|
26
|
+
{actions}
|
|
27
|
+
</div>
|
|
28
|
+
) : null}
|
|
23
29
|
</div>
|
|
24
30
|
</div>
|
|
25
31
|
);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
.test-result-breadcrumbs {
|
|
2
2
|
margin-right: auto;
|
|
3
3
|
display: flex;
|
|
4
|
+
flex: 1 1 auto;
|
|
5
|
+
min-width: 0;
|
|
4
6
|
gap: 4px;
|
|
5
7
|
overflow: hidden;
|
|
6
8
|
white-space: nowrap;
|
|
@@ -16,6 +18,7 @@
|
|
|
16
18
|
|
|
17
19
|
.test-result-breadcrumb {
|
|
18
20
|
display: flex;
|
|
21
|
+
min-width: 0;
|
|
19
22
|
gap: 4px;
|
|
20
23
|
color: var(--on-text-secondary);
|
|
21
24
|
white-space: nowrap;
|
|
@@ -28,11 +28,12 @@ export const TrRetriesItem: FunctionalComponent<TrRetriesItemProps> = ({ testRes
|
|
|
28
28
|
const retryTitle = convertedStop ? `${retryTitlePrefix} – ${convertedStop}` : retryTitlePrefix;
|
|
29
29
|
|
|
30
30
|
const formattedDuration = typeof duration === "number" ? formatDuration(duration) : undefined;
|
|
31
|
+
const hasErrorDetails = Boolean(error?.trace || error?.message);
|
|
31
32
|
|
|
32
33
|
return (
|
|
33
34
|
<div data-testid="test-result-retries-item">
|
|
34
35
|
<div className={styles["test-result-retries-item-header"]} onClick={() => setIsOpen(!isOpened)}>
|
|
35
|
-
{
|
|
36
|
+
{hasErrorDetails && (
|
|
36
37
|
<ArrowButton
|
|
37
38
|
data-testid="test-result-retries-item-arrow-button"
|
|
38
39
|
isOpened={isOpened}
|
|
@@ -61,9 +62,9 @@ export const TrRetriesItem: FunctionalComponent<TrRetriesItemProps> = ({ testRes
|
|
|
61
62
|
</div>
|
|
62
63
|
</div>
|
|
63
64
|
</div>
|
|
64
|
-
{isOpened &&
|
|
65
|
+
{isOpened && hasErrorDetails && (
|
|
65
66
|
<div className={styles["test-result-retries-item-content"]}>
|
|
66
|
-
<TrError {...error} status={status} />
|
|
67
|
+
<TrError {...(error ?? {})} status={status} />
|
|
67
68
|
</div>
|
|
68
69
|
)}
|
|
69
70
|
</div>
|
|
@@ -10,13 +10,13 @@ import * as styles from "./styles.scss";
|
|
|
10
10
|
export const TrRetriesView: FunctionalComponent<{
|
|
11
11
|
testResult: AwesomeTestResult;
|
|
12
12
|
}> = ({ testResult }) => {
|
|
13
|
-
const
|
|
13
|
+
const retries = testResult?.retries ?? [];
|
|
14
14
|
const { t } = useI18n("empty");
|
|
15
15
|
|
|
16
16
|
return (
|
|
17
17
|
<div className={styles["test-result-retries"]}>
|
|
18
18
|
{retries.length ? (
|
|
19
|
-
retries
|
|
19
|
+
retries.map((item, key) => (
|
|
20
20
|
<TrRetriesItem
|
|
21
21
|
testResultItem={item as unknown as AwesomeTestResult}
|
|
22
22
|
key={key}
|
|
@@ -3,21 +3,14 @@ import type { FunctionalComponent } from "preact";
|
|
|
3
3
|
import { useState } from "preact/hooks";
|
|
4
4
|
import type { AwesomeTestResult } from "types";
|
|
5
5
|
|
|
6
|
+
import { fixtureResultToTrStepItem } from "@/components/TestResult/bodyItems";
|
|
6
7
|
import { TrDropdown } from "@/components/TestResult/TrDropdown";
|
|
7
|
-
import { TrAttachment } from "@/components/TestResult/TrSteps/TrAttachment";
|
|
8
8
|
import { TrStep } from "@/components/TestResult/TrSteps/TrStep";
|
|
9
9
|
import { useI18n } from "@/stores/locale";
|
|
10
10
|
import { collapsedTrees, toggleTree } from "@/stores/tree";
|
|
11
11
|
|
|
12
12
|
import * as styles from "@/components/TestResult/TrSteps/styles.scss";
|
|
13
13
|
|
|
14
|
-
const typeMap = {
|
|
15
|
-
before: TrStep,
|
|
16
|
-
after: TrStep,
|
|
17
|
-
step: TrStep,
|
|
18
|
-
attachment: TrAttachment,
|
|
19
|
-
};
|
|
20
|
-
|
|
21
14
|
export type TrSetupProps = {
|
|
22
15
|
setup: AwesomeTestResult["setup"];
|
|
23
16
|
id?: string;
|
|
@@ -45,14 +38,11 @@ export const TrSetup: FunctionalComponent<TrSetupProps> = ({ setup, id }) => {
|
|
|
45
38
|
/>
|
|
46
39
|
{isOpened && (
|
|
47
40
|
<div className={styles["test-result-steps-root"]}>
|
|
48
|
-
{setup?.map((
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<StepComponent item={item} stepIndex={key + 1} key={key} className={styles["test-result-step-root"]} />
|
|
54
|
-
) : null;
|
|
55
|
-
})}
|
|
41
|
+
{setup?.map((fixture, key) => (
|
|
42
|
+
<div className={styles["test-result-step-root"]} key={fixture.id}>
|
|
43
|
+
<TrStep item={fixtureResultToTrStepItem(fixture)} stepIndex={key + 1} />
|
|
44
|
+
</div>
|
|
45
|
+
))}
|
|
56
46
|
</div>
|
|
57
47
|
)}
|
|
58
48
|
</div>
|
|
@@ -8,7 +8,7 @@ import { useState } from "preact/hooks";
|
|
|
8
8
|
import { TrAttachmentInfo } from "@/components/TestResult/TrSteps/TrAttachmentInfo";
|
|
9
9
|
import { useI18n } from "@/stores";
|
|
10
10
|
import { openModal } from "@/stores/modal";
|
|
11
|
-
import {
|
|
11
|
+
import { isTreeOpened, toggleTree } from "@/stores/tree";
|
|
12
12
|
|
|
13
13
|
import * as styles from "@/components/TestResult/TrSteps/styles.scss";
|
|
14
14
|
|
|
@@ -62,7 +62,7 @@ export const TrAttachment: FunctionComponent<{
|
|
|
62
62
|
className?: string;
|
|
63
63
|
}> = ({ item, stepIndex }) => {
|
|
64
64
|
const attachmentTreeId = item.link?.id !== null ? `attachment-${item.link.id}` : null;
|
|
65
|
-
const isOpened =
|
|
65
|
+
const isOpened = attachmentTreeId !== null ? isTreeOpened(attachmentTreeId, false) : false;
|
|
66
66
|
const [showPreview, setShowPreview] = useState(false);
|
|
67
67
|
const [highlightCode, setHighlightCode] = useState(true);
|
|
68
68
|
const { t: tAttachments } = useI18n("attachments");
|
|
@@ -100,7 +100,7 @@ export const TrAttachment: FunctionComponent<{
|
|
|
100
100
|
onClick={(e) => {
|
|
101
101
|
e.stopPropagation();
|
|
102
102
|
if (attachmentTreeId !== null) {
|
|
103
|
-
toggleTree(attachmentTreeId);
|
|
103
|
+
toggleTree(attachmentTreeId, false);
|
|
104
104
|
}
|
|
105
105
|
}}
|
|
106
106
|
>
|
|
@@ -3,8 +3,12 @@ import type { FunctionComponent } from "preact";
|
|
|
3
3
|
|
|
4
4
|
import { hasTestLevelErrorContent, type TestLevelErrorItem } from "@/components/TestResult/bodyItems";
|
|
5
5
|
import { TrError } from "@/components/TestResult/TrError";
|
|
6
|
+
import {
|
|
7
|
+
getStepTreeExpansionPolicy,
|
|
8
|
+
isOpenByDefaultForPolicy,
|
|
9
|
+
} from "@/components/TestResult/TrSteps/stepTreeExpansion";
|
|
6
10
|
import { TrStepHeader } from "@/components/TestResult/TrSteps/TrStepHeader";
|
|
7
|
-
import {
|
|
11
|
+
import { isTreeOpened, toggleTree } from "@/stores/tree";
|
|
8
12
|
|
|
9
13
|
import * as styles from "@/components/TestResult/TrSteps/styles.scss";
|
|
10
14
|
|
|
@@ -14,8 +18,10 @@ export type TrErrorStepProps = {
|
|
|
14
18
|
};
|
|
15
19
|
|
|
16
20
|
export const TrErrorStep: FunctionComponent<TrErrorStepProps> = ({ item, stepIndex }) => {
|
|
17
|
-
const isOpened = !collapsedTrees.value.has(item.id);
|
|
18
21
|
const hasContent = hasTestLevelErrorContent(item.error);
|
|
22
|
+
const policy = getStepTreeExpansionPolicy();
|
|
23
|
+
const openedByDefault = isOpenByDefaultForPolicy(policy, item.status === "failed" || item.status === "broken");
|
|
24
|
+
const isOpened = isTreeOpened(item.id, openedByDefault);
|
|
19
25
|
|
|
20
26
|
return (
|
|
21
27
|
<div data-testid="test-result-step" className={styles["test-result-step"]}>
|
|
@@ -25,7 +31,7 @@ export const TrErrorStep: FunctionComponent<TrErrorStepProps> = ({ item, stepInd
|
|
|
25
31
|
stepIndex={stepIndex}
|
|
26
32
|
isOpened={isOpened}
|
|
27
33
|
hasContent={hasContent}
|
|
28
|
-
onToggle={() => toggleTree(item.id)}
|
|
34
|
+
onToggle={() => toggleTree(item.id, openedByDefault)}
|
|
29
35
|
/>
|
|
30
36
|
{isOpened && hasContent && (
|
|
31
37
|
<div
|
|
@@ -1,16 +1,36 @@
|
|
|
1
|
+
import { IconButton, allureIcons } from "@allurereport/web-components";
|
|
1
2
|
import type { FunctionComponent } from "preact";
|
|
3
|
+
import { useState } from "preact/hooks";
|
|
2
4
|
|
|
3
5
|
import { MetadataList } from "@/components/Metadata";
|
|
4
6
|
import { type MetadataItem } from "@/components/ReportMetadata";
|
|
5
|
-
import type
|
|
7
|
+
import { hasErrorDiff, type TrStepItem } from "@/components/TestResult/bodyItems";
|
|
6
8
|
import { TrError } from "@/components/TestResult/TrError";
|
|
9
|
+
import {
|
|
10
|
+
collectExpandableStepNodes,
|
|
11
|
+
hasStepContent,
|
|
12
|
+
getStepTreeExpansionPolicy,
|
|
13
|
+
getNextSubtreeToggleState,
|
|
14
|
+
getSubtreeToggleIcon,
|
|
15
|
+
isSubtreeFirstLevelOnlyOpened,
|
|
16
|
+
isStepOpenedByDefault,
|
|
17
|
+
type SubtreeNode,
|
|
18
|
+
type SubtreeToggleState,
|
|
19
|
+
} from "@/components/TestResult/TrSteps/stepTreeExpansion";
|
|
7
20
|
import { TrBodyItems } from "@/components/TestResult/TrSteps/TrBodyItems";
|
|
8
21
|
import { TrStepHeader } from "@/components/TestResult/TrSteps/TrStepHeader";
|
|
9
22
|
import { TrStepInfo } from "@/components/TestResult/TrSteps/TrStepInfo";
|
|
10
|
-
import {
|
|
23
|
+
import { isTreeOpened, setTreeOpened, toggleTree } from "@/stores/tree";
|
|
11
24
|
|
|
12
25
|
import * as styles from "@/components/TestResult/TrSteps/styles.scss";
|
|
13
26
|
|
|
27
|
+
const iconBySubtreeState = {
|
|
28
|
+
"single-down": allureIcons.lineArrowsChevronDown,
|
|
29
|
+
"single-up": allureIcons.lineArrowsChevronUp,
|
|
30
|
+
"double-down": allureIcons.lineArrowsChevronDownDouble,
|
|
31
|
+
"double-up": allureIcons.lineArrowsChevronUpDouble,
|
|
32
|
+
} as const;
|
|
33
|
+
|
|
14
34
|
export const TrStepParameters = (props: { parameters: TrStepItem["item"]["parameters"] }) => {
|
|
15
35
|
const { parameters } = props;
|
|
16
36
|
|
|
@@ -23,14 +43,22 @@ export const TrStepParameters = (props: { parameters: TrStepItem["item"]["parame
|
|
|
23
43
|
|
|
24
44
|
export const TrStepsContent = (props: { item: TrStepItem }) => {
|
|
25
45
|
const { item: stepData, bodyItems, suppressInlineError } = props.item;
|
|
46
|
+
const inlineError = {
|
|
47
|
+
message: stepData.message ?? stepData.error?.message,
|
|
48
|
+
trace: stepData.trace ?? stepData.error?.trace,
|
|
49
|
+
actual: stepData.error?.actual,
|
|
50
|
+
expected: stepData.error?.expected,
|
|
51
|
+
};
|
|
26
52
|
const hasInlineError = Boolean(
|
|
27
|
-
(
|
|
53
|
+
(inlineError.message || inlineError.trace || hasErrorDiff(inlineError)) &&
|
|
54
|
+
!stepData.hasSimilarErrorInSubSteps &&
|
|
55
|
+
!suppressInlineError,
|
|
28
56
|
);
|
|
29
57
|
|
|
30
58
|
return (
|
|
31
59
|
<div data-testid={"test-result-step-content"} className={styles["test-result-step-content"]}>
|
|
32
60
|
{Boolean(stepData.parameters?.length) && <TrStepParameters parameters={stepData.parameters} />}
|
|
33
|
-
{hasInlineError && <TrError {...stepData} />}
|
|
61
|
+
{hasInlineError && <TrError {...inlineError} status={stepData.status} />}
|
|
34
62
|
{Boolean(bodyItems.length) && <TrBodyItems bodyItems={bodyItems} />}
|
|
35
63
|
</div>
|
|
36
64
|
);
|
|
@@ -41,11 +69,76 @@ export const TrStep: FunctionComponent<{
|
|
|
41
69
|
stepIndex?: number;
|
|
42
70
|
}> = ({ item, stepIndex }) => {
|
|
43
71
|
const { item: stepData, bodyItems, suppressInlineError } = item;
|
|
72
|
+
const inlineError = {
|
|
73
|
+
message: stepData.message ?? stepData.error?.message,
|
|
74
|
+
trace: stepData.trace ?? stepData.error?.trace,
|
|
75
|
+
actual: stepData.error?.actual,
|
|
76
|
+
expected: stepData.error?.expected,
|
|
77
|
+
};
|
|
44
78
|
const hasInlineError = Boolean(
|
|
45
|
-
(
|
|
79
|
+
(inlineError.message || inlineError.trace || hasErrorDiff(inlineError)) &&
|
|
80
|
+
!stepData.hasSimilarErrorInSubSteps &&
|
|
81
|
+
!suppressInlineError,
|
|
82
|
+
);
|
|
83
|
+
const policy = getStepTreeExpansionPolicy();
|
|
84
|
+
const hasContent = hasStepContent(item);
|
|
85
|
+
const openedByDefault = isStepOpenedByDefault(policy, stepData.status, bodyItems);
|
|
86
|
+
const isOpened = isTreeOpened(stepData.stepId, openedByDefault);
|
|
87
|
+
const expandableDescendantNodes = collectExpandableStepNodes(bodyItems, policy);
|
|
88
|
+
const hasExpandableDescendants = expandableDescendantNodes.length > 0;
|
|
89
|
+
const subtreeNodes: SubtreeNode[] = hasExpandableDescendants
|
|
90
|
+
? [
|
|
91
|
+
{ id: stepData.stepId, openedByDefault, isRoot: true },
|
|
92
|
+
...expandableDescendantNodes.map((node) => ({ ...node, isRoot: false })),
|
|
93
|
+
]
|
|
94
|
+
: [];
|
|
95
|
+
const [lastSubtreeToggle, setLastSubtreeToggle] = useState<SubtreeToggleState | null>(null);
|
|
96
|
+
const isRootSubtreeOpened = isTreeOpened(stepData.stepId, openedByDefault);
|
|
97
|
+
const isSubtreeCollapsedAll = !isRootSubtreeOpened;
|
|
98
|
+
const isSubtreeFirstLevelOnly = isSubtreeFirstLevelOnlyOpened(
|
|
99
|
+
stepData.stepId,
|
|
100
|
+
openedByDefault,
|
|
101
|
+
subtreeNodes,
|
|
102
|
+
isTreeOpened,
|
|
46
103
|
);
|
|
47
|
-
const
|
|
48
|
-
|
|
104
|
+
const isSubtreeExpandedAll =
|
|
105
|
+
hasExpandableDescendants && subtreeNodes.every((node) => isTreeOpened(node.id, node.openedByDefault));
|
|
106
|
+
const hasOnlyLeafResults = hasExpandableDescendants && subtreeNodes.every((node) => node.isRoot);
|
|
107
|
+
const subtreeToggleIcon =
|
|
108
|
+
iconBySubtreeState[
|
|
109
|
+
getSubtreeToggleIcon({
|
|
110
|
+
hasOnlyLeafResults,
|
|
111
|
+
isSubtreeCollapsedAll,
|
|
112
|
+
isSubtreeFirstLevelOnly,
|
|
113
|
+
})
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
const setSubtreeState = (state: SubtreeToggleState) => {
|
|
117
|
+
subtreeNodes.forEach((node) => {
|
|
118
|
+
const shouldOpenSubtree = state === "all" ? true : state === "first" ? node.isRoot : false;
|
|
119
|
+
setTreeOpened(node.id, shouldOpenSubtree, node.openedByDefault);
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const toggleSubtree = (event: MouseEvent) => {
|
|
124
|
+
event.stopPropagation();
|
|
125
|
+
const nextState = getNextSubtreeToggleState({
|
|
126
|
+
hasOnlyLeafResults,
|
|
127
|
+
isSubtreeCollapsedAll,
|
|
128
|
+
isSubtreeFirstLevelOnly,
|
|
129
|
+
isSubtreeExpandedAll,
|
|
130
|
+
lastSubtreeToggle,
|
|
131
|
+
});
|
|
132
|
+
setSubtreeState(nextState);
|
|
133
|
+
if (nextState !== "first") {
|
|
134
|
+
setLastSubtreeToggle(nextState);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const toggleStep = () => {
|
|
139
|
+
setLastSubtreeToggle(null);
|
|
140
|
+
toggleTree(stepData.stepId, openedByDefault);
|
|
141
|
+
};
|
|
49
142
|
|
|
50
143
|
return (
|
|
51
144
|
<div data-testid={"test-result-step"} className={styles["test-result-step"]}>
|
|
@@ -55,7 +148,19 @@ export const TrStep: FunctionComponent<{
|
|
|
55
148
|
stepIndex={stepIndex}
|
|
56
149
|
isOpened={isOpened}
|
|
57
150
|
hasContent={hasContent}
|
|
58
|
-
onToggle={
|
|
151
|
+
onToggle={toggleStep}
|
|
152
|
+
subtreeToggle={
|
|
153
|
+
hasExpandableDescendants ? (
|
|
154
|
+
<IconButton
|
|
155
|
+
style="ghost"
|
|
156
|
+
size="xs"
|
|
157
|
+
icon={subtreeToggleIcon}
|
|
158
|
+
onClick={toggleSubtree}
|
|
159
|
+
data-testid="test-result-step-subtree-toggle"
|
|
160
|
+
className={styles["test-result-step-subtree-toggle"]}
|
|
161
|
+
/>
|
|
162
|
+
) : null
|
|
163
|
+
}
|
|
59
164
|
extra={<TrStepInfo item={stepData} />}
|
|
60
165
|
/>
|
|
61
166
|
{hasContent && isOpened && <TrStepsContent item={item} />}
|
|
@@ -12,6 +12,7 @@ export type TrStepHeaderProps = {
|
|
|
12
12
|
hasContent: boolean;
|
|
13
13
|
onToggle: () => void;
|
|
14
14
|
extra?: preact.ComponentChildren;
|
|
15
|
+
subtreeToggle?: preact.ComponentChildren;
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
export const TrStepHeader: FunctionComponent<TrStepHeaderProps> = ({
|
|
@@ -22,6 +23,7 @@ export const TrStepHeader: FunctionComponent<TrStepHeaderProps> = ({
|
|
|
22
23
|
hasContent,
|
|
23
24
|
onToggle,
|
|
24
25
|
extra,
|
|
26
|
+
subtreeToggle,
|
|
25
27
|
}) => (
|
|
26
28
|
<div
|
|
27
29
|
data-testid="test-result-step-header"
|
|
@@ -45,6 +47,7 @@ export const TrStepHeader: FunctionComponent<TrStepHeaderProps> = ({
|
|
|
45
47
|
<Text data-testid="test-result-step-title" className={styles["test-result-header-text"]}>
|
|
46
48
|
{title}
|
|
47
49
|
</Text>
|
|
50
|
+
{subtreeToggle}
|
|
48
51
|
{extra}
|
|
49
52
|
</div>
|
|
50
53
|
);
|
|
@@ -1,22 +1,96 @@
|
|
|
1
|
-
import { allureIcons } from "@allurereport/web-components";
|
|
1
|
+
import { IconButton, allureIcons } from "@allurereport/web-components";
|
|
2
2
|
import type { FunctionalComponent } from "preact";
|
|
3
|
+
import { useState } from "preact/hooks";
|
|
3
4
|
|
|
4
5
|
import type { TrBodyItem } from "@/components/TestResult/bodyItems";
|
|
5
6
|
import { TrDropdown } from "@/components/TestResult/TrDropdown";
|
|
7
|
+
import {
|
|
8
|
+
collectExpandableStepNodes,
|
|
9
|
+
getNextSubtreeToggleState,
|
|
10
|
+
getSubtreeToggleIcon,
|
|
11
|
+
getStepTreeExpansionPolicy,
|
|
12
|
+
hasFailedStepContext,
|
|
13
|
+
isSubtreeFirstLevelOnlyOpened,
|
|
14
|
+
isOpenByDefaultForPolicy,
|
|
15
|
+
type SubtreeNode,
|
|
16
|
+
type SubtreeToggleState,
|
|
17
|
+
} from "@/components/TestResult/TrSteps/stepTreeExpansion";
|
|
6
18
|
import { TrBodyItems } from "@/components/TestResult/TrSteps/TrBodyItems";
|
|
7
19
|
import { useI18n } from "@/stores/locale";
|
|
8
|
-
import {
|
|
20
|
+
import { isTreeOpened, setTreeOpened, toggleTree } from "@/stores/tree";
|
|
9
21
|
|
|
10
22
|
import * as styles from "./styles.scss";
|
|
11
23
|
|
|
24
|
+
const iconBySubtreeState = {
|
|
25
|
+
"single-down": allureIcons.lineArrowsChevronDown,
|
|
26
|
+
"single-up": allureIcons.lineArrowsChevronUp,
|
|
27
|
+
"double-down": allureIcons.lineArrowsChevronDownDouble,
|
|
28
|
+
"double-up": allureIcons.lineArrowsChevronUpDouble,
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
12
31
|
export type TrStepsProps = {
|
|
13
32
|
bodyItems: TrBodyItem[];
|
|
14
33
|
id?: string;
|
|
15
34
|
};
|
|
16
35
|
|
|
17
36
|
export const TrSteps: FunctionalComponent<TrStepsProps> = ({ bodyItems, id }) => {
|
|
18
|
-
const stepsId = id
|
|
19
|
-
const
|
|
37
|
+
const stepsId = typeof id === "string" ? `${id}-steps` : null;
|
|
38
|
+
const policy = getStepTreeExpansionPolicy();
|
|
39
|
+
const openedByDefault = isOpenByDefaultForPolicy(policy, hasFailedStepContext(bodyItems));
|
|
40
|
+
const isOpened = stepsId !== null ? isTreeOpened(stepsId, openedByDefault) : openedByDefault;
|
|
41
|
+
const expandableTreeNodes = collectExpandableStepNodes(bodyItems, policy);
|
|
42
|
+
const hasChildren = stepsId !== null && bodyItems.length > 0;
|
|
43
|
+
const subtreeNodes: SubtreeNode[] = hasChildren
|
|
44
|
+
? [
|
|
45
|
+
{ id: stepsId, openedByDefault, isRoot: true },
|
|
46
|
+
...expandableTreeNodes.map((node) => ({ ...node, isRoot: false })),
|
|
47
|
+
]
|
|
48
|
+
: [];
|
|
49
|
+
const [lastSubtreeToggle, setLastSubtreeToggle] = useState<SubtreeToggleState | null>(null);
|
|
50
|
+
const isRootSubtreeOpened = stepsId !== null ? isTreeOpened(stepsId, openedByDefault) : false;
|
|
51
|
+
const isSubtreeCollapsedAll = !isRootSubtreeOpened;
|
|
52
|
+
const isSubtreeFirstLevelOnly =
|
|
53
|
+
stepsId !== null ? isSubtreeFirstLevelOnlyOpened(stepsId, openedByDefault, subtreeNodes, isTreeOpened) : false;
|
|
54
|
+
const isSubtreeExpandedAll = hasChildren && subtreeNodes.every((node) => isTreeOpened(node.id, node.openedByDefault));
|
|
55
|
+
const hasOnlyLeafResults = hasChildren && subtreeNodes.every((node) => node.isRoot);
|
|
56
|
+
const subtreeToggleIcon =
|
|
57
|
+
iconBySubtreeState[
|
|
58
|
+
getSubtreeToggleIcon({
|
|
59
|
+
hasOnlyLeafResults,
|
|
60
|
+
isSubtreeCollapsedAll,
|
|
61
|
+
isSubtreeFirstLevelOnly,
|
|
62
|
+
})
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const setSubtreeState = (state: SubtreeToggleState) => {
|
|
66
|
+
subtreeNodes.forEach((node) => {
|
|
67
|
+
const shouldOpenSubtree = state === "all" ? true : state === "first" ? node.isRoot : false;
|
|
68
|
+
setTreeOpened(node.id, shouldOpenSubtree, node.openedByDefault);
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const handleToggleSubtree = (event: MouseEvent) => {
|
|
73
|
+
event.stopPropagation();
|
|
74
|
+
const nextState = getNextSubtreeToggleState({
|
|
75
|
+
hasOnlyLeafResults,
|
|
76
|
+
isSubtreeCollapsedAll,
|
|
77
|
+
isSubtreeFirstLevelOnly,
|
|
78
|
+
isSubtreeExpandedAll,
|
|
79
|
+
lastSubtreeToggle,
|
|
80
|
+
});
|
|
81
|
+
setSubtreeState(nextState);
|
|
82
|
+
if (nextState !== "first") {
|
|
83
|
+
setLastSubtreeToggle(nextState);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const toggleRoot = () => {
|
|
88
|
+
if (stepsId === null) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
setLastSubtreeToggle(null);
|
|
92
|
+
toggleTree(stepsId, openedByDefault);
|
|
93
|
+
};
|
|
20
94
|
|
|
21
95
|
const { t } = useI18n("execution");
|
|
22
96
|
return (
|
|
@@ -24,9 +98,20 @@ export const TrSteps: FunctionalComponent<TrStepsProps> = ({ bodyItems, id }) =>
|
|
|
24
98
|
<TrDropdown
|
|
25
99
|
icon={allureIcons.lineHelpersPlayCircle}
|
|
26
100
|
isOpened={isOpened}
|
|
27
|
-
setIsOpen={
|
|
101
|
+
setIsOpen={toggleRoot}
|
|
28
102
|
counter={bodyItems.length}
|
|
29
103
|
title={t("body")}
|
|
104
|
+
actions={
|
|
105
|
+
hasChildren ? (
|
|
106
|
+
<IconButton
|
|
107
|
+
style="ghost"
|
|
108
|
+
size="xs"
|
|
109
|
+
icon={subtreeToggleIcon}
|
|
110
|
+
onClick={handleToggleSubtree}
|
|
111
|
+
data-testid="test-result-steps-subtree-toggle"
|
|
112
|
+
/>
|
|
113
|
+
) : null
|
|
114
|
+
}
|
|
30
115
|
/>
|
|
31
116
|
{isOpened && (
|
|
32
117
|
<div data-testid="test-result-steps-root" className={styles["test-result-steps-root"]}>
|