Skip to content

Commit

Permalink
Merge pull request #1584 from flanksource/mainawycliffe/issue1580
Browse files Browse the repository at this point in the history
Mainawycliffe/issue1580
  • Loading branch information
moshloop authored Jan 4, 2024
2 parents 1dc7a33 + db17364 commit 75806d7
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 21 deletions.
6 changes: 3 additions & 3 deletions src/api/types/playbooks.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Agent, CreatedAt, Avatar } from "../traits";
import { Agent, Avatar, CreatedAt } from "../traits";
import { ConfigItem } from "./configs";
import { HealthCheck, HealthCheckSummary } from "./health";
import { HealthCheckSummary } from "./health";
import { Topology } from "./topology";
import { User } from "./users";
import { ElementType } from "react";

export type PlaybookRunStatus =
| "scheduled"
Expand All @@ -24,6 +23,7 @@ export type PlaybookRunAction = {
result?: {
stdout?: string;
logs?: string;
stderr?: string;
[key: string]: unknown;
};
error?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ export default function PlaybookRunActionFetch({ playbookRunActionId }: Props) {
});

if (isLoading || !action) {
return <Loading text="..." />;
return (
<div className="flex w-full h-full justify-center items-center">
<Loading text="Loading ..." />
</div>
);
}

return <PlaybooksRunActionsResults action={action} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { Age } from "../../../../ui/Age";
import { PlaybookStatusIcon } from "../../../Icon/PlaybookStatusIcon";

