spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

claude-code / mods / diff / tests / views.test.ts
246 lines · 8.4 KBRaw
1import { describe, expect, test, tier } from 'claude-code/testing'
2
3import Fixtures from './fixtures'
4
5tier('builtin')
6
7describe('views', () => {
8 test('docked, every file has its hunks under the list', async ($, on) => {
9 const world = Fixtures.inRepository(on, Fixtures.TWO_FILES)
10
11 await $.session.start(Fixtures.SESSION)
12 await $.command.run(Fixtures.DIFF)
13 await world.clock.advance(Fixtures.SETTLE_MS)
14
15 const drawn = Fixtures.textOf(await $.ui.render(Fixtures.PANE))
16
17 expect(drawn).toContain('2 files changed +3 -1')
18
19 expect(drawn, "a closing empty row, as the built-in's").toContain(
20 '+const a = 2\n ',
21 )
22
23 expect(drawn, 'none after the last file').toMatch(/\+export const c = 2$/)
24 expect(drawn).not.toContain('❯')
25 })
26
27 test('inline, a row opens that file alone', async ($, on) => {
28 const world = Fixtures.inRepository(on, Fixtures.TWO_FILES)
29
30 await $.session.start(Fixtures.SESSION)
31 await $.command.run(Fixtures.DIALOG_DIFF)
32 await world.clock.advance(Fixtures.SETTLE_MS)
33 await $.ui.render(Fixtures.INLINE_PANE)
34
35 expect(await $.ui.press({ plugin: 'diff', key: 'file:lib.ts' })).toEqual({
36 element: 'file:lib.ts',
37 })
38
39 const drawn = Fixtures.textOf(await $.ui.render(Fixtures.INLINE_PANE))
40
41 expect(drawn).toContain('+export const c = 2')
42 expect(drawn).not.toContain('+const a = 2')
43 expect(drawn).not.toContain('app.ts')
44 expect(drawn).toContain('\u2191/\u2193 to scroll \u00b7 Esc to back')
45 })
46
47 test('docked, a wheel tick moves the body, not the list', async ($, on) => {
48 const world = Fixtures.inRepository(on, Fixtures.MANY_FILES)
49
50 await $.session.start(Fixtures.SESSION)
51 await $.command.run(Fixtures.DIFF)
52 await world.clock.advance(Fixtures.SETTLE_MS)
53
54 const before = Fixtures.textOf(await $.ui.render(Fixtures.PANE))
55
56 expect(before, 'listed, and named over its body').toContain('file0.ts')
57 expect(before.split('file0.ts')).toHaveLength(3)
58
59 expect(await $.ui.scroll(Fixtures.WHEEL_TICK)).toEqual({})
60
61 const after = Fixtures.textOf(await $.ui.render(Fixtures.PANE))
62
63 expect(after.split('file0.ts'), 'its name row scrolled away').toHaveLength(
64 2,
65 )
66
67 expect(after).toContain('-const v = 0')
68 expect(after).toContain('10 files changed +10 -10')
69 expect(after, 'the list stays').not.toContain('more above')
70 })
71
72 test('docked, the wheel over a long list moves the list', async ($, on) => {
73 const world = Fixtures.inRepository(on, Fixtures.MANY_FILES)
74
75 await $.session.start(Fixtures.SESSION)
76 await $.command.run(Fixtures.DIFF)
77 await world.clock.advance(Fixtures.SETTLE_MS)
78 await $.ui.render(Fixtures.PANE)
79
80 expect(await $.ui.scroll(Fixtures.WHEEL_OVER_LIST)).toEqual({})
81
82 const after = Fixtures.textOf(await $.ui.render(Fixtures.PANE))
83
84 expect(after).toContain('\u2191 1 more above')
85 expect(after).toContain('file8.ts')
86 expect(after.split('file0.ts'), 'the body stays').toHaveLength(2)
87 })
88
89 test('past eight files the docked list scrolls by key', async ($, on) => {
90 const world = Fixtures.inRepository(on, Fixtures.MANY_FILES)
91
92 await $.session.start(Fixtures.SESSION)
93 await $.command.run(Fixtures.DIFF)
94 await world.clock.advance(Fixtures.SETTLE_MS)
95
96 const drawn = Fixtures.textOf(await $.ui.render(Fixtures.PANE))
97
98 expect(drawn).toContain('10 files changed +10 -10')
99 expect(drawn).toContain('file7.ts')
100 expect(drawn).toContain('\u2193 2 more below (opt+\u2193 to scroll)')
101 expect(drawn).not.toContain('file8.ts')
102
103 expect(await $.ui.press({ plugin: 'diff', key: 'list-down' })).toEqual({
104 element: 'list-down',
105 })
106
107 const scrolled = Fixtures.textOf(await $.ui.render(Fixtures.PANE))
108
109 expect(scrolled).toContain('\u2191 1 more above')
110 expect(scrolled).toContain('file8.ts')
111 expect(scrolled).toContain('\u2193 1 more below')
112 })
113
114 test('a rename lists as git prints it, and reads no body', async ($, on) => {
115 const world = Fixtures.inRepository(on, Fixtures.RENAMED)
116
117 await $.session.start(Fixtures.SESSION)
118 await $.command.run(Fixtures.DIFF)
119 await world.clock.advance(Fixtures.SETTLE_MS)
120
121 const drawn = Fixtures.textOf(await $.ui.render(Fixtures.PANE))
122
123 expect(drawn).toContain('1 file changed')
124 expect(drawn).toContain('docs/{notes.txt => renamed-notes.txt}')
125 expect(drawn).toContain('No diff content')
126
127 expect(
128 world.runs.filter(run => run.argv.includes('docs/renamed-notes.txt')),
129 ).toEqual([])
130 })
131
132 test('inline, the dialog lists every file and its keys', async ($, on) => {
133 const world = Fixtures.inRepository(on, Fixtures.TWO_FILES)
134
135 await $.session.start(Fixtures.SESSION)
136 await $.command.run(Fixtures.DIALOG_DIFF)
137 await world.clock.advance(Fixtures.SETTLE_MS)
138
139 const drawn = Fixtures.textOf(await $.ui.render(Fixtures.INLINE_PANE))
140
141 expect(drawn).toContain('Uncommitted changes (git diff HEAD)')
142 expect(drawn).toContain('2 files changed +3 -1')
143 expect(drawn).toContain('\u276f app.ts')
144 expect(drawn).toContain(' lib.ts')
145 expect(drawn).not.toContain('+const a = 2')
146
147 expect(drawn).toContain(
148 '\u2191/\u2193 to select \u00b7 Enter to view \u00b7 Esc to close',
149 )
150
151 expect(world.opened.at(-1), 'sized to its rows once listed').toEqual({
152 id: 'diff',
153 title: 'Diff',
154 holdToasts: true,
155 closeOnEscape: true,
156 rows: 8,
157 })
158 })
159
160 test('narrow under the fullscreen layout, the resize line', async ($, on) => {
161 const world = Fixtures.inRepository(on, Fixtures.TWO_FILES)
162
163 await $.session.start(Fixtures.SESSION)
164 await $.command.run(Fixtures.DIFF)
165 await world.clock.advance(Fixtures.SETTLE_MS)
166
167 expect(
168 Fixtures.textOf(await $.ui.render(Fixtures.NARROW_INLINE_PANE)),
169 ).toBe(
170 'Resize your terminal to at least 110 columns to show the diff panel',
171 )
172 })
173
174 test('off fullscreen, the window follows the walk', async ($, on) => {
175 const world = Fixtures.inRepository(on, Fixtures.MANY_FILES)
176
177 await $.session.start(Fixtures.SESSION)
178 await $.command.run(Fixtures.DIALOG_DIFF)
179 await world.clock.advance(Fixtures.SETTLE_MS)
180
181 const drawn = Fixtures.textOf(await $.ui.render(Fixtures.INLINE_PANE))
182
183 expect(drawn).toContain('\u276f file0.ts')
184 expect(drawn).toContain(' file4.ts')
185 expect(drawn).not.toContain('file5.ts')
186 expect(drawn).toContain(' \u2193 5 more files')
187 expect(await $.ui.focus(Fixtures.ringOnto('file:file3.ts'))).toEqual({})
188
189 expect(
190 world.focused.map(focus => focus.element),
191 'its row once centred',
192 ).toEqual(['file:file2.ts'])
193
194 const walked = Fixtures.textOf(await $.ui.render(Fixtures.INLINE_PANE))
195
196 expect(walked).toContain(' \u2191 1 more file')
197 expect(walked).toContain('\u276f file3.ts')
198 expect(walked).toContain(' file5.ts')
199 expect(walked).toContain(' \u2193 4 more files')
200 expect(walked).not.toContain('file0.ts')
201 })
202
203 test('off fullscreen, the walk stops at the last file', async ($, on) => {
204 const world = Fixtures.inRepository(on, Fixtures.MANY_FILES)
205
206 await $.session.start(Fixtures.SESSION)
207 await $.command.run(Fixtures.DIALOG_DIFF)
208 await world.clock.advance(Fixtures.SETTLE_MS)
209 await $.ui.render(Fixtures.INLINE_PANE)
210 await $.ui.focus(Fixtures.ringOnto('file:file3.ts'))
211 await $.ui.render(Fixtures.INLINE_PANE)
212 await $.ui.focus(Fixtures.ringOnto('file:file5.ts'))
213 await $.ui.render(Fixtures.INLINE_PANE)
214 await $.ui.focus(Fixtures.ringOnto('file:file7.ts'))
215 await $.ui.render(Fixtures.INLINE_PANE)
216 await $.ui.focus(Fixtures.ringOnto('file:file9.ts'))
217
218 const last = Fixtures.textOf(await $.ui.render(Fixtures.INLINE_PANE))
219
220 expect(last).toContain('\u276f file9.ts')
221 expect(last).toContain(' \u2191 5 more files')
222
223 expect(
224 await $.ui.focus(Fixtures.ringOnto('file:file5.ts')),
225 'the ring would wrap to the first row drawn; it stays',
226 ).toEqual({})
227
228 expect(Fixtures.textOf(await $.ui.render(Fixtures.INLINE_PANE))).toContain(
229 '\u276f file9.ts',
230 )
231
232 expect(world.focused, 'the wrap never reached the engine').toHaveLength(4)
233 })
234
235 test('off fullscreen, the dialog draws at any width', async ($, on) => {
236 const world = Fixtures.inRepository(on, Fixtures.TWO_FILES)
237
238 await $.session.start(Fixtures.SESSION)
239 await $.command.run(Fixtures.DIALOG_DIFF)
240 await world.clock.advance(Fixtures.SETTLE_MS)
241
242 expect(
243 Fixtures.textOf(await $.ui.render(Fixtures.NARROW_INLINE_PANE)),
244 ).toContain('Uncommitted changes (git diff HEAD)')
245 })
246})