Skip to content

Commit 52138bc

Browse files
timbrown5timjastuartrowe
authored
Add step logs to stage view - as expandable sections. (#210)
Co-authored-by: Tim Jacomb <[email protected]> Co-authored-by: Stuart Rowe <[email protected]> Co-authored-by: Tim Jacomb <[email protected]>
1 parent 1500c5e commit 52138bc

File tree

13 files changed

+992
-373
lines changed

13 files changed

+992
-373
lines changed

package-lock.json

Lines changed: 20 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"@reacticons/ionicons": "6.0.4",
3030
"html-react-parser": "1.4.14",
3131
"react": "18.2.0",
32-
"react-dom": "18.2.0"
32+
"react-dom": "18.2.0",
33+
"react-virtuoso": "^4.1.0"
3334
},
3435
"devDependencies": {
3536
"@babel/preset-env": "7.19.4",

src/main/frontend/pipeline-console-view/pipeline-console/main/Ansi.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export function parseEscapeCode(escapeCode: string): Result {
6464
result.resetFG = true;
6565
result.setFG = false;
6666
}
67-
67+
6868
if (num === 48 || num === 0) {
6969
result.resetBG = true;
7070
result.setBG = false;
@@ -123,7 +123,10 @@ export function tokenizeANSIString(input?: string): string[] | Result[] {
123123
if (commentEndIndex !== -1) {
124124
commentEndIndex += 3;
125125
}
126-
if (escapeCodeIndex > commentStartIndex && escapeCodeIndex < commentEndIndex) {
126+
if (
127+
escapeCodeIndex > commentStartIndex &&
128+
escapeCodeIndex < commentEndIndex
129+
) {
127130
// Skip past the comment
128131
loopCounter = commentEndIndex;
129132
continue;
@@ -152,7 +155,9 @@ export function tokenizeANSIString(input?: string): string[] | Result[] {
152155
// Create token for the escape code
153156

154157
// TODO fix type checking
155-
const parsedEscapeCode: any = parseEscapeCode(input.substring(loopCounter, escapeCodeIndex + 1));
158+
const parsedEscapeCode: any = parseEscapeCode(
159+
input.substring(loopCounter, escapeCodeIndex + 1)
160+
);
156161
result.push(parsedEscapeCode);
157162

158163
//--------------------------------------------------------------------------
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from "react";
2+
3+
import { makeReactChildren, tokenizeANSIString } from "./Ansi";
4+
5+
export interface ConsoleLineProps {
6+
lineNumber: string;
7+
content: string;
8+
stepId: string;
9+
startByte: number;
10+
}
11+
12+
// Console output line
13+
export const ConsoleLine = (props: ConsoleLineProps) => (
14+
<pre
15+
className="console-output-line"
16+
key={`console-line-pre${props.lineNumber}`}
17+
>
18+
<div
19+
className="console-output-line-anchor"
20+
id={`log-${props.lineNumber}`}
21+
key={`${props.lineNumber}-anchor`}
22+
/>
23+
<div className="console-output-line" key={`${props.lineNumber}-body`}>
24+
<a
25+
className="console-line-number"
26+
href={`?start-byte=${props.startByte}&selected-node=${props.stepId}#log-${props.lineNumber}`} //`}
27+
>
28+
{props.lineNumber}
29+
</a>
30+
{makeReactChildren(
31+
tokenizeANSIString(props.content),
32+
`${props.stepId}-${props.lineNumber}`
33+
)}
34+
</div>
35+
</pre>
36+
);
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/** * @jest-environment jsdom */
2+
3+
import "@testing-library/jest-dom/extend-expect";
4+
import { waitFor } from "@testing-library/react";
5+
import React, { ReactElement } from "react";
6+
import { ConsoleLogCard } from "./ConsoleLogCard";
7+
import type { ConsoleLogCardProps } from "./ConsoleLogCard";
8+
import { Result, StepInfo } from "./PipelineConsoleModel";
9+
import { render } from "@testing-library/react";
10+
import { VirtuosoMockContext } from "react-virtuoso";
11+
12+
function sleep(ms: number) {
13+
return new Promise((resolve) => setTimeout(resolve, ms));
14+
}
15+
describe("ConsoleLogCard", () => {
16+
const baseStep: StepInfo = {
17+
name: "This is a step",
18+
title: "This is a title",
19+
state: Result.success,
20+
completePercent: 50,
21+
id: 2,
22+
type: "STAGE",
23+
pauseDurationMillis: "",
24+
startTimeMillis: "",
25+
totalDurationMillis: "",
26+
stageId: "1",
27+
consoleLines: ["Hello, world!"],
28+
consoleStartByte: 0,
29+
consoleEndByte: 13,
30+
};
31+
32+
const TestComponent = (props: ConsoleLogCardProps) => {
33+
return (
34+
<div id="test-parent">
35+
<ConsoleLogCard {...props} />
36+
</div>
37+
);
38+
};
39+
40+
const DefaultTestProps = {
41+
step: baseStep,
42+
isExpanded: false,
43+
handleStepToggle: () => {
44+
console.log("handleStepToggle triggered");
45+
},
46+
handleMoreConsoleClick: () => {
47+
console.log("handleMoreConsoleClick triggered");
48+
},
49+
scrollParentId: "test-parent",
50+
} as ConsoleLogCardProps;
51+
52+
function renderInContext(element: ReactElement) {
53+
return render(element, {
54+
wrapper: ({ children }) => (
55+
<VirtuosoMockContext.Provider
56+
value={{ viewportHeight: 300, itemHeight: 100 }}
57+
>
58+
{children}
59+
</VirtuosoMockContext.Provider>
60+
),
61+
});
62+
}
63+
64+
it("renders step header only when not expanded", async () => {
65+
const { getByText } = renderInContext(
66+
TestComponent({ ...DefaultTestProps })
67+
);
68+
expect(getByText(/This is a step/));
69+
});
70+
71+
it("renders step console when expanded", async () => {
72+
const { getByText } = renderInContext(
73+
TestComponent({ ...DefaultTestProps, isExpanded: true })
74+
);
75+
expect(getByText(/This is a step/));
76+
waitFor(() => {
77+
expect(getByText(/Hello, world!/));
78+
});
79+
});
80+
});

0 commit comments

Comments
 (0)