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