Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { destroyShellTerminal } from "@posthog/ui/features/terminal/destroyShellTerminal";
import { ErrorBoundary } from "@posthog/ui/shell/ErrorBoundary";
import { useCallback, useEffect, useRef, useState } from "react";
import { FOCUSABLE_SELECTOR } from "../../../utils/overlay";
import {
Expand Down Expand Up @@ -132,7 +133,12 @@ function GridCell({
zoom: zoom !== 1 ? zoom : undefined,
}}
>
<CommandCenterPanel cell={cell} isActiveSession={isActive} />
<ErrorBoundary
name="command-center-cell"
resetKey={cell.taskId ?? cell.terminalId ?? cell.cellIndex}
>
<CommandCenterPanel cell={cell} isActiveSession={isActive} />
</ErrorBoundary>
</div>
{isActive && (
<div className="pointer-events-none absolute inset-0 border-2 border-accent-9" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { act, renderHook } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import type { SelectorOption } from "./types";
import { useActionSelectorState } from "./useActionSelectorState";

function makeProps(
overrides: Partial<Parameters<typeof useActionSelectorState>[0]> = {},
) {
return {
options: [] as SelectorOption[],
multiSelect: false,
allowCustomInput: false,
hideSubmitButton: false,
currentStep: 0,
steps: undefined,
onSelect: vi.fn(),
onMultiSelect: vi.fn(),
onStepChange: vi.fn(),
onStepAnswer: vi.fn(),
...overrides,
};
}

describe("useActionSelectorState.selectCurrent", () => {
it("does not throw when there are no options (empty allOptions)", () => {
const { result } = renderHook(() => useActionSelectorState(makeProps()));

// Pressing Enter with an empty option list must be a no-op, not a crash.
expect(() => act(() => result.current.selectCurrent())).not.toThrow();
});

it("does not throw when the highlighted index is stale after options shrink", () => {
const options: SelectorOption[] = [
{ id: "a", label: "A" },
{ id: "b", label: "B" },
{ id: "c", label: "C" },
];
const { result, rerender } = renderHook(
(props: Parameters<typeof useActionSelectorState>[0]) =>
useActionSelectorState(props),
{ initialProps: makeProps({ options }) },
);

// Move the highlight to the last option, then remove every option.
act(() => result.current.moveUp());
rerender(makeProps({ options: [] }));

expect(() => act(() => result.current.selectCurrent())).not.toThrow();
});

it("still selects the highlighted option when one exists", () => {
const onSelect = vi.fn();
const options: SelectorOption[] = [{ id: "approve", label: "Approve" }];
const { result } = renderHook(() =>
useActionSelectorState(makeProps({ options, onSelect })),
);

act(() => result.current.selectCurrent());

expect(onSelect).toHaveBeenCalledWith("approve");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ export function useActionSelectorState({

const selectCurrent = useCallback(() => {
const selected = allOptions[selectedIndex];
if (!selected) return;

if (isSubmitOption(selected.id)) {
if (!showSubmitButton) {
Expand Down
Loading