type PlaybookRunsActionItemProps = {
action: PlaybookRunAction;
action: Pick<
PlaybookRunAction,
"status" | "name" | "start_time" | "end_time" | "id"
>;
onClick?: () => void;
isSelected?: boolean;
stepNumber: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type PlaybookRunActionsProps = {

export default function PlaybookRunsActions({ data }: PlaybookRunActionsProps) {
const [selectedAction, setSelectedAction] = useState<PlaybookRunAction>();
let resource = getResourceForRun(data);
const resource = getResourceForRun(data);

return (
<div className="flex flex-col flex-1 gap-6">
Expand Down
40 changes: 32 additions & 8 deletions src/components/Playbooks/Runs/Actions/PlaybooksActionsResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,32 @@ import { PlaybookRunAction } from "../../../../api/types/playbooks";

const convert = new Convert();

function DisplayStdout({ stdout }: { stdout?: string }) {
if (!stdout) {
return null;
}
const html = convert.toHtml(stdout);
return <pre dangerouslySetInnerHTML={{ __html: html }} />;
}

function DisplayStderr({ stderr }: { stderr?: string }) {
if (!stderr) {
return null;
}
const html = convert.toHtml(stderr);
return (
<pre className="text-red-500" dangerouslySetInnerHTML={{ __html: html }} />
);
}

function DisplayLogs({ logs }: { logs?: string }) {
if (!logs) {
return null;
}
const html = convert.toHtml(logs);
return <pre dangerouslySetInnerHTML={{ __html: html }} />;
}

type Props = {
action: Pick<PlaybookRunAction, "result" | "error">;
className?: string;
Expand All @@ -22,15 +48,13 @@ export default function PlaybooksRunActionsResults({
return <pre className={className}>{action.error}</pre>;
}

if (result?.stdout) {
return <pre className={className}>{result.stdout}</pre>;
}

if (result?.logs) {
const html = convert.toHtml(result.logs);

if (result?.stderr || result?.stdout || result?.logs) {
return (
<pre className={className} dangerouslySetInnerHTML={{ __html: html }} />
<div className={`flex flex-col gap-2 ${className}`}>
<DisplayStdout stdout={result?.stdout} />
<DisplayStderr stderr={result?.stderr} />
<DisplayLogs logs={result?.logs} />
</div>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { fireEvent, render, screen } from "@testing-library/react";
import { rest } from "msw";
import { setupServer } from "msw/node";
import {
PlaybookRunAction,
PlaybookRunWithActions
} from "../../../../../api/types/playbooks";
import PlaybookRunsActions from "./../PlaybookRunsActions";

const mockAction: PlaybookRunAction = {
playbook_run_id: "1",
id: "1",
name: "Test Action",
status: "completed",
start_time: "2022-01-01T00:00:00Z",
end_time: "2022-01-01T01:00:00Z"
};

const server = setupServer(
rest.get("/api/db/playbook_run_actions", (req, res, ctx) => {
return res(ctx.json([mockAction]));
})
);

describe.skip("PlaybookRunsActions", () => {
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

it("renders correctly", async () => {
const mockData: PlaybookRunWithActions = {
id: "1",
playbook_id: "1",
playbooks: {
name: "Test Playbook",
id: "1",
source: "UI",
created_at: "2022-01-01T00:00:00Z",
spec: {},
updated_at: "2022-01-01T00:00:00Z"
},
status: "completed",
start_time: "2022-01-01T00:00:00Z",
end_time: "2022-01-01T01:00:00Z",
created_at: "2022-01-01T00:00:00Z",
actions: [
{
playbook_run_id: "1",
id: "1",
name: "Test Action 1",
status: "completed",
start_time: "2022-01-01T00:00:00Z",
end_time: "2022-01-01T01:00:00Z"
},
{
playbook_run_id: "1",
id: "2",
name: "Test Action 2",
status: "completed",
start_time: "2022-01-01T00:00:00Z",
end_time: "2022-01-01T01:00:00Z"
}
]
// other fields as needed
};

render(<PlaybookRunsActions data={mockData} />);

expect(screen.getByText("Test Playbook")).toBeInTheDocument();
expect(screen.getByText("Test Action 1")).toBeInTheDocument();
expect(screen.getByText("Test Action 2")).toBeInTheDocument();

// Click on an action to trigger the network request
fireEvent.click(screen.getByText("Test Action 1"));

// Wait for the request to complete
await screen.findByText(/some text from the response/i);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from "react";
import { render, fireEvent, screen } from "@testing-library/react";
import PlaybookRunsActionItem from "./../PlaybookRunsActionItem";
import { PlaybookRunAction } from "../../../../../api/types/playbooks";

describe("PlaybookRunsActionItem", () => {
const mockAction: PlaybookRunAction = {
playbook_run_id: "1",
id: "1",
name: "Test Action",
status: "completed",
start_time: "2022-01-01T00:00:00Z",
end_time: "2022-01-01T01:00:00Z"
};

const mockOnClick = jest.fn();

it("renders correctly", () => {
render(
<PlaybookRunsActionItem
action={mockAction}
onClick={mockOnClick}
isSelected={false}
/>
);

expect(screen.getByRole("button")).toBeInTheDocument();
expect(screen.getByText("Test Action")).toBeInTheDocument();
});

it("calls onClick when clicked", () => {
render(
<PlaybookRunsActionItem
action={mockAction}
onClick={mockOnClick}
isSelected={false}
/>
);

fireEvent.click(screen.getByRole("button"));

expect(mockOnClick).toHaveBeenCalled();
});

it("has correct style when selected", () => {
render(
<PlaybookRunsActionItem
action={mockAction}
onClick={mockOnClick}
isSelected={true}
/>
);

expect(screen.getByRole("button")).toHaveClass("bg-gray-200");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ describe("PlaybooksRunActionsResults", () => {
).toBeInTheDocument();
});

it("applies the className prop to the pre element", () => {
const action = { result: { stdout: "Hello, world!" } };
render(
<PlaybooksRunActionsResults action={action} className="text-red-500" />
);
expect(screen.getByText("Hello, world!")).toHaveClass("text-red-500");
it("shows both stdout and stderr when both are present", () => {
const action = {
result: { stdout: "Hello, world!", stderr: "Goodbye, world!" }
};
render(<PlaybooksRunActionsResults action={action} />);
expect(screen.getByText("Hello, world!")).toBeInTheDocument();
expect(screen.getByText("Goodbye, world!")).toBeInTheDocument();
});

it("renders error when action has error", () => {
Expand Down
1 change: 0 additions & 1 deletion src/ui/Age/Age.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export default function Age({

let duration = dayjs.duration(_to.diff(_from));

console.log(from, to, duration);
if (duration.asMilliseconds() < 1000) {
return (
<span title={_from.format()} className={className}>
Expand Down

0 comments on commit 75806d7

Please sign in to comment.