@flanksource/clicky-ui 0.2.12 → 0.2.13
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/components/Combobox.cjs +47 -33
- package/dist/components/Combobox.cjs.map +1 -1
- package/dist/components/Combobox.d.ts +12 -0
- package/dist/components/Combobox.d.ts.map +1 -1
- package/dist/components/Combobox.js +48 -34
- package/dist/components/Combobox.js.map +1 -1
- package/dist/components/IconButton.cjs +26 -0
- package/dist/components/IconButton.cjs.map +1 -0
- package/dist/components/IconButton.d.ts +25 -0
- package/dist/components/IconButton.d.ts.map +1 -0
- package/dist/components/IconButton.js +26 -0
- package/dist/components/IconButton.js.map +1 -0
- package/dist/components/SecretKeySelector.cjs +151 -0
- package/dist/components/SecretKeySelector.cjs.map +1 -0
- package/dist/components/SecretKeySelector.d.ts +47 -0
- package/dist/components/SecretKeySelector.d.ts.map +1 -0
- package/dist/components/SecretKeySelector.js +151 -0
- package/dist/components/SecretKeySelector.js.map +1 -0
- package/dist/components/WorkloadPicker.cjs +144 -0
- package/dist/components/WorkloadPicker.cjs.map +1 -0
- package/dist/components/WorkloadPicker.d.ts +52 -0
- package/dist/components/WorkloadPicker.d.ts.map +1 -0
- package/dist/components/WorkloadPicker.js +144 -0
- package/dist/components/WorkloadPicker.js.map +1 -0
- package/dist/components/icon-menu-picker.cjs +83 -112
- package/dist/components/icon-menu-picker.cjs.map +1 -1
- package/dist/components/icon-menu-picker.d.ts.map +1 -1
- package/dist/components/icon-menu-picker.js +85 -114
- package/dist/components/icon-menu-picker.js.map +1 -1
- package/dist/components.cjs +10 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.ts +3 -0
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +11 -1
- package/dist/components.js.map +1 -1
- package/dist/data/Clicky.cjs +1 -1
- package/dist/data/Clicky.cjs.map +1 -1
- package/dist/data/Clicky.js +1 -1
- package/dist/data/Clicky.js.map +1 -1
- package/dist/data/Version.cjs +42 -0
- package/dist/data/Version.cjs.map +1 -0
- package/dist/data/Version.d.ts +36 -0
- package/dist/data/Version.d.ts.map +1 -0
- package/dist/data/Version.js +42 -0
- package/dist/data/Version.js.map +1 -0
- package/dist/data/test-runner/TestDetailPanel.cjs +165 -0
- package/dist/data/test-runner/TestDetailPanel.cjs.map +1 -0
- package/dist/data/test-runner/TestDetailPanel.d.ts +7 -0
- package/dist/data/test-runner/TestDetailPanel.d.ts.map +1 -0
- package/dist/data/test-runner/TestDetailPanel.js +165 -0
- package/dist/data/test-runner/TestDetailPanel.js.map +1 -0
- package/dist/data/test-runner/TestFailureDetail.cjs +78 -0
- package/dist/data/test-runner/TestFailureDetail.cjs.map +1 -0
- package/dist/data/test-runner/TestFailureDetail.d.ts +14 -0
- package/dist/data/test-runner/TestFailureDetail.d.ts.map +1 -0
- package/dist/data/test-runner/TestFailureDetail.js +78 -0
- package/dist/data/test-runner/TestFailureDetail.js.map +1 -0
- package/dist/data/test-runner/TestFilterBar.cjs +108 -0
- package/dist/data/test-runner/TestFilterBar.cjs.map +1 -0
- package/dist/data/test-runner/TestFilterBar.d.ts +15 -0
- package/dist/data/test-runner/TestFilterBar.d.ts.map +1 -0
- package/dist/data/test-runner/TestFilterBar.js +108 -0
- package/dist/data/test-runner/TestFilterBar.js.map +1 -0
- package/dist/data/test-runner/TestRunSummary.cjs +81 -0
- package/dist/data/test-runner/TestRunSummary.cjs.map +1 -0
- package/dist/data/test-runner/TestRunSummary.d.ts +19 -0
- package/dist/data/test-runner/TestRunSummary.d.ts.map +1 -0
- package/dist/data/test-runner/TestRunSummary.js +81 -0
- package/dist/data/test-runner/TestRunSummary.js.map +1 -0
- package/dist/data/test-runner/TestRunner.cjs +143 -0
- package/dist/data/test-runner/TestRunner.cjs.map +1 -0
- package/dist/data/test-runner/TestRunner.d.ts +42 -0
- package/dist/data/test-runner/TestRunner.d.ts.map +1 -0
- package/dist/data/test-runner/TestRunner.fixtures.d.ts +26 -0
- package/dist/data/test-runner/TestRunner.fixtures.d.ts.map +1 -0
- package/dist/data/test-runner/TestRunner.js +143 -0
- package/dist/data/test-runner/TestRunner.js.map +1 -0
- package/dist/data/test-runner/TestTree.cjs +42 -0
- package/dist/data/test-runner/TestTree.cjs.map +1 -0
- package/dist/data/test-runner/TestTree.d.ts +9 -0
- package/dist/data/test-runner/TestTree.d.ts.map +1 -0
- package/dist/data/test-runner/TestTree.js +42 -0
- package/dist/data/test-runner/TestTree.js.map +1 -0
- package/dist/data/test-runner/TestTreeNode.cjs +99 -0
- package/dist/data/test-runner/TestTreeNode.cjs.map +1 -0
- package/dist/data/test-runner/TestTreeNode.d.ts +12 -0
- package/dist/data/test-runner/TestTreeNode.d.ts.map +1 -0
- package/dist/data/test-runner/TestTreeNode.js +99 -0
- package/dist/data/test-runner/TestTreeNode.js.map +1 -0
- package/dist/data/test-runner/adapter.cjs +57 -0
- package/dist/data/test-runner/adapter.cjs.map +1 -0
- package/dist/data/test-runner/adapter.d.ts +60 -0
- package/dist/data/test-runner/adapter.d.ts.map +1 -0
- package/dist/data/test-runner/adapter.js +57 -0
- package/dist/data/test-runner/adapter.js.map +1 -0
- package/dist/data/test-runner/context.cjs +15 -0
- package/dist/data/test-runner/context.cjs.map +1 -0
- package/dist/data/test-runner/context.d.ts +40 -0
- package/dist/data/test-runner/context.d.ts.map +1 -0
- package/dist/data/test-runner/context.js +15 -0
- package/dist/data/test-runner/context.js.map +1 -0
- package/dist/data/test-runner/filterState.cjs +47 -0
- package/dist/data/test-runner/filterState.cjs.map +1 -0
- package/dist/data/test-runner/filterState.d.ts +14 -0
- package/dist/data/test-runner/filterState.d.ts.map +1 -0
- package/dist/data/test-runner/filterState.js +47 -0
- package/dist/data/test-runner/filterState.js.map +1 -0
- package/dist/data/test-runner/frameworkIcon.cjs +27 -0
- package/dist/data/test-runner/frameworkIcon.cjs.map +1 -0
- package/dist/data/test-runner/frameworkIcon.d.ts +3 -0
- package/dist/data/test-runner/frameworkIcon.d.ts.map +1 -0
- package/dist/data/test-runner/frameworkIcon.js +27 -0
- package/dist/data/test-runner/frameworkIcon.js.map +1 -0
- package/dist/data/test-runner/index.d.ts +14 -0
- package/dist/data/test-runner/index.d.ts.map +1 -0
- package/dist/data/test-runner/status.cjs +234 -0
- package/dist/data/test-runner/status.cjs.map +1 -0
- package/dist/data/test-runner/status.d.ts +52 -0
- package/dist/data/test-runner/status.d.ts.map +1 -0
- package/dist/data/test-runner/status.js +234 -0
- package/dist/data/test-runner/status.js.map +1 -0
- package/dist/data/test-runner/types.d.ts +148 -0
- package/dist/data/test-runner/types.d.ts.map +1 -0
- package/dist/data/version-info.cjs +26 -0
- package/dist/data/version-info.cjs.map +1 -0
- package/dist/data/version-info.d.ts +11 -0
- package/dist/data/version-info.d.ts.map +1 -0
- package/dist/data/version-info.js +26 -0
- package/dist/data/version-info.js.map +1 -0
- package/dist/data.cjs +49 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +3 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +50 -1
- package/dist/data.js.map +1 -1
- package/dist/index.cjs +59 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +60 -1
- package/dist/index.js.map +1 -1
- package/dist/overlay/DropdownMenu.cjs +57 -30
- package/dist/overlay/DropdownMenu.cjs.map +1 -1
- package/dist/overlay/DropdownMenu.d.ts.map +1 -1
- package/dist/overlay/DropdownMenu.js +57 -30
- package/dist/overlay/DropdownMenu.js.map +1 -1
- package/dist/overlay/Modal.cjs +62 -6
- package/dist/overlay/Modal.cjs.map +1 -1
- package/dist/overlay/Modal.d.ts +19 -1
- package/dist/overlay/Modal.d.ts.map +1 -1
- package/dist/overlay/Modal.js +62 -6
- package/dist/overlay/Modal.js.map +1 -1
- package/dist/rpc/CommandForm.cjs +2 -2
- package/dist/rpc/CommandForm.cjs.map +1 -1
- package/dist/rpc/CommandForm.d.ts.map +1 -1
- package/dist/rpc/CommandForm.js +3 -3
- package/dist/rpc/CommandForm.js.map +1 -1
- package/dist/rpc/formMetadata.cjs +5 -4
- package/dist/rpc/formMetadata.cjs.map +1 -1
- package/dist/rpc/formMetadata.d.ts.map +1 -1
- package/dist/rpc/formMetadata.js +6 -5
- package/dist/rpc/formMetadata.js.map +1 -1
- package/dist/rpc/types.cjs +4 -0
- package/dist/rpc/types.cjs.map +1 -1
- package/dist/rpc/types.d.ts +10 -0
- package/dist/rpc/types.d.ts.map +1 -1
- package/dist/rpc/types.js +5 -1
- package/dist/rpc/types.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const react = require("react");
|
|
5
|
+
const utils = require("../lib/utils.cjs");
|
|
6
|
+
const Badge = require("./Badge.cjs");
|
|
7
|
+
const versionInfo = require("./version-info.cjs");
|
|
8
|
+
const MODE_BADGE = {
|
|
9
|
+
dev: { label: "dev", tone: "info" },
|
|
10
|
+
storybook: { label: "storybook", tone: "neutral" }
|
|
11
|
+
};
|
|
12
|
+
function formatDate(iso) {
|
|
13
|
+
if (!iso) return "";
|
|
14
|
+
const parsed = new Date(iso);
|
|
15
|
+
return Number.isNaN(parsed.getTime()) ? iso : parsed.toISOString();
|
|
16
|
+
}
|
|
17
|
+
const Version = react.forwardRef(
|
|
18
|
+
({ commit = true, tag = true, date = true, info, className, ...props }, ref) => {
|
|
19
|
+
const resolved = { ...versionInfo.getVersionInfo(), ...info };
|
|
20
|
+
const parts = [];
|
|
21
|
+
if (tag && resolved.tag) parts.push(resolved.tag);
|
|
22
|
+
if (commit && resolved.commit) parts.push(resolved.commit);
|
|
23
|
+
if (date && resolved.date) parts.push(formatDate(resolved.date));
|
|
24
|
+
const mode = resolved.mode !== "production" ? MODE_BADGE[resolved.mode] : null;
|
|
25
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
26
|
+
"span",
|
|
27
|
+
{
|
|
28
|
+
ref,
|
|
29
|
+
className: utils.cn("inline-flex items-center gap-1.5 font-mono text-xs text-muted-foreground", className),
|
|
30
|
+
...props,
|
|
31
|
+
children: [
|
|
32
|
+
parts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { children: parts.join(" · ") }),
|
|
33
|
+
resolved.dirty && /* @__PURE__ */ jsxRuntime.jsx(Badge.Badge, { tone: "warning", variant: "soft", size: "xs", children: "dirty" }),
|
|
34
|
+
mode && /* @__PURE__ */ jsxRuntime.jsx(Badge.Badge, { tone: mode.tone, variant: "soft", size: "xs", children: mode.label })
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
Version.displayName = "Version";
|
|
41
|
+
exports.Version = Version;
|
|
42
|
+
//# sourceMappingURL=Version.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Version.cjs","sources":["../../src/data/Version.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Badge, type BadgeTone } from \"./Badge\";\nimport { getVersionInfo, type RuntimeMode, type VersionInfo } from \"./version-info\";\n\nexport type VersionProps = HTMLAttributes<HTMLSpanElement> & {\n /** Show the short git commit hash. */\n commit?: boolean;\n /** Show the git tag. */\n tag?: boolean;\n /** Show the build date. */\n date?: boolean;\n /**\n * Override the resolved build/runtime info. Primarily for stories and tests;\n * when omitted the values injected at build time are used.\n */\n info?: Partial<VersionInfo>;\n};\n\nconst MODE_BADGE: Record<Exclude<RuntimeMode, \"production\">, { label: string; tone: BadgeTone }> = {\n dev: { label: \"dev\", tone: \"info\" },\n storybook: { label: \"storybook\", tone: \"neutral\" },\n};\n\nfunction formatDate(iso: string): string {\n if (!iso) return \"\";\n const parsed = new Date(iso);\n return Number.isNaN(parsed.getTime()) ? iso : parsed.toISOString();\n}\n\n/**\n * Renders clicky-ui build metadata: git commit, tag, and build date, plus a\n * status badge for a dirty working tree, Vite dev mode, or Storybook.\n *\n * Commit/tag/date/dirty are captured when the library is built (Vite `define`);\n * `dev` and `storybook` are detected at runtime.\n */\nexport const Version = forwardRef<HTMLSpanElement, VersionProps>(\n ({ commit = true, tag = true, date = true, info, className, ...props }, ref) => {\n const resolved = { ...getVersionInfo(), ...info };\n\n const parts: string[] = [];\n if (tag && resolved.tag) parts.push(resolved.tag);\n if (commit && resolved.commit) parts.push(resolved.commit);\n if (date && resolved.date) parts.push(formatDate(resolved.date));\n\n const mode = resolved.mode !== \"production\" ? MODE_BADGE[resolved.mode] : null;\n\n return (\n <span\n ref={ref}\n className={cn(\"inline-flex items-center gap-1.5 font-mono text-xs text-muted-foreground\", className)}\n {...props}\n >\n {parts.length > 0 && <span>{parts.join(\" · \")}</span>}\n {resolved.dirty && (\n <Badge tone=\"warning\" variant=\"soft\" size=\"xs\">\n dirty\n </Badge>\n )}\n {mode && (\n <Badge tone={mode.tone} variant=\"soft\" size=\"xs\">\n {mode.label}\n </Badge>\n )}\n </span>\n );\n },\n);\n\nVersion.displayName = \"Version\";\n"],"names":["forwardRef","getVersionInfo","jsxs","cn","jsx","Badge"],"mappings":";;;;;;;AAmBA,MAAM,aAA6F;AAAA,EACjG,KAAK,EAAE,OAAO,OAAO,MAAM,OAAA;AAAA,EAC3B,WAAW,EAAE,OAAO,aAAa,MAAM,UAAA;AACzC;AAEA,SAAS,WAAW,KAAqB;AACvC,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,SAAS,IAAI,KAAK,GAAG;AAC3B,SAAO,OAAO,MAAM,OAAO,QAAA,CAAS,IAAI,MAAM,OAAO,YAAA;AACvD;AASO,MAAM,UAAUA,MAAAA;AAAAA,EACrB,CAAC,EAAE,SAAS,MAAM,MAAM,MAAM,OAAO,MAAM,MAAM,WAAW,GAAG,MAAA,GAAS,QAAQ;AAC9E,UAAM,WAAW,EAAE,GAAGC,2BAAA,GAAkB,GAAG,KAAA;AAE3C,UAAM,QAAkB,CAAA;AACxB,QAAI,OAAO,SAAS,IAAK,OAAM,KAAK,SAAS,GAAG;AAChD,QAAI,UAAU,SAAS,OAAQ,OAAM,KAAK,SAAS,MAAM;AACzD,QAAI,QAAQ,SAAS,KAAM,OAAM,KAAK,WAAW,SAAS,IAAI,CAAC;AAE/D,UAAM,OAAO,SAAS,SAAS,eAAe,WAAW,SAAS,IAAI,IAAI;AAE1E,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAWC,MAAAA,GAAG,4EAA4E,SAAS;AAAA,QAClG,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,MAAM,SAAS,KAAKC,2BAAAA,IAAC,UAAM,UAAA,MAAM,KAAK,KAAK,GAAE;AAAA,UAC7C,SAAS,SACRA,2BAAAA,IAACC,MAAAA,OAAA,EAAM,MAAK,WAAU,SAAQ,QAAO,MAAK,MAAK,UAAA,QAAA,CAE/C;AAAA,UAED,QACCD,2BAAAA,IAACC,MAAAA,OAAA,EAAM,MAAM,KAAK,MAAM,SAAQ,QAAO,MAAK,MACzC,UAAA,KAAK,MAAA,CACR;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,QAAQ,cAAc;;"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { HTMLAttributes } from 'react';
|
|
2
|
+
import { VersionInfo } from './version-info';
|
|
3
|
+
export type VersionProps = HTMLAttributes<HTMLSpanElement> & {
|
|
4
|
+
/** Show the short git commit hash. */
|
|
5
|
+
commit?: boolean;
|
|
6
|
+
/** Show the git tag. */
|
|
7
|
+
tag?: boolean;
|
|
8
|
+
/** Show the build date. */
|
|
9
|
+
date?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Override the resolved build/runtime info. Primarily for stories and tests;
|
|
12
|
+
* when omitted the values injected at build time are used.
|
|
13
|
+
*/
|
|
14
|
+
info?: Partial<VersionInfo>;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Renders clicky-ui build metadata: git commit, tag, and build date, plus a
|
|
18
|
+
* status badge for a dirty working tree, Vite dev mode, or Storybook.
|
|
19
|
+
*
|
|
20
|
+
* Commit/tag/date/dirty are captured when the library is built (Vite `define`);
|
|
21
|
+
* `dev` and `storybook` are detected at runtime.
|
|
22
|
+
*/
|
|
23
|
+
export declare const Version: import('react').ForwardRefExoticComponent<HTMLAttributes<HTMLSpanElement> & {
|
|
24
|
+
/** Show the short git commit hash. */
|
|
25
|
+
commit?: boolean;
|
|
26
|
+
/** Show the git tag. */
|
|
27
|
+
tag?: boolean;
|
|
28
|
+
/** Show the build date. */
|
|
29
|
+
date?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Override the resolved build/runtime info. Primarily for stories and tests;
|
|
32
|
+
* when omitted the values injected at build time are used.
|
|
33
|
+
*/
|
|
34
|
+
info?: Partial<VersionInfo>;
|
|
35
|
+
} & import('react').RefAttributes<HTMLSpanElement>>;
|
|
36
|
+
//# sourceMappingURL=Version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Version.d.ts","sourceRoot":"","sources":["../../src/data/Version.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,OAAO,CAAC;AAGxD,OAAO,EAAoC,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAEpF,MAAM,MAAM,YAAY,GAAG,cAAc,CAAC,eAAe,CAAC,GAAG;IAC3D,sCAAsC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wBAAwB;IACxB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,2BAA2B;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CAC7B,CAAC;AAaF;;;;;;GAMG;AACH,eAAO,MAAM,OAAO;IA/BlB,sCAAsC;aAC7B,OAAO;IAChB,wBAAwB;UAClB,OAAO;IACb,2BAA2B;WACpB,OAAO;IACd;;;OAGG;WACI,OAAO,CAAC,WAAW,CAAC;mDAoD5B,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
import { Badge } from "./Badge.js";
|
|
5
|
+
import { getVersionInfo } from "./version-info.js";
|
|
6
|
+
const MODE_BADGE = {
|
|
7
|
+
dev: { label: "dev", tone: "info" },
|
|
8
|
+
storybook: { label: "storybook", tone: "neutral" }
|
|
9
|
+
};
|
|
10
|
+
function formatDate(iso) {
|
|
11
|
+
if (!iso) return "";
|
|
12
|
+
const parsed = new Date(iso);
|
|
13
|
+
return Number.isNaN(parsed.getTime()) ? iso : parsed.toISOString();
|
|
14
|
+
}
|
|
15
|
+
const Version = forwardRef(
|
|
16
|
+
({ commit = true, tag = true, date = true, info, className, ...props }, ref) => {
|
|
17
|
+
const resolved = { ...getVersionInfo(), ...info };
|
|
18
|
+
const parts = [];
|
|
19
|
+
if (tag && resolved.tag) parts.push(resolved.tag);
|
|
20
|
+
if (commit && resolved.commit) parts.push(resolved.commit);
|
|
21
|
+
if (date && resolved.date) parts.push(formatDate(resolved.date));
|
|
22
|
+
const mode = resolved.mode !== "production" ? MODE_BADGE[resolved.mode] : null;
|
|
23
|
+
return /* @__PURE__ */ jsxs(
|
|
24
|
+
"span",
|
|
25
|
+
{
|
|
26
|
+
ref,
|
|
27
|
+
className: cn("inline-flex items-center gap-1.5 font-mono text-xs text-muted-foreground", className),
|
|
28
|
+
...props,
|
|
29
|
+
children: [
|
|
30
|
+
parts.length > 0 && /* @__PURE__ */ jsx("span", { children: parts.join(" · ") }),
|
|
31
|
+
resolved.dirty && /* @__PURE__ */ jsx(Badge, { tone: "warning", variant: "soft", size: "xs", children: "dirty" }),
|
|
32
|
+
mode && /* @__PURE__ */ jsx(Badge, { tone: mode.tone, variant: "soft", size: "xs", children: mode.label })
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
Version.displayName = "Version";
|
|
39
|
+
export {
|
|
40
|
+
Version
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=Version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Version.js","sources":["../../src/data/Version.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Badge, type BadgeTone } from \"./Badge\";\nimport { getVersionInfo, type RuntimeMode, type VersionInfo } from \"./version-info\";\n\nexport type VersionProps = HTMLAttributes<HTMLSpanElement> & {\n /** Show the short git commit hash. */\n commit?: boolean;\n /** Show the git tag. */\n tag?: boolean;\n /** Show the build date. */\n date?: boolean;\n /**\n * Override the resolved build/runtime info. Primarily for stories and tests;\n * when omitted the values injected at build time are used.\n */\n info?: Partial<VersionInfo>;\n};\n\nconst MODE_BADGE: Record<Exclude<RuntimeMode, \"production\">, { label: string; tone: BadgeTone }> = {\n dev: { label: \"dev\", tone: \"info\" },\n storybook: { label: \"storybook\", tone: \"neutral\" },\n};\n\nfunction formatDate(iso: string): string {\n if (!iso) return \"\";\n const parsed = new Date(iso);\n return Number.isNaN(parsed.getTime()) ? iso : parsed.toISOString();\n}\n\n/**\n * Renders clicky-ui build metadata: git commit, tag, and build date, plus a\n * status badge for a dirty working tree, Vite dev mode, or Storybook.\n *\n * Commit/tag/date/dirty are captured when the library is built (Vite `define`);\n * `dev` and `storybook` are detected at runtime.\n */\nexport const Version = forwardRef<HTMLSpanElement, VersionProps>(\n ({ commit = true, tag = true, date = true, info, className, ...props }, ref) => {\n const resolved = { ...getVersionInfo(), ...info };\n\n const parts: string[] = [];\n if (tag && resolved.tag) parts.push(resolved.tag);\n if (commit && resolved.commit) parts.push(resolved.commit);\n if (date && resolved.date) parts.push(formatDate(resolved.date));\n\n const mode = resolved.mode !== \"production\" ? MODE_BADGE[resolved.mode] : null;\n\n return (\n <span\n ref={ref}\n className={cn(\"inline-flex items-center gap-1.5 font-mono text-xs text-muted-foreground\", className)}\n {...props}\n >\n {parts.length > 0 && <span>{parts.join(\" · \")}</span>}\n {resolved.dirty && (\n <Badge tone=\"warning\" variant=\"soft\" size=\"xs\">\n dirty\n </Badge>\n )}\n {mode && (\n <Badge tone={mode.tone} variant=\"soft\" size=\"xs\">\n {mode.label}\n </Badge>\n )}\n </span>\n );\n },\n);\n\nVersion.displayName = \"Version\";\n"],"names":[],"mappings":";;;;;AAmBA,MAAM,aAA6F;AAAA,EACjG,KAAK,EAAE,OAAO,OAAO,MAAM,OAAA;AAAA,EAC3B,WAAW,EAAE,OAAO,aAAa,MAAM,UAAA;AACzC;AAEA,SAAS,WAAW,KAAqB;AACvC,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,SAAS,IAAI,KAAK,GAAG;AAC3B,SAAO,OAAO,MAAM,OAAO,QAAA,CAAS,IAAI,MAAM,OAAO,YAAA;AACvD;AASO,MAAM,UAAU;AAAA,EACrB,CAAC,EAAE,SAAS,MAAM,MAAM,MAAM,OAAO,MAAM,MAAM,WAAW,GAAG,MAAA,GAAS,QAAQ;AAC9E,UAAM,WAAW,EAAE,GAAG,eAAA,GAAkB,GAAG,KAAA;AAE3C,UAAM,QAAkB,CAAA;AACxB,QAAI,OAAO,SAAS,IAAK,OAAM,KAAK,SAAS,GAAG;AAChD,QAAI,UAAU,SAAS,OAAQ,OAAM,KAAK,SAAS,MAAM;AACzD,QAAI,QAAQ,SAAS,KAAM,OAAM,KAAK,WAAW,SAAS,IAAI,CAAC;AAE/D,UAAM,OAAO,SAAS,SAAS,eAAe,WAAW,SAAS,IAAI,IAAI;AAE1E,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAW,GAAG,4EAA4E,SAAS;AAAA,QAClG,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,MAAM,SAAS,KAAK,oBAAC,UAAM,UAAA,MAAM,KAAK,KAAK,GAAE;AAAA,UAC7C,SAAS,SACR,oBAAC,OAAA,EAAM,MAAK,WAAU,SAAQ,QAAO,MAAK,MAAK,UAAA,QAAA,CAE/C;AAAA,UAED,QACC,oBAAC,OAAA,EAAM,MAAM,KAAK,MAAM,SAAQ,QAAO,MAAK,MACzC,UAAA,KAAK,MAAA,CACR;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,QAAQ,cAAc;"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const react = require("react");
|
|
5
|
+
const Icon = require("../Icon.cjs");
|
|
6
|
+
const button = require("../../components/button.cjs");
|
|
7
|
+
const JsonView = require("../JsonView.cjs");
|
|
8
|
+
const TabButton = require("../TabButton.cjs");
|
|
9
|
+
const utils = require("../../lib/utils.cjs");
|
|
10
|
+
const context = require("./context.cjs");
|
|
11
|
+
const status = require("./status.cjs");
|
|
12
|
+
const frameworkIcon = require("./frameworkIcon.cjs");
|
|
13
|
+
const TestFailureDetail = require("./TestFailureDetail.cjs");
|
|
14
|
+
const UiClass = require("../../icons/components/UiClass.cjs");
|
|
15
|
+
const UiRefresh = require("../../icons/components/UiRefresh.cjs");
|
|
16
|
+
const UiStop = require("../../icons/components/UiStop.cjs");
|
|
17
|
+
const UiClock = require("../../icons/components/UiClock.cjs");
|
|
18
|
+
const ICON_TONE = {
|
|
19
|
+
danger: "rose",
|
|
20
|
+
warning: "amber",
|
|
21
|
+
success: "emerald",
|
|
22
|
+
info: "sky",
|
|
23
|
+
neutral: "neutral"
|
|
24
|
+
};
|
|
25
|
+
function DefaultDetailBody({ node }) {
|
|
26
|
+
var _a;
|
|
27
|
+
const hasChildren = (((_a = node.children) == null ? void 0 : _a.length) ?? 0) > 0;
|
|
28
|
+
const counts = hasChildren ? status.sum(node) : null;
|
|
29
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 p-density-4", children: [
|
|
30
|
+
node.message && !node.failure_detail && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap break-words text-sm", children: node.message }),
|
|
31
|
+
node.failure_detail && /* @__PURE__ */ jsxRuntime.jsx(TestFailureDetail.TestFailureDetail, { failure: node.failure_detail }),
|
|
32
|
+
/* @__PURE__ */ jsxRuntime.jsx(TestFailureDetail.TestOutput, { node }),
|
|
33
|
+
node.attempts && node.attempts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(TestFailureDetail.TestAttempts, { attempts: node.attempts }),
|
|
34
|
+
counts && counts.total > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-3 text-xs text-muted-foreground", children: [
|
|
35
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
36
|
+
counts.total,
|
|
37
|
+
" total"
|
|
38
|
+
] }),
|
|
39
|
+
counts.passed > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
40
|
+
counts.passed,
|
|
41
|
+
" passed"
|
|
42
|
+
] }),
|
|
43
|
+
counts.failed > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
44
|
+
counts.failed,
|
|
45
|
+
" failed"
|
|
46
|
+
] }),
|
|
47
|
+
counts.skipped > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
48
|
+
counts.skipped,
|
|
49
|
+
" skipped"
|
|
50
|
+
] })
|
|
51
|
+
] }),
|
|
52
|
+
node.detail != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
53
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "Detail" }),
|
|
54
|
+
/* @__PURE__ */ jsxRuntime.jsx(JsonView.JsonView, { data: node.detail })
|
|
55
|
+
] })
|
|
56
|
+
] });
|
|
57
|
+
}
|
|
58
|
+
function DetailHeader({ node, actions }) {
|
|
59
|
+
const runner = context.useTestRunner();
|
|
60
|
+
const fwIcon = frameworkIcon.frameworkIcon(node.framework);
|
|
61
|
+
const canRerun = !!runner.onRerun && node.framework !== "task";
|
|
62
|
+
const canStop = !!runner.onStop && !!node.can_stop && !!node.task_id;
|
|
63
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2 border-b border-border p-density-4", children: [
|
|
64
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
65
|
+
Icon.Icon,
|
|
66
|
+
{
|
|
67
|
+
icon: status.statusIconFor(node),
|
|
68
|
+
className: utils.cn("mt-0.5 shrink-0 text-2xl", node.running && "animate-spin"),
|
|
69
|
+
tone: ICON_TONE[status.statusToneFor(node)]
|
|
70
|
+
}
|
|
71
|
+
),
|
|
72
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
73
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
74
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "break-words text-base font-semibold", children: node.name }),
|
|
75
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
76
|
+
actions,
|
|
77
|
+
canRerun && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
78
|
+
button.Button,
|
|
79
|
+
{
|
|
80
|
+
size: "sm",
|
|
81
|
+
disabled: runner.busy.rerun,
|
|
82
|
+
onClick: () => runner.onRerun(node),
|
|
83
|
+
title: "Rerun this test or subtree",
|
|
84
|
+
children: [
|
|
85
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiRefresh.UiRefresh, className: "mr-1 text-sm" }),
|
|
86
|
+
runner.busy.rerun ? "Running..." : "Rerun"
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
canStop && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
91
|
+
button.Button,
|
|
92
|
+
{
|
|
93
|
+
size: "sm",
|
|
94
|
+
variant: "destructive",
|
|
95
|
+
disabled: runner.busy.stop,
|
|
96
|
+
onClick: () => runner.onStop(node),
|
|
97
|
+
title: "Stop this running task",
|
|
98
|
+
children: [
|
|
99
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiStop.UiStop, className: "mr-1 text-sm" }),
|
|
100
|
+
runner.busy.stop ? "Stopping..." : "Stop"
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
] })
|
|
105
|
+
] }),
|
|
106
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-2 text-xs text-muted-foreground", children: [
|
|
107
|
+
fwIcon && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5", children: [
|
|
108
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: fwIcon, className: "text-sm" }),
|
|
109
|
+
node.framework
|
|
110
|
+
] }),
|
|
111
|
+
node.duration ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
112
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiClock.UiClock, className: "text-xs" }),
|
|
113
|
+
status.formatTestDuration(node.duration)
|
|
114
|
+
] }) : null,
|
|
115
|
+
node.file && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono", children: [
|
|
116
|
+
node.file,
|
|
117
|
+
node.line ? `:${node.line}` : ""
|
|
118
|
+
] })
|
|
119
|
+
] })
|
|
120
|
+
] })
|
|
121
|
+
] });
|
|
122
|
+
}
|
|
123
|
+
function TestDetailPanel() {
|
|
124
|
+
var _a;
|
|
125
|
+
const runner = context.useTestRunner();
|
|
126
|
+
const node = runner.selected;
|
|
127
|
+
const [activeTab, setActiveTab] = react.useState("detail");
|
|
128
|
+
if (!node) {
|
|
129
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center text-sm text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
130
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiClass.UiClass, className: "mb-2 block text-4xl" }),
|
|
131
|
+
"Select a test to view its details."
|
|
132
|
+
] }) });
|
|
133
|
+
}
|
|
134
|
+
const detailAdapter = runner.adapters.resolveDetail(node, runner);
|
|
135
|
+
const actionAdapter = runner.adapters.resolveActions(node, runner);
|
|
136
|
+
const tabs = runner.adapters.resolveTabs(node, runner);
|
|
137
|
+
const ownsScroll = runner.adapters.ownsScroll(node, runner);
|
|
138
|
+
const body = (detailAdapter == null ? void 0 : detailAdapter.renderDetail) ? detailAdapter.renderDetail({ node, runner }) : /* @__PURE__ */ jsxRuntime.jsx(DefaultDetailBody, { node });
|
|
139
|
+
const actions = (_a = actionAdapter == null ? void 0 : actionAdapter.nodeActions) == null ? void 0 : _a.call(actionAdapter, { node, runner });
|
|
140
|
+
const detailRegion = ownsScroll ? "min-h-0 flex-1" : "min-h-0 flex-1 overflow-y-auto";
|
|
141
|
+
if (tabs.length === 0) {
|
|
142
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [
|
|
143
|
+
/* @__PURE__ */ jsxRuntime.jsx(DetailHeader, { node, actions }),
|
|
144
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: detailRegion, children: body })
|
|
145
|
+
] });
|
|
146
|
+
}
|
|
147
|
+
const detailTab = { id: "detail", label: "Detail", render: () => body };
|
|
148
|
+
const allTabs = [detailTab, ...tabs];
|
|
149
|
+
const active = allTabs.find((t) => t.id === activeTab) ?? detailTab;
|
|
150
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [
|
|
151
|
+
/* @__PURE__ */ jsxRuntime.jsx(DetailHeader, { node, actions }),
|
|
152
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex shrink-0 items-center gap-1 border-b border-border px-density-2 py-density-1", children: allTabs.map((t) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
153
|
+
TabButton.TabButton,
|
|
154
|
+
{
|
|
155
|
+
active: active.id === t.id,
|
|
156
|
+
onClick: () => setActiveTab(t.id),
|
|
157
|
+
label: t.label
|
|
158
|
+
},
|
|
159
|
+
t.id
|
|
160
|
+
)) }),
|
|
161
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: detailRegion, children: active.render() })
|
|
162
|
+
] });
|
|
163
|
+
}
|
|
164
|
+
exports.TestDetailPanel = TestDetailPanel;
|
|
165
|
+
//# sourceMappingURL=TestDetailPanel.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestDetailPanel.cjs","sources":["../../../src/data/test-runner/TestDetailPanel.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport { Button } from \"../../components/button\";\nimport { JsonView } from \"../JsonView\";\nimport { TabButton } from \"../TabButton\";\nimport { cn } from \"../../lib/utils\";\nimport { UiClass, UiClock, UiRefresh, UiStop } from \"../../icons\";\nimport type { Test } from \"./types\";\nimport { useTestRunner } from \"./context\";\nimport { formatTestDuration, statusIconFor, statusToneFor, sum } from \"./status\";\nimport { frameworkIcon } from \"./frameworkIcon\";\nimport { TestAttempts, TestFailureDetail, TestOutput } from \"./TestFailureDetail\";\n\nconst ICON_TONE = {\n danger: \"rose\",\n warning: \"amber\",\n success: \"emerald\",\n info: \"sky\",\n neutral: \"neutral\",\n} as const;\n\n/** Built-in detail body — used when no adapter contributes `renderDetail`. */\nfunction DefaultDetailBody({ node }: { node: Test }) {\n const hasChildren = (node.children?.length ?? 0) > 0;\n const counts = hasChildren ? sum(node) : null;\n return (\n <div className=\"space-y-4 p-density-4\">\n {node.message && !node.failure_detail && (\n <p className=\"whitespace-pre-wrap break-words text-sm\">{node.message}</p>\n )}\n {node.failure_detail && <TestFailureDetail failure={node.failure_detail} />}\n <TestOutput node={node} />\n {node.attempts && node.attempts.length > 0 && <TestAttempts attempts={node.attempts} />}\n {counts && counts.total > 0 && (\n <div className=\"flex flex-wrap gap-3 text-xs text-muted-foreground\">\n <span>{counts.total} total</span>\n {counts.passed > 0 && <span>{counts.passed} passed</span>}\n {counts.failed > 0 && <span>{counts.failed} failed</span>}\n {counts.skipped > 0 && <span>{counts.skipped} skipped</span>}\n </div>\n )}\n {node.detail != null && (\n <div className=\"space-y-1\">\n <div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">Detail</div>\n <JsonView data={node.detail} />\n </div>\n )}\n </div>\n );\n}\n\nfunction DetailHeader({ node, actions }: { node: Test; actions?: ReactNode }) {\n const runner = useTestRunner();\n const fwIcon = frameworkIcon(node.framework);\n const canRerun = !!runner.onRerun && node.framework !== \"task\";\n const canStop = !!runner.onStop && !!node.can_stop && !!node.task_id;\n return (\n <div className=\"flex items-start gap-2 border-b border-border p-density-4\">\n <Icon\n icon={statusIconFor(node)}\n className={cn(\"mt-0.5 shrink-0 text-2xl\", node.running && \"animate-spin\")}\n tone={ICON_TONE[statusToneFor(node)]}\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-start justify-between gap-2\">\n <h2 className=\"break-words text-base font-semibold\">{node.name}</h2>\n <div className=\"flex shrink-0 items-center gap-1\">\n {actions}\n {canRerun && (\n <Button\n size=\"sm\"\n disabled={runner.busy.rerun}\n onClick={() => runner.onRerun!(node)}\n title=\"Rerun this test or subtree\"\n >\n <Icon icon={UiRefresh} className=\"mr-1 text-sm\" />\n {runner.busy.rerun ? \"Running...\" : \"Rerun\"}\n </Button>\n )}\n {canStop && (\n <Button\n size=\"sm\"\n variant=\"destructive\"\n disabled={runner.busy.stop}\n onClick={() => runner.onStop!(node)}\n title=\"Stop this running task\"\n >\n <Icon icon={UiStop} className=\"mr-1 text-sm\" />\n {runner.busy.stop ? \"Stopping...\" : \"Stop\"}\n </Button>\n )}\n </div>\n </div>\n <div className=\"mt-1 flex flex-wrap items-center gap-2 text-xs text-muted-foreground\">\n {fwIcon && (\n <span className=\"inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5\">\n <Icon icon={fwIcon} className=\"text-sm\" />\n {node.framework}\n </span>\n )}\n {node.duration ? (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {formatTestDuration(node.duration)}\n </span>\n ) : null}\n {node.file && (\n <span className=\"font-mono\">\n {node.file}\n {node.line ? `:${node.line}` : \"\"}\n </span>\n )}\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * The right pane. Resolves the node's adapter for body / tabs / header actions,\n * falling back to the built-in default body. Shows an empty state when nothing\n * is selected.\n */\nexport function TestDetailPanel() {\n const runner = useTestRunner();\n const node = runner.selected;\n const [activeTab, setActiveTab] = useState<string>(\"detail\");\n\n if (!node) {\n return (\n <div className=\"flex h-full items-center justify-center text-sm text-muted-foreground\">\n <div className=\"text-center\">\n <Icon icon={UiClass} className=\"mb-2 block text-4xl\" />\n Select a test to view its details.\n </div>\n </div>\n );\n }\n\n const detailAdapter = runner.adapters.resolveDetail(node, runner);\n const actionAdapter = runner.adapters.resolveActions(node, runner);\n const tabs = runner.adapters.resolveTabs(node, runner);\n const ownsScroll = runner.adapters.ownsScroll(node, runner);\n\n const body = detailAdapter?.renderDetail\n ? detailAdapter.renderDetail({ node, runner })\n : <DefaultDetailBody node={node} />;\n const actions = actionAdapter?.nodeActions?.({ node, runner });\n\n const detailRegion = ownsScroll ? \"min-h-0 flex-1\" : \"min-h-0 flex-1 overflow-y-auto\";\n\n // No extra tabs → just the body under the header.\n if (tabs.length === 0) {\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <DetailHeader node={node} actions={actions} />\n <div className={detailRegion}>{body}</div>\n </div>\n );\n }\n\n const detailTab = { id: \"detail\", label: \"Detail\", render: () => body };\n const allTabs = [detailTab, ...tabs];\n const active = allTabs.find((t) => t.id === activeTab) ?? detailTab;\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <DetailHeader node={node} actions={actions} />\n <div className=\"flex shrink-0 items-center gap-1 border-b border-border px-density-2 py-density-1\">\n {allTabs.map((t) => (\n <TabButton\n key={t.id}\n active={active.id === t.id}\n onClick={() => setActiveTab(t.id)}\n label={t.label}\n />\n ))}\n </div>\n <div className={detailRegion}>{active.render()}</div>\n </div>\n );\n}\n"],"names":["sum","jsxs","jsx","TestFailureDetail","TestOutput","TestAttempts","JsonView","useTestRunner","frameworkIcon","Icon","statusIconFor","cn","statusToneFor","Button","UiRefresh","UiStop","UiClock","formatTestDuration","useState","UiClass","TabButton"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The right pane. Resolves the node's adapter for body / tabs / header actions,
|
|
3
|
+
* falling back to the built-in default body. Shows an empty state when nothing
|
|
4
|
+
* is selected.
|
|
5
|
+
*/
|
|
6
|
+
export declare function TestDetailPanel(): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=TestDetailPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestDetailPanel.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestDetailPanel.tsx"],"names":[],"mappings":"AAsHA;;;;GAIG;AACH,wBAAgB,eAAe,4CA0D9B"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Icon } from "../Icon.js";
|
|
4
|
+
import { Button } from "../../components/button.js";
|
|
5
|
+
import { JsonView } from "../JsonView.js";
|
|
6
|
+
import { TabButton } from "../TabButton.js";
|
|
7
|
+
import { cn } from "../../lib/utils.js";
|
|
8
|
+
import { useTestRunner } from "./context.js";
|
|
9
|
+
import { sum, statusToneFor, statusIconFor, formatTestDuration } from "./status.js";
|
|
10
|
+
import { frameworkIcon } from "./frameworkIcon.js";
|
|
11
|
+
import { TestFailureDetail, TestOutput, TestAttempts } from "./TestFailureDetail.js";
|
|
12
|
+
import { UiClass } from "../../icons/components/UiClass.js";
|
|
13
|
+
import { UiRefresh } from "../../icons/components/UiRefresh.js";
|
|
14
|
+
import { UiStop } from "../../icons/components/UiStop.js";
|
|
15
|
+
import { UiClock } from "../../icons/components/UiClock.js";
|
|
16
|
+
const ICON_TONE = {
|
|
17
|
+
danger: "rose",
|
|
18
|
+
warning: "amber",
|
|
19
|
+
success: "emerald",
|
|
20
|
+
info: "sky",
|
|
21
|
+
neutral: "neutral"
|
|
22
|
+
};
|
|
23
|
+
function DefaultDetailBody({ node }) {
|
|
24
|
+
var _a;
|
|
25
|
+
const hasChildren = (((_a = node.children) == null ? void 0 : _a.length) ?? 0) > 0;
|
|
26
|
+
const counts = hasChildren ? sum(node) : null;
|
|
27
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4 p-density-4", children: [
|
|
28
|
+
node.message && !node.failure_detail && /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap break-words text-sm", children: node.message }),
|
|
29
|
+
node.failure_detail && /* @__PURE__ */ jsx(TestFailureDetail, { failure: node.failure_detail }),
|
|
30
|
+
/* @__PURE__ */ jsx(TestOutput, { node }),
|
|
31
|
+
node.attempts && node.attempts.length > 0 && /* @__PURE__ */ jsx(TestAttempts, { attempts: node.attempts }),
|
|
32
|
+
counts && counts.total > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-3 text-xs text-muted-foreground", children: [
|
|
33
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
34
|
+
counts.total,
|
|
35
|
+
" total"
|
|
36
|
+
] }),
|
|
37
|
+
counts.passed > 0 && /* @__PURE__ */ jsxs("span", { children: [
|
|
38
|
+
counts.passed,
|
|
39
|
+
" passed"
|
|
40
|
+
] }),
|
|
41
|
+
counts.failed > 0 && /* @__PURE__ */ jsxs("span", { children: [
|
|
42
|
+
counts.failed,
|
|
43
|
+
" failed"
|
|
44
|
+
] }),
|
|
45
|
+
counts.skipped > 0 && /* @__PURE__ */ jsxs("span", { children: [
|
|
46
|
+
counts.skipped,
|
|
47
|
+
" skipped"
|
|
48
|
+
] })
|
|
49
|
+
] }),
|
|
50
|
+
node.detail != null && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
51
|
+
/* @__PURE__ */ jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "Detail" }),
|
|
52
|
+
/* @__PURE__ */ jsx(JsonView, { data: node.detail })
|
|
53
|
+
] })
|
|
54
|
+
] });
|
|
55
|
+
}
|
|
56
|
+
function DetailHeader({ node, actions }) {
|
|
57
|
+
const runner = useTestRunner();
|
|
58
|
+
const fwIcon = frameworkIcon(node.framework);
|
|
59
|
+
const canRerun = !!runner.onRerun && node.framework !== "task";
|
|
60
|
+
const canStop = !!runner.onStop && !!node.can_stop && !!node.task_id;
|
|
61
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 border-b border-border p-density-4", children: [
|
|
62
|
+
/* @__PURE__ */ jsx(
|
|
63
|
+
Icon,
|
|
64
|
+
{
|
|
65
|
+
icon: statusIconFor(node),
|
|
66
|
+
className: cn("mt-0.5 shrink-0 text-2xl", node.running && "animate-spin"),
|
|
67
|
+
tone: ICON_TONE[statusToneFor(node)]
|
|
68
|
+
}
|
|
69
|
+
),
|
|
70
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
71
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
72
|
+
/* @__PURE__ */ jsx("h2", { className: "break-words text-base font-semibold", children: node.name }),
|
|
73
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
74
|
+
actions,
|
|
75
|
+
canRerun && /* @__PURE__ */ jsxs(
|
|
76
|
+
Button,
|
|
77
|
+
{
|
|
78
|
+
size: "sm",
|
|
79
|
+
disabled: runner.busy.rerun,
|
|
80
|
+
onClick: () => runner.onRerun(node),
|
|
81
|
+
title: "Rerun this test or subtree",
|
|
82
|
+
children: [
|
|
83
|
+
/* @__PURE__ */ jsx(Icon, { icon: UiRefresh, className: "mr-1 text-sm" }),
|
|
84
|
+
runner.busy.rerun ? "Running..." : "Rerun"
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
),
|
|
88
|
+
canStop && /* @__PURE__ */ jsxs(
|
|
89
|
+
Button,
|
|
90
|
+
{
|
|
91
|
+
size: "sm",
|
|
92
|
+
variant: "destructive",
|
|
93
|
+
disabled: runner.busy.stop,
|
|
94
|
+
onClick: () => runner.onStop(node),
|
|
95
|
+
title: "Stop this running task",
|
|
96
|
+
children: [
|
|
97
|
+
/* @__PURE__ */ jsx(Icon, { icon: UiStop, className: "mr-1 text-sm" }),
|
|
98
|
+
runner.busy.stop ? "Stopping..." : "Stop"
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
] })
|
|
103
|
+
] }),
|
|
104
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-2 text-xs text-muted-foreground", children: [
|
|
105
|
+
fwIcon && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5", children: [
|
|
106
|
+
/* @__PURE__ */ jsx(Icon, { icon: fwIcon, className: "text-sm" }),
|
|
107
|
+
node.framework
|
|
108
|
+
] }),
|
|
109
|
+
node.duration ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
110
|
+
/* @__PURE__ */ jsx(Icon, { icon: UiClock, className: "text-xs" }),
|
|
111
|
+
formatTestDuration(node.duration)
|
|
112
|
+
] }) : null,
|
|
113
|
+
node.file && /* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
|
|
114
|
+
node.file,
|
|
115
|
+
node.line ? `:${node.line}` : ""
|
|
116
|
+
] })
|
|
117
|
+
] })
|
|
118
|
+
] })
|
|
119
|
+
] });
|
|
120
|
+
}
|
|
121
|
+
function TestDetailPanel() {
|
|
122
|
+
var _a;
|
|
123
|
+
const runner = useTestRunner();
|
|
124
|
+
const node = runner.selected;
|
|
125
|
+
const [activeTab, setActiveTab] = useState("detail");
|
|
126
|
+
if (!node) {
|
|
127
|
+
return /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center text-sm text-muted-foreground", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
128
|
+
/* @__PURE__ */ jsx(Icon, { icon: UiClass, className: "mb-2 block text-4xl" }),
|
|
129
|
+
"Select a test to view its details."
|
|
130
|
+
] }) });
|
|
131
|
+
}
|
|
132
|
+
const detailAdapter = runner.adapters.resolveDetail(node, runner);
|
|
133
|
+
const actionAdapter = runner.adapters.resolveActions(node, runner);
|
|
134
|
+
const tabs = runner.adapters.resolveTabs(node, runner);
|
|
135
|
+
const ownsScroll = runner.adapters.ownsScroll(node, runner);
|
|
136
|
+
const body = (detailAdapter == null ? void 0 : detailAdapter.renderDetail) ? detailAdapter.renderDetail({ node, runner }) : /* @__PURE__ */ jsx(DefaultDetailBody, { node });
|
|
137
|
+
const actions = (_a = actionAdapter == null ? void 0 : actionAdapter.nodeActions) == null ? void 0 : _a.call(actionAdapter, { node, runner });
|
|
138
|
+
const detailRegion = ownsScroll ? "min-h-0 flex-1" : "min-h-0 flex-1 overflow-y-auto";
|
|
139
|
+
if (tabs.length === 0) {
|
|
140
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [
|
|
141
|
+
/* @__PURE__ */ jsx(DetailHeader, { node, actions }),
|
|
142
|
+
/* @__PURE__ */ jsx("div", { className: detailRegion, children: body })
|
|
143
|
+
] });
|
|
144
|
+
}
|
|
145
|
+
const detailTab = { id: "detail", label: "Detail", render: () => body };
|
|
146
|
+
const allTabs = [detailTab, ...tabs];
|
|
147
|
+
const active = allTabs.find((t) => t.id === activeTab) ?? detailTab;
|
|
148
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [
|
|
149
|
+
/* @__PURE__ */ jsx(DetailHeader, { node, actions }),
|
|
150
|
+
/* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-1 border-b border-border px-density-2 py-density-1", children: allTabs.map((t) => /* @__PURE__ */ jsx(
|
|
151
|
+
TabButton,
|
|
152
|
+
{
|
|
153
|
+
active: active.id === t.id,
|
|
154
|
+
onClick: () => setActiveTab(t.id),
|
|
155
|
+
label: t.label
|
|
156
|
+
},
|
|
157
|
+
t.id
|
|
158
|
+
)) }),
|
|
159
|
+
/* @__PURE__ */ jsx("div", { className: detailRegion, children: active.render() })
|
|
160
|
+
] });
|
|
161
|
+
}
|
|
162
|
+
export {
|
|
163
|
+
TestDetailPanel
|
|
164
|
+
};
|
|
165
|
+
//# sourceMappingURL=TestDetailPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestDetailPanel.js","sources":["../../../src/data/test-runner/TestDetailPanel.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport { Button } from \"../../components/button\";\nimport { JsonView } from \"../JsonView\";\nimport { TabButton } from \"../TabButton\";\nimport { cn } from \"../../lib/utils\";\nimport { UiClass, UiClock, UiRefresh, UiStop } from \"../../icons\";\nimport type { Test } from \"./types\";\nimport { useTestRunner } from \"./context\";\nimport { formatTestDuration, statusIconFor, statusToneFor, sum } from \"./status\";\nimport { frameworkIcon } from \"./frameworkIcon\";\nimport { TestAttempts, TestFailureDetail, TestOutput } from \"./TestFailureDetail\";\n\nconst ICON_TONE = {\n danger: \"rose\",\n warning: \"amber\",\n success: \"emerald\",\n info: \"sky\",\n neutral: \"neutral\",\n} as const;\n\n/** Built-in detail body — used when no adapter contributes `renderDetail`. */\nfunction DefaultDetailBody({ node }: { node: Test }) {\n const hasChildren = (node.children?.length ?? 0) > 0;\n const counts = hasChildren ? sum(node) : null;\n return (\n <div className=\"space-y-4 p-density-4\">\n {node.message && !node.failure_detail && (\n <p className=\"whitespace-pre-wrap break-words text-sm\">{node.message}</p>\n )}\n {node.failure_detail && <TestFailureDetail failure={node.failure_detail} />}\n <TestOutput node={node} />\n {node.attempts && node.attempts.length > 0 && <TestAttempts attempts={node.attempts} />}\n {counts && counts.total > 0 && (\n <div className=\"flex flex-wrap gap-3 text-xs text-muted-foreground\">\n <span>{counts.total} total</span>\n {counts.passed > 0 && <span>{counts.passed} passed</span>}\n {counts.failed > 0 && <span>{counts.failed} failed</span>}\n {counts.skipped > 0 && <span>{counts.skipped} skipped</span>}\n </div>\n )}\n {node.detail != null && (\n <div className=\"space-y-1\">\n <div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">Detail</div>\n <JsonView data={node.detail} />\n </div>\n )}\n </div>\n );\n}\n\nfunction DetailHeader({ node, actions }: { node: Test; actions?: ReactNode }) {\n const runner = useTestRunner();\n const fwIcon = frameworkIcon(node.framework);\n const canRerun = !!runner.onRerun && node.framework !== \"task\";\n const canStop = !!runner.onStop && !!node.can_stop && !!node.task_id;\n return (\n <div className=\"flex items-start gap-2 border-b border-border p-density-4\">\n <Icon\n icon={statusIconFor(node)}\n className={cn(\"mt-0.5 shrink-0 text-2xl\", node.running && \"animate-spin\")}\n tone={ICON_TONE[statusToneFor(node)]}\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-start justify-between gap-2\">\n <h2 className=\"break-words text-base font-semibold\">{node.name}</h2>\n <div className=\"flex shrink-0 items-center gap-1\">\n {actions}\n {canRerun && (\n <Button\n size=\"sm\"\n disabled={runner.busy.rerun}\n onClick={() => runner.onRerun!(node)}\n title=\"Rerun this test or subtree\"\n >\n <Icon icon={UiRefresh} className=\"mr-1 text-sm\" />\n {runner.busy.rerun ? \"Running...\" : \"Rerun\"}\n </Button>\n )}\n {canStop && (\n <Button\n size=\"sm\"\n variant=\"destructive\"\n disabled={runner.busy.stop}\n onClick={() => runner.onStop!(node)}\n title=\"Stop this running task\"\n >\n <Icon icon={UiStop} className=\"mr-1 text-sm\" />\n {runner.busy.stop ? \"Stopping...\" : \"Stop\"}\n </Button>\n )}\n </div>\n </div>\n <div className=\"mt-1 flex flex-wrap items-center gap-2 text-xs text-muted-foreground\">\n {fwIcon && (\n <span className=\"inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5\">\n <Icon icon={fwIcon} className=\"text-sm\" />\n {node.framework}\n </span>\n )}\n {node.duration ? (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {formatTestDuration(node.duration)}\n </span>\n ) : null}\n {node.file && (\n <span className=\"font-mono\">\n {node.file}\n {node.line ? `:${node.line}` : \"\"}\n </span>\n )}\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * The right pane. Resolves the node's adapter for body / tabs / header actions,\n * falling back to the built-in default body. Shows an empty state when nothing\n * is selected.\n */\nexport function TestDetailPanel() {\n const runner = useTestRunner();\n const node = runner.selected;\n const [activeTab, setActiveTab] = useState<string>(\"detail\");\n\n if (!node) {\n return (\n <div className=\"flex h-full items-center justify-center text-sm text-muted-foreground\">\n <div className=\"text-center\">\n <Icon icon={UiClass} className=\"mb-2 block text-4xl\" />\n Select a test to view its details.\n </div>\n </div>\n );\n }\n\n const detailAdapter = runner.adapters.resolveDetail(node, runner);\n const actionAdapter = runner.adapters.resolveActions(node, runner);\n const tabs = runner.adapters.resolveTabs(node, runner);\n const ownsScroll = runner.adapters.ownsScroll(node, runner);\n\n const body = detailAdapter?.renderDetail\n ? detailAdapter.renderDetail({ node, runner })\n : <DefaultDetailBody node={node} />;\n const actions = actionAdapter?.nodeActions?.({ node, runner });\n\n const detailRegion = ownsScroll ? \"min-h-0 flex-1\" : \"min-h-0 flex-1 overflow-y-auto\";\n\n // No extra tabs → just the body under the header.\n if (tabs.length === 0) {\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <DetailHeader node={node} actions={actions} />\n <div className={detailRegion}>{body}</div>\n </div>\n );\n }\n\n const detailTab = { id: \"detail\", label: \"Detail\", render: () => body };\n const allTabs = [detailTab, ...tabs];\n const active = allTabs.find((t) => t.id === activeTab) ?? detailTab;\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <DetailHeader node={node} actions={actions} />\n <div className=\"flex shrink-0 items-center gap-1 border-b border-border px-density-2 py-density-1\">\n {allTabs.map((t) => (\n <TabButton\n key={t.id}\n active={active.id === t.id}\n onClick={() => setActiveTab(t.id)}\n label={t.label}\n />\n ))}\n </div>\n <div className={detailRegion}>{active.render()}</div>\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AAaA,MAAM,YAAY;AAAA,EAChB,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,MAAM;AAAA,EACN,SAAS;AACX;AAGA,SAAS,kBAAkB,EAAE,QAAwB;;AACnD,QAAM,iBAAe,UAAK,aAAL,mBAAe,WAAU,KAAK;AACnD,QAAM,SAAS,cAAc,IAAI,IAAI,IAAI;AACzC,SACE,qBAAC,OAAA,EAAI,WAAU,yBACZ,UAAA;AAAA,IAAA,KAAK,WAAW,CAAC,KAAK,sCACpB,KAAA,EAAE,WAAU,2CAA2C,UAAA,KAAK,QAAA,CAAQ;AAAA,IAEtE,KAAK,kBAAkB,oBAAC,mBAAA,EAAkB,SAAS,KAAK,gBAAgB;AAAA,IACzE,oBAAC,cAAW,MAAY;AAAA,IACvB,KAAK,YAAY,KAAK,SAAS,SAAS,KAAK,oBAAC,cAAA,EAAa,UAAU,KAAK,SAAA,CAAU;AAAA,IACpF,UAAU,OAAO,QAAQ,KACxB,qBAAC,OAAA,EAAI,WAAU,sDACb,UAAA;AAAA,MAAA,qBAAC,QAAA,EAAM,UAAA;AAAA,QAAA,OAAO;AAAA,QAAM;AAAA,MAAA,GAAM;AAAA,MACzB,OAAO,SAAS,KAAK,qBAAC,QAAA,EAAM,UAAA;AAAA,QAAA,OAAO;AAAA,QAAO;AAAA,MAAA,GAAO;AAAA,MACjD,OAAO,SAAS,KAAK,qBAAC,QAAA,EAAM,UAAA;AAAA,QAAA,OAAO;AAAA,QAAO;AAAA,MAAA,GAAO;AAAA,MACjD,OAAO,UAAU,KAAK,qBAAC,QAAA,EAAM,UAAA;AAAA,QAAA,OAAO;AAAA,QAAQ;AAAA,MAAA,EAAA,CAAQ;AAAA,IAAA,GACvD;AAAA,IAED,KAAK,UAAU,QACd,qBAAC,OAAA,EAAI,WAAU,aACb,UAAA;AAAA,MAAA,oBAAC,OAAA,EAAI,WAAU,6DAA4D,UAAA,UAAM;AAAA,MACjF,oBAAC,UAAA,EAAS,MAAM,KAAK,OAAA,CAAQ;AAAA,IAAA,EAAA,CAC/B;AAAA,EAAA,GAEJ;AAEJ;AAEA,SAAS,aAAa,EAAE,MAAM,WAAgD;AAC5E,QAAM,SAAS,cAAA;AACf,QAAM,SAAS,cAAc,KAAK,SAAS;AAC3C,QAAM,WAAW,CAAC,CAAC,OAAO,WAAW,KAAK,cAAc;AACxD,QAAM,UAAU,CAAC,CAAC,OAAO,UAAU,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,KAAK;AAC7D,SACE,qBAAC,OAAA,EAAI,WAAU,6DACb,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAM,cAAc,IAAI;AAAA,QACxB,WAAW,GAAG,4BAA4B,KAAK,WAAW,cAAc;AAAA,QACxE,MAAM,UAAU,cAAc,IAAI,CAAC;AAAA,MAAA;AAAA,IAAA;AAAA,IAErC,qBAAC,OAAA,EAAI,WAAU,kBACb,UAAA;AAAA,MAAA,qBAAC,OAAA,EAAI,WAAU,0CACb,UAAA;AAAA,QAAA,oBAAC,MAAA,EAAG,WAAU,uCAAuC,UAAA,KAAK,MAAK;AAAA,QAC/D,qBAAC,OAAA,EAAI,WAAU,oCACZ,UAAA;AAAA,UAAA;AAAA,UACA,YACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,UAAU,OAAO,KAAK;AAAA,cACtB,SAAS,MAAM,OAAO,QAAS,IAAI;AAAA,cACnC,OAAM;AAAA,cAEN,UAAA;AAAA,gBAAA,oBAAC,MAAA,EAAK,MAAM,WAAW,WAAU,gBAAe;AAAA,gBAC/C,OAAO,KAAK,QAAQ,eAAe;AAAA,cAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAGvC,WACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,UAAU,OAAO,KAAK;AAAA,cACtB,SAAS,MAAM,OAAO,OAAQ,IAAI;AAAA,cAClC,OAAM;AAAA,cAEN,UAAA;AAAA,gBAAA,oBAAC,MAAA,EAAK,MAAM,QAAQ,WAAU,gBAAe;AAAA,gBAC5C,OAAO,KAAK,OAAO,gBAAgB;AAAA,cAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QACtC,EAAA,CAEJ;AAAA,MAAA,GACF;AAAA,MACA,qBAAC,OAAA,EAAI,WAAU,wEACZ,UAAA;AAAA,QAAA,UACC,qBAAC,QAAA,EAAK,WAAU,iEACd,UAAA;AAAA,UAAA,oBAAC,MAAA,EAAK,MAAM,QAAQ,WAAU,WAAU;AAAA,UACvC,KAAK;AAAA,QAAA,GACR;AAAA,QAED,KAAK,WACJ,qBAAC,QAAA,EAAK,WAAU,kCACd,UAAA;AAAA,UAAA,oBAAC,MAAA,EAAK,MAAM,SAAS,WAAU,WAAU;AAAA,UACxC,mBAAmB,KAAK,QAAQ;AAAA,QAAA,EAAA,CACnC,IACE;AAAA,QACH,KAAK,QACJ,qBAAC,QAAA,EAAK,WAAU,aACb,UAAA;AAAA,UAAA,KAAK;AAAA,UACL,KAAK,OAAO,IAAI,KAAK,IAAI,KAAK;AAAA,QAAA,EAAA,CACjC;AAAA,MAAA,EAAA,CAEJ;AAAA,IAAA,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;AAOO,SAAS,kBAAkB;;AAChC,QAAM,SAAS,cAAA;AACf,QAAM,OAAO,OAAO;AACpB,QAAM,CAAC,WAAW,YAAY,IAAI,SAAiB,QAAQ;AAE3D,MAAI,CAAC,MAAM;AACT,+BACG,OAAA,EAAI,WAAU,yEACb,UAAA,qBAAC,OAAA,EAAI,WAAU,eACb,UAAA;AAAA,MAAA,oBAAC,MAAA,EAAK,MAAM,SAAS,WAAU,uBAAsB;AAAA,MAAE;AAAA,IAAA,EAAA,CAEzD,EAAA,CACF;AAAA,EAEJ;AAEA,QAAM,gBAAgB,OAAO,SAAS,cAAc,MAAM,MAAM;AAChE,QAAM,gBAAgB,OAAO,SAAS,eAAe,MAAM,MAAM;AACjE,QAAM,OAAO,OAAO,SAAS,YAAY,MAAM,MAAM;AACrD,QAAM,aAAa,OAAO,SAAS,WAAW,MAAM,MAAM;AAE1D,QAAM,QAAO,+CAAe,gBACxB,cAAc,aAAa,EAAE,MAAM,OAAA,CAAQ,IAC3C,oBAAC,mBAAA,EAAkB,KAAA,CAAY;AACnC,QAAM,WAAU,oDAAe,gBAAf,uCAA6B,EAAE,MAAM;AAErD,QAAM,eAAe,aAAa,mBAAmB;AAGrD,MAAI,KAAK,WAAW,GAAG;AACrB,WACE,qBAAC,OAAA,EAAI,WAAU,gCACb,UAAA;AAAA,MAAA,oBAAC,cAAA,EAAa,MAAY,QAAA,CAAkB;AAAA,MAC5C,oBAAC,OAAA,EAAI,WAAW,cAAe,UAAA,KAAA,CAAK;AAAA,IAAA,GACtC;AAAA,EAEJ;AAEA,QAAM,YAAY,EAAE,IAAI,UAAU,OAAO,UAAU,QAAQ,MAAM,KAAA;AACjE,QAAM,UAAU,CAAC,WAAW,GAAG,IAAI;AACnC,QAAM,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS,KAAK;AAE1D,SACE,qBAAC,OAAA,EAAI,WAAU,gCACb,UAAA;AAAA,IAAA,oBAAC,cAAA,EAAa,MAAY,QAAA,CAAkB;AAAA,wBAC3C,OAAA,EAAI,WAAU,qFACZ,UAAA,QAAQ,IAAI,CAAC,MACZ;AAAA,MAAC;AAAA,MAAA;AAAA,QAEC,QAAQ,OAAO,OAAO,EAAE;AAAA,QACxB,SAAS,MAAM,aAAa,EAAE,EAAE;AAAA,QAChC,OAAO,EAAE;AAAA,MAAA;AAAA,MAHJ,EAAE;AAAA,IAAA,CAKV,GACH;AAAA,wBACC,OAAA,EAAI,WAAW,cAAe,UAAA,OAAO,SAAO,CAAE;AAAA,EAAA,GACjD;AAEJ;"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const LogViewer = require("../LogViewer.cjs");
|
|
5
|
+
const utils = require("../../lib/utils.cjs");
|
|
6
|
+
const status = require("./status.cjs");
|
|
7
|
+
function Field({
|
|
8
|
+
label,
|
|
9
|
+
value,
|
|
10
|
+
mono
|
|
11
|
+
}) {
|
|
12
|
+
if (!value) return null;
|
|
13
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
14
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: label }),
|
|
15
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16
|
+
"pre",
|
|
17
|
+
{
|
|
18
|
+
className: utils.cn(
|
|
19
|
+
"whitespace-pre-wrap break-words rounded-md bg-muted p-2 text-xs",
|
|
20
|
+
mono && "font-mono"
|
|
21
|
+
),
|
|
22
|
+
children: value
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
] });
|
|
26
|
+
}
|
|
27
|
+
function TestFailureDetail({ failure }) {
|
|
28
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 rounded-md border border-red-200 bg-red-50/50 p-3 dark:border-red-900 dark:bg-red-950/30", children: [
|
|
29
|
+
failure.summary && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-red-700 dark:text-red-300", children: failure.summary }),
|
|
30
|
+
/* @__PURE__ */ jsxRuntime.jsx(Field, { label: "Matcher", value: failure.matcher, mono: true }),
|
|
31
|
+
/* @__PURE__ */ jsxRuntime.jsx(Field, { label: "Expected", value: failure.expected, mono: true }),
|
|
32
|
+
/* @__PURE__ */ jsxRuntime.jsx(Field, { label: "Actual", value: failure.actual, mono: true }),
|
|
33
|
+
/* @__PURE__ */ jsxRuntime.jsx(Field, { label: "Location", value: failure.location, mono: true }),
|
|
34
|
+
failure.stack && /* @__PURE__ */ jsxRuntime.jsx(LogViewer.LogViewer, { logs: failure.stack, collapsedLines: 6 })
|
|
35
|
+
] });
|
|
36
|
+
}
|
|
37
|
+
function TestOutput({ node }) {
|
|
38
|
+
if (!node.stdout && !node.stderr) return null;
|
|
39
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
40
|
+
node.stdout && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
41
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "stdout" }),
|
|
42
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogViewer.LogViewer, { logs: node.stdout })
|
|
43
|
+
] }),
|
|
44
|
+
node.stderr && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
45
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "stderr" }),
|
|
46
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogViewer.LogViewer, { logs: node.stderr, borderClass: "border-red-300 dark:border-red-900" })
|
|
47
|
+
] })
|
|
48
|
+
] });
|
|
49
|
+
}
|
|
50
|
+
function attemptTone(a) {
|
|
51
|
+
if (a.failed || a.timed_out) return "text-red-600 dark:text-red-400";
|
|
52
|
+
if (a.passed) return "text-green-600 dark:text-green-400";
|
|
53
|
+
return "text-muted-foreground";
|
|
54
|
+
}
|
|
55
|
+
function TestAttempts({ attempts }) {
|
|
56
|
+
if (attempts.length === 0) return null;
|
|
57
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
58
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: [
|
|
59
|
+
"Attempts (",
|
|
60
|
+
attempts.length,
|
|
61
|
+
")"
|
|
62
|
+
] }),
|
|
63
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "divide-y divide-border rounded-md border border-border text-xs", children: attempts.map((a) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center justify-between gap-2 px-2 py-1", children: [
|
|
64
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: utils.cn("font-medium", attemptTone(a)), children: [
|
|
65
|
+
"#",
|
|
66
|
+
a.sequence,
|
|
67
|
+
" ",
|
|
68
|
+
a.run_kind ?? ""
|
|
69
|
+
] }),
|
|
70
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-muted-foreground", children: a.message }),
|
|
71
|
+
a.duration ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground", children: status.formatTestDuration(a.duration) }) : null
|
|
72
|
+
] }, a.sequence)) })
|
|
73
|
+
] });
|
|
74
|
+
}
|
|
75
|
+
exports.TestAttempts = TestAttempts;
|
|
76
|
+
exports.TestFailureDetail = TestFailureDetail;
|
|
77
|
+
exports.TestOutput = TestOutput;
|
|
78
|
+
//# sourceMappingURL=TestFailureDetail.cjs.map
|