fumadocs-test-reports 0.1.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/LICENSE +21 -0
- package/README.md +114 -0
- package/css/preset.css +300 -0
- package/dist/tunit/index.d.ts +3 -0
- package/dist/tunit/index.js +2 -0
- package/dist/tunit/parse.d.ts +9 -0
- package/dist/tunit/parse.js +180 -0
- package/dist/tunit/types.d.ts +121 -0
- package/dist/tunit/ui/index.d.ts +4 -0
- package/dist/tunit/ui/index.js +3 -0
- package/dist/tunit/ui/labels.d.ts +3 -0
- package/dist/tunit/ui/labels.js +93 -0
- package/dist/tunit/ui/presentation.js +55 -0
- package/dist/tunit/ui/report-link.d.ts +12 -0
- package/dist/tunit/ui/report-link.js +23 -0
- package/dist/tunit/ui/report.d.ts +13 -0
- package/dist/tunit/ui/report.js +265 -0
- package/dist/tunit/ui/trace-timeline.js +81 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +2 -0
- package/dist/ui/primitives.d.ts +32 -0
- package/dist/ui/primitives.js +48 -0
- package/package.json +52 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Duration, StdoutPanel } from "../../ui/primitives.js";
|
|
2
|
+
import { getLabels } from "./labels.js";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Accordion, Accordions } from "fumadocs-ui/components/accordion";
|
|
5
|
+
//#region src/tunit/ui/trace-timeline.tsx
|
|
6
|
+
function TUnitTraceTimeline({ spans, locale }) {
|
|
7
|
+
const labels = getLabels(locale);
|
|
8
|
+
const origin = Math.min(...spans.map((span) => span.startTimeMs));
|
|
9
|
+
const extent = Math.max(...spans.map((span) => span.startTimeMs + span.durationMs)) - origin || 1;
|
|
10
|
+
const byId = new Map(spans.map((span) => [`${span.traceId}:${span.spanId}`, span]));
|
|
11
|
+
function depth(span) {
|
|
12
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13
|
+
let current = span;
|
|
14
|
+
let level = 0;
|
|
15
|
+
while (current?.parentSpanId && level < 8) {
|
|
16
|
+
const key = `${current.traceId}:${current.parentSpanId}`;
|
|
17
|
+
if (seen.has(key)) break;
|
|
18
|
+
seen.add(key);
|
|
19
|
+
current = byId.get(key);
|
|
20
|
+
if (current) level++;
|
|
21
|
+
}
|
|
22
|
+
return level;
|
|
23
|
+
}
|
|
24
|
+
return /* @__PURE__ */ jsx(Accordions, {
|
|
25
|
+
className: "ftr-traces",
|
|
26
|
+
children: spans.toSorted((a, b) => a.startTimeMs - b.startTimeMs).map((span) => /* @__PURE__ */ jsxs(Accordion, {
|
|
27
|
+
className: "ftr-span",
|
|
28
|
+
value: `${span.traceId}:${span.spanId}`,
|
|
29
|
+
title: /* @__PURE__ */ jsxs("span", {
|
|
30
|
+
className: "ftr-span-heading",
|
|
31
|
+
children: [
|
|
32
|
+
/* @__PURE__ */ jsx("span", {
|
|
33
|
+
style: { paddingInlineStart: `${depth(span) * 12}px` },
|
|
34
|
+
children: span.name
|
|
35
|
+
}),
|
|
36
|
+
/* @__PURE__ */ jsx("small", { children: /* @__PURE__ */ jsx(Duration, {
|
|
37
|
+
value: span.durationMs,
|
|
38
|
+
unknownLabel: labels.unknown
|
|
39
|
+
}) }),
|
|
40
|
+
/* @__PURE__ */ jsx("span", {
|
|
41
|
+
className: "ftr-track",
|
|
42
|
+
"aria-hidden": "true",
|
|
43
|
+
children: /* @__PURE__ */ jsx("span", { style: {
|
|
44
|
+
marginInlineStart: `${(span.startTimeMs - origin) / extent * 100}%`,
|
|
45
|
+
width: `${span.durationMs / extent * 100}%`
|
|
46
|
+
} })
|
|
47
|
+
})
|
|
48
|
+
]
|
|
49
|
+
}),
|
|
50
|
+
children: [
|
|
51
|
+
/* @__PURE__ */ jsxs("dl", {
|
|
52
|
+
className: "ftr-fields",
|
|
53
|
+
children: [
|
|
54
|
+
/* @__PURE__ */ jsx("dt", { children: "Trace ID" }),
|
|
55
|
+
/* @__PURE__ */ jsx("dd", { children: span.traceId }),
|
|
56
|
+
/* @__PURE__ */ jsx("dt", { children: "Span ID" }),
|
|
57
|
+
/* @__PURE__ */ jsx("dd", { children: span.spanId }),
|
|
58
|
+
span.parentSpanId && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("dt", { children: "Parent ID" }), /* @__PURE__ */ jsx("dd", { children: span.parentSpanId })] }),
|
|
59
|
+
span.source && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("dt", { children: labels.traceSource }), /* @__PURE__ */ jsx("dd", { children: span.source })] }),
|
|
60
|
+
span.kind && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("dt", { children: "Kind" }), /* @__PURE__ */ jsx("dd", { children: span.kind })] }),
|
|
61
|
+
span.status && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("dt", { children: "Status" }), /* @__PURE__ */ jsxs("dd", { children: [span.status, span.statusMessage ? `: ${span.statusMessage}` : ""] })] }),
|
|
62
|
+
span.tags?.map((tag, i) => /* @__PURE__ */ jsxs("div", {
|
|
63
|
+
className: "ftr-field",
|
|
64
|
+
children: [/* @__PURE__ */ jsx("dt", { children: tag.key }), /* @__PURE__ */ jsx("dd", { children: tag.value })]
|
|
65
|
+
}, i))
|
|
66
|
+
]
|
|
67
|
+
}),
|
|
68
|
+
!!span.events?.length && /* @__PURE__ */ jsx(StdoutPanel, {
|
|
69
|
+
content: JSON.stringify(span.events, null, 2),
|
|
70
|
+
title: "Events"
|
|
71
|
+
}),
|
|
72
|
+
!!span.links?.length && /* @__PURE__ */ jsx(StdoutPanel, {
|
|
73
|
+
content: JSON.stringify(span.links, null, 2),
|
|
74
|
+
title: "Links"
|
|
75
|
+
})
|
|
76
|
+
]
|
|
77
|
+
}, `${span.traceId}:${span.spanId}`))
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { TUnitTraceTimeline };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { BadgeTone, Duration, DurationProps, ErrorDetail, ErrorDetailProps, StatusBadge, StatusBadgeProps, StdoutPanel, StdoutPanelProps } from "./primitives.js";
|
|
2
|
+
export { type BadgeTone, Duration, type DurationProps, ErrorDetail, type ErrorDetailProps, StatusBadge, type StatusBadgeProps, StdoutPanel, type StdoutPanelProps };
|
package/dist/ui/index.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/ui/primitives.d.ts
|
|
3
|
+
/** Visual tone only; the presenter owns the meaning of the framework's status. */
|
|
4
|
+
export type BadgeTone = 'success' | 'danger' | 'neutral' | 'info';
|
|
5
|
+
export interface StatusBadgeProps {
|
|
6
|
+
status: string;
|
|
7
|
+
tone?: BadgeTone;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function StatusBadge({ status, tone, className }: StatusBadgeProps): import("react").JSX.Element;
|
|
11
|
+
export interface DurationProps {
|
|
12
|
+
/** Milliseconds. Missing or null means unknown; zero is a known zero. */
|
|
13
|
+
value?: number | null;
|
|
14
|
+
unknownLabel?: string;
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function Duration({ value, unknownLabel, className }: DurationProps): import("react").JSX.Element;
|
|
18
|
+
export interface ErrorDetailProps {
|
|
19
|
+
message?: string | null;
|
|
20
|
+
type?: string | null;
|
|
21
|
+
stack?: string | null;
|
|
22
|
+
children?: ReactNode;
|
|
23
|
+
}
|
|
24
|
+
export declare function ErrorDetail({ message, type, stack, children }: ErrorDetailProps): import("react").JSX.Element;
|
|
25
|
+
export interface StdoutPanelProps {
|
|
26
|
+
content?: string | null;
|
|
27
|
+
title?: string;
|
|
28
|
+
emptyLabel?: string;
|
|
29
|
+
}
|
|
30
|
+
/** Plain text only, suitable for stdout or stderr. */
|
|
31
|
+
export declare function StdoutPanel({ content, title, emptyLabel }: StdoutPanelProps): import("react").JSX.Element;
|
|
32
|
+
//#endregion
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Callout } from "fumadocs-ui/components/callout";
|
|
2
|
+
import { CodeBlock, Pre } from "fumadocs-ui/components/codeblock";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/ui/primitives.tsx
|
|
5
|
+
function StatusBadge({ status, tone = "neutral", className = "" }) {
|
|
6
|
+
return /* @__PURE__ */ jsx("span", {
|
|
7
|
+
className: `ftr-ui ftr-badge ftr-tone-${tone} ${className}`,
|
|
8
|
+
children: status
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
function Duration({ value, unknownLabel = "Unknown", className }) {
|
|
12
|
+
const text = value == null ? unknownLabel : value >= 1e3 ? `${(value / 1e3).toFixed(2)} s` : `${value.toFixed(1)} ms`;
|
|
13
|
+
return /* @__PURE__ */ jsx("span", {
|
|
14
|
+
className,
|
|
15
|
+
children: text
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function ErrorDetail({ message, type, stack, children }) {
|
|
19
|
+
return /* @__PURE__ */ jsxs(Callout, {
|
|
20
|
+
type: "error",
|
|
21
|
+
title: type ?? void 0,
|
|
22
|
+
className: "ftr-ui ftr-error-details",
|
|
23
|
+
children: [
|
|
24
|
+
message != null && /* @__PURE__ */ jsx("p", {
|
|
25
|
+
className: "ftr-error-message",
|
|
26
|
+
children: message
|
|
27
|
+
}),
|
|
28
|
+
stack != null && /* @__PURE__ */ jsx(StdoutPanel, { content: stack }),
|
|
29
|
+
children
|
|
30
|
+
]
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** Plain text only, suitable for stdout or stderr. */
|
|
34
|
+
function StdoutPanel({ content, title, emptyLabel = "No output recorded." }) {
|
|
35
|
+
return /* @__PURE__ */ jsx("div", {
|
|
36
|
+
className: "ftr-ui ftr-output-panel",
|
|
37
|
+
children: content == null ? /* @__PURE__ */ jsx("p", {
|
|
38
|
+
className: "ftr-empty",
|
|
39
|
+
children: emptyLabel
|
|
40
|
+
}) : /* @__PURE__ */ jsx(CodeBlock, {
|
|
41
|
+
title,
|
|
42
|
+
className: "ftr-codeblock",
|
|
43
|
+
children: /* @__PURE__ */ jsx(Pre, { children: /* @__PURE__ */ jsx("code", { children: content }) })
|
|
44
|
+
})
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { Duration, ErrorDetail, StatusBadge, StdoutPanel };
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fumadocs-test-reports",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Test report adapters and React UI for Fumadocs",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"fumadocs",
|
|
7
|
+
"react",
|
|
8
|
+
"test-report",
|
|
9
|
+
"tunit"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"css"
|
|
15
|
+
],
|
|
16
|
+
"type": "module",
|
|
17
|
+
"sideEffects": [
|
|
18
|
+
"./css/**/*.css"
|
|
19
|
+
],
|
|
20
|
+
"exports": {
|
|
21
|
+
"./tunit": "./dist/tunit/index.js",
|
|
22
|
+
"./tunit/ui": "./dist/tunit/ui/index.js",
|
|
23
|
+
"./ui": "./dist/ui/index.js",
|
|
24
|
+
"./css/*": "./css/*"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public",
|
|
28
|
+
"registry": "https://registry.npmjs.org/"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsdown",
|
|
32
|
+
"clean": "rimraf dist",
|
|
33
|
+
"dev": "tsdown --watch --clean false",
|
|
34
|
+
"prepack": "pnpm build",
|
|
35
|
+
"lint": "oxlint .",
|
|
36
|
+
"types:check": "tsc --noEmit"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "26.6.4",
|
|
40
|
+
"@types/react": "19.3.0",
|
|
41
|
+
"@types/react-dom": "19.3.0",
|
|
42
|
+
"fumadocs-core": "16.16.2",
|
|
43
|
+
"fumadocs-ui": "npm:@fumadocs/base-ui@16.16.2",
|
|
44
|
+
"react": "19.3.0",
|
|
45
|
+
"react-dom": "19.3.0",
|
|
46
|
+
"tsdown": "0.23.0"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"fumadocs-ui": "^16.16.2",
|
|
50
|
+
"react": "^19.2.0"
|
|
51
|
+
}
|
|
52
|
+
}
|