v0.27.2 5960dd8

workspace_close_test.go RawHistoryFolder

1package coordinator
2
3import (
4	"strings"
5	"sync"
6	"testing"
7
8	"github.com/bnema/dumber/internal/domain/entity"
9)
10
11func testLeafNode(id string) *entity.PaneNode {
12	return &entity.PaneNode{
13		ID:   id,
14		Pane: entity.NewPane(entity.PaneID(id)),
15	}
16}
17
18func testSplitNode(id string, left, right *entity.PaneNode) *entity.PaneNode {
19	node := &entity.PaneNode{
20		ID:       id,
21		SplitDir: entity.SplitHorizontal,
22		Children: []*entity.PaneNode{left, right},
23	}
24	if left != nil {
25		left.Parent = node
26	}
27	if right != nil {
28		right.Parent = node
29	}
30	return node
31}
32
33func TestDeriveIncrementalCloseTreeContext_ValidSplit(t *testing.T) {
34	closing := testLeafNode("closing")
35	sibling := testLeafNode("sibling")
36	parent := testSplitNode("parent", closing, sibling)
37	other := testLeafNode("other")
38	grand := testSplitNode("grand", parent, other)
39
40	ctx, err := deriveIncrementalCloseTreeContext(closing)
41	if err != nil {
42		t.Fatalf("unexpected error: %v", err)
43	}
44
45	if ctx.parentNode != parent {
46		t.Fatalf("parent mismatch")
47	}
48	if ctx.siblingNode != sibling {
49		t.Fatalf("sibling mismatch")
50	}
51	if ctx.grandparentNode != grand {
52		t.Fatalf("grandparent mismatch")
53	}
54	if ctx.siblingIsStartChild {
55		t.Fatalf("sibling should be end child")
56	}
57	if !ctx.parentIsStartInGrand {
58		t.Fatalf("parent should be start child in grandparent")
59	}
60}
61
62func TestDeriveIncrementalCloseTreeContext_MissingSibling(t *testing.T) {
63	closing := testLeafNode("closing")
64	parent := testSplitNode("parent", closing, nil)
65
66	_, err := deriveIncrementalCloseTreeContext(closing)
67	if err == nil {
68		t.Fatalf("expected invariant error")
69	}
70	if !strings.Contains(err.Error(), "sibling") && !strings.Contains(err.Error(), "nil child") {
71		t.Fatalf("expected sibling/nil-child error, got: %v", err)
72	}
73
74	if got := paneNodeID(parent); got != "parent" {
75		t.Fatalf("unexpected parent id helper output: %s", got)
76	}
77}
78
79func TestDeriveIncrementalCloseTreeContext_ParentDoesNotContainClosingPane(t *testing.T) {
80	closing := testLeafNode("closing")
81	left := testLeafNode("left")
82	right := testLeafNode("right")
83	parent := testSplitNode("parent", left, right)
84	closing.Parent = parent
85
86	_, err := deriveIncrementalCloseTreeContext(closing)
87	if err == nil {
88		t.Fatalf("expected invariant error")
89	}
90	if !strings.Contains(err.Error(), "not found under parent") {
91		t.Fatalf("unexpected error: %v", err)
92	}
93}
94
95func TestCaptureIncrementalCloseContext_MissingSiblingSetsPrecheckReason(t *testing.T) {
96	closing := testLeafNode("closing")
97	testSplitNode("parent", closing, nil)
98
99	coord := &WorkspaceCoordinator{}
100	ctx := coord.captureIncrementalCloseContext(nil, closing)
101	if ctx.precheckReason == "" {
102		t.Fatalf("expected precheck reason")
103	}
104}
105
106func TestDeriveIncrementalCloseTreeContext_ConcurrentPaneAndTabCloseSnapshots(t *testing.T) {
107	paneCloseNode := testLeafNode("pane-close")
108	paneCloseSibling := testLeafNode("pane-close-sibling")
109	testSplitNode("pane-parent", paneCloseNode, paneCloseSibling)
110
111	tabCloseNode := testLeafNode("tab-close")
112	tabCloseSibling := testLeafNode("tab-close-sibling")
113	testSplitNode("tab-parent", tabCloseSibling, tabCloseNode)
114
115	run := func(node *entity.PaneNode, wg *sync.WaitGroup, errCh chan<- error) {
116		defer wg.Done()
117		for i := 0; i < 200; i++ {
118			if _, err := deriveIncrementalCloseTreeContext(node); err != nil {
119				errCh <- err
120				return
121			}
122		}
123	}
124
125	errCh := make(chan error, 2)
126	var wg sync.WaitGroup
127	wg.Add(2)
128	go run(paneCloseNode, &wg, errCh)
129	go run(tabCloseNode, &wg, errCh)
130	wg.Wait()
131	close(errCh)
132
133	for err := range errCh {
134		if err != nil {
135			t.Fatalf("unexpected concurrent derive error: %v", err)
136		}
137	}
138}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close