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
264 lines · 9.0 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('the walk follows under the name a build registers', 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
211 expect(
212 await $.ui.focus(Fixtures.ringOnto('file:file3.ts', 'cc-plugin-diff')),
213 ).toEqual({})
214
215 expect(
216 world.focused.map(focus => focus.element),
217 'its row once centred',
218 ).toEqual(['file:file2.ts'])
219 })
220
221 test('off fullscreen, the walk stops at the last file', async ($, on) => {
222 const world = Fixtures.inRepository(on, Fixtures.MANY_FILES)
223
224 await $.session.start(Fixtures.SESSION)
225 await $.command.run(Fixtures.DIALOG_DIFF)
226 await world.clock.advance(Fixtures.SETTLE_MS)
227 await $.ui.render(Fixtures.INLINE_PANE)
228 await $.ui.focus(Fixtures.ringOnto('file:file3.ts'))
229 await $.ui.render(Fixtures.INLINE_PANE)
230 await $.ui.focus(Fixtures.ringOnto('file:file5.ts'))
231 await $.ui.render(Fixtures.INLINE_PANE)
232 await $.ui.focus(Fixtures.ringOnto('file:file7.ts'))
233 await $.ui.render(Fixtures.INLINE_PANE)
234 await $.ui.focus(Fixtures.ringOnto('file:file9.ts'))
235
236 const last = Fixtures.textOf(await $.ui.render(Fixtures.INLINE_PANE))
237
238 expect(last).toContain('\u276f file9.ts')
239 expect(last).toContain(' \u2191 5 more files')
240
241 expect(
242 await $.ui.focus(Fixtures.ringOnto('file:file5.ts')),
243 'the ring would wrap to the first row drawn; it stays',
244 ).toEqual({})
245
246 expect(Fixtures.textOf(await $.ui.render(Fixtures.INLINE_PANE))).toContain(
247 '\u276f file9.ts',
248 )
249
250 expect(world.focused, 'the wrap never reached the engine').toHaveLength(4)
251 })
252
253 test('off fullscreen, the dialog draws at any width', async ($, on) => {
254 const world = Fixtures.inRepository(on, Fixtures.TWO_FILES)
255
256 await $.session.start(Fixtures.SESSION)
257 await $.command.run(Fixtures.DIALOG_DIFF)
258 await world.clock.advance(Fixtures.SETTLE_MS)
259
260 expect(
261 Fixtures.textOf(await $.ui.render(Fixtures.NARROW_INLINE_PANE)),
262 ).toContain('Uncommitted changes (git diff HEAD)')
263 })
264})