feat(team-mode): add rebalance-team-window command with tests
This commit is contained in:
@@ -0,0 +1,84 @@
|
|||||||
|
/// <reference types="bun-types" />
|
||||||
|
|
||||||
|
import { beforeEach, describe, expect, it, mock } from "bun:test"
|
||||||
|
|
||||||
|
import {
|
||||||
|
rebalanceTeamWindowWith,
|
||||||
|
type RebalanceTeamWindowDeps,
|
||||||
|
} from "./rebalance-team-window"
|
||||||
|
|
||||||
|
describe("rebalanceTeamWindowWith", () => {
|
||||||
|
let runTmux: RebalanceTeamWindowDeps["runTmux"]
|
||||||
|
let log: RebalanceTeamWindowDeps["log"]
|
||||||
|
let calls: Array<Array<string>>
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
calls = []
|
||||||
|
runTmux = mock(async (args: string[]): Promise<{ success: boolean }> => {
|
||||||
|
calls.push(args)
|
||||||
|
return { success: true }
|
||||||
|
})
|
||||||
|
log = mock((): void => undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("#given main-vertical #when rebalance #then select-layout, set main-pane-width 60%, re-select-layout", async () => {
|
||||||
|
// given
|
||||||
|
const deps: RebalanceTeamWindowDeps = { runTmux, log }
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await rebalanceTeamWindowWith("@1", "main-vertical", deps)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result).toBe(true)
|
||||||
|
expect(calls).toEqual([
|
||||||
|
["select-layout", "-t", "@1", "main-vertical"],
|
||||||
|
["set-window-option", "-t", "@1", "main-pane-width", "60%"],
|
||||||
|
["select-layout", "-t", "@1", "main-vertical"],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("#given focus windowId and pane-list shrunk from 3 to 2 #when rebalanceTeamWindow runs #then select-layout is invoked with main-vertical", async () => {
|
||||||
|
// given
|
||||||
|
const deps: RebalanceTeamWindowDeps = { runTmux, log }
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await rebalanceTeamWindowWith("@focus", "main-vertical", deps)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result).toBe(true)
|
||||||
|
expect(calls).toEqual([
|
||||||
|
["select-layout", "-t", "@focus", "main-vertical"],
|
||||||
|
["set-window-option", "-t", "@focus", "main-pane-width", "60%"],
|
||||||
|
["select-layout", "-t", "@focus", "main-vertical"],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("#given tiled #when rebalance #then only select-layout called", async () => {
|
||||||
|
// given
|
||||||
|
const deps: RebalanceTeamWindowDeps = { runTmux, log }
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await rebalanceTeamWindowWith("@1", "tiled", deps)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result).toBe(true)
|
||||||
|
expect(calls).toEqual([["select-layout", "-t", "@1", "tiled"]])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("#given select-layout fails #when rebalance #then returns false, log once", async () => {
|
||||||
|
// given
|
||||||
|
runTmux = mock(async (args: string[]): Promise<{ success: boolean }> => {
|
||||||
|
calls.push(args)
|
||||||
|
return { success: false }
|
||||||
|
})
|
||||||
|
|
||||||
|
const deps: RebalanceTeamWindowDeps = { runTmux, log }
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await rebalanceTeamWindowWith("@1", "main-vertical", deps)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result).toBe(false)
|
||||||
|
expect(log).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
export type RebalanceLayout = "main-vertical" | "tiled"
|
||||||
|
|
||||||
|
export type RebalanceTeamWindowDeps = {
|
||||||
|
runTmux: (args: string[]) => Promise<{ success: boolean }>
|
||||||
|
log: (message: string, meta?: Record<string, unknown>) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function rebalanceTeamWindowWith(
|
||||||
|
windowId: string,
|
||||||
|
layout: RebalanceLayout,
|
||||||
|
deps: RebalanceTeamWindowDeps,
|
||||||
|
): Promise<boolean> {
|
||||||
|
if (windowId.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectLayoutArgs = ["select-layout", "-t", windowId, layout]
|
||||||
|
const initialLayout = await deps.runTmux(selectLayoutArgs)
|
||||||
|
if (!initialLayout.success) {
|
||||||
|
deps.log("[rebalanceTeamWindow] FAILED", { windowId, layout, step: "select-layout" })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layout === "tiled") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const setMainPaneWidth = await deps.runTmux([
|
||||||
|
"set-window-option",
|
||||||
|
"-t",
|
||||||
|
windowId,
|
||||||
|
"main-pane-width",
|
||||||
|
"60%",
|
||||||
|
])
|
||||||
|
if (!setMainPaneWidth.success) {
|
||||||
|
deps.log("[rebalanceTeamWindow] FAILED", { windowId, layout, step: "set-window-option" })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// tmux applies main-pane-width against the active layout, so select-layout again after resizing.
|
||||||
|
const finalLayout = await deps.runTmux(selectLayoutArgs)
|
||||||
|
if (!finalLayout.success) {
|
||||||
|
deps.log("[rebalanceTeamWindow] FAILED", { windowId, layout, step: "select-layout" })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function rebalanceTeamWindow(
|
||||||
|
windowId: string,
|
||||||
|
layout: RebalanceLayout,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const [{ log }, { getTmuxPath }, { runTmuxCommand }] = await Promise.all([
|
||||||
|
import("../../../shared"),
|
||||||
|
import("../../../tools/interactive-bash/tmux-path-resolver"),
|
||||||
|
import("../../../shared/tmux"),
|
||||||
|
])
|
||||||
|
|
||||||
|
const tmuxPath = await getTmuxPath()
|
||||||
|
if (!tmuxPath) {
|
||||||
|
log("[rebalanceTeamWindow] SKIP: tmux not found", { windowId, layout })
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return rebalanceTeamWindowWith(windowId, layout, {
|
||||||
|
runTmux: (args) => runTmuxCommand(tmuxPath, args),
|
||||||
|
log,
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user