diff --git a/src/features/run/RunWidget.test.tsx b/src/features/run/RunWidget.test.tsx index abd5fdea8..858fa3b3e 100644 --- a/src/features/run/RunWidget.test.tsx +++ b/src/features/run/RunWidget.test.tsx @@ -3,7 +3,8 @@ import { cleanup, fireEvent, render, screen } from "@testing-library/react"; import { executeAction } from "@/platform/actions"; import { setContext } from "@/platform/context"; import type { RunConfig } from "@/platform/ipc"; -import { useDebugRunSessions, type DebugRunSession } from "@/workbench/contributions"; +import { registerMenuGroup, useDebugRunSessions, type DebugRunSession } from "@/workbench/contributions"; +import { useContextMenuStore } from "@/ui"; import { applicationConfig } from "./application"; import { activate } from "./index"; import { RunWidget } from "./RunWidget"; @@ -93,11 +94,39 @@ it("targets the selected debug session while npm runs, through buttons and short await executeAction("run.run"); expect(session.rerun).toHaveBeenCalledTimes(2); fireEvent.click(screen.getByRole("button", { name: `Stop '${app.name}'` })); - await executeAction("run.stop"); - expect(session.stop).toHaveBeenCalledTimes(2); + await vi.waitFor(() => expect(session.stop).toHaveBeenCalledOnce()); expect(useRun.getState().sessions[0]?.stopped).toBeUndefined(); }); +it("lists every running process with Stop All for the Stop shortcut, like IDEA", async () => { + const session = debug(app); + useRun.setState({ sessions: [run(npm, 3)] }); + useDebugRunSessions.setState({ sessions: [session] }); + const unregister = registerMenuGroup({ + id: "test.stopDebug", + menus: ["stopProcess"], + items: () => [{ label: app.name, run: () => session.stop() }], + }); + try { + render(); + await executeAction("run.stop"); + const items = useContextMenuStore.getState().menu!.items; + expect(items.map((i) => ("label" in i ? i.label : i.kind))).toEqual([ + "Stop Process", + npm.name, + app.name, + "separator", + "Stop All", + ]); + const stopAll = items.at(-1) as { run: () => void }; + stopAll.run(); + expect(session.stop).toHaveBeenCalledOnce(); + expect(useRun.getState().sessions[0]?.stopped).toBe(true); + } finally { + unregister(); + } +}); + it("ignores an unrelated debug session for an idle selection", () => { useDebugRunSessions.setState({ sessions: [debug(npm)] }); render(); diff --git a/src/features/run/RunWidget.tsx b/src/features/run/RunWidget.tsx index bfb150cd8..2254b24b8 100644 --- a/src/features/run/RunWidget.tsx +++ b/src/features/run/RunWidget.tsx @@ -144,7 +144,7 @@ export function RunWidget() { className={clsx(styles.widgetButton, styles.widgetFilled, styles.widgetFilledStop)} title={active || count === 1 ? tx`Stop '${stopName ?? ""}'` : tx`Stop (${count} running)`} shortcut={shortcutLabel("run.stop")} - onClick={() => void executeAction("run.stop")} + onClick={() => void executeAction("run.stop", "widget")} /> )} s.stop()); - return; - } - const target = selectedTarget(useRun.getState().selectedId, running, debug); - if (target) { - target.stop(); - return; + if (arg === "widget") { + const compound = compoundTargets(selectedConfig(), running, debug); + if (compound.length) { + compound.forEach((s) => s.stop()); + return; + } + const target = selectedTarget(useRun.getState().selectedId, running, debug); + if (target) { + target.stop(); + return; + } } const items = [ ...running.map((s): MenuItem => ({ @@ -357,7 +363,7 @@ export function activate(): () => void { category: "Run", icon: "stop", when: "projectOpen", - run: () => stopAction(), + run: ({ arg }) => stopAction(arg), }, { id: "run.rerun",