]>
Commit | Line | Data |
---|---|---|
1 | fs = require 'fs-plus' | |
2 | path = require 'path' | |
3 | os = require 'os' | |
4 | uuid = require 'node-uuid' | |
5 | helpers = require './spec-helper' | |
6 | ||
7 | Ex = require('../lib/ex').singleton() | |
8 | ||
9 | describe "the commands", -> | |
10 | [editor, editorElement, vimState, exState, dir, dir2] = [] | |
11 | projectPath = (fileName) -> path.join(dir, fileName) | |
12 | beforeEach -> | |
13 | vimMode = atom.packages.loadPackage('vim-mode') | |
14 | exMode = atom.packages.loadPackage('ex-mode') | |
15 | exMode.activate() | |
16 | ||
17 | waitsForPromise -> | |
18 | vimMode.activate().then -> | |
19 | helpers.activateExMode().then -> | |
20 | dir = path.join(os.tmpdir(), "atom-ex-mode-spec-#{uuid.v4()}") | |
21 | dir2 = path.join(os.tmpdir(), "atom-ex-mode-spec-#{uuid.v4()}") | |
22 | fs.makeTreeSync(dir) | |
23 | fs.makeTreeSync(dir2) | |
24 | atom.project.setPaths([dir, dir2]) | |
25 | ||
26 | helpers.getEditorElement (element) -> | |
27 | atom.commands.dispatch(element, 'ex-mode:open') | |
28 | keydown('escape') | |
29 | editorElement = element | |
30 | editor = editorElement.getModel() | |
31 | vimState = vimMode.mainModule.getEditorState(editor) | |
32 | exState = exMode.mainModule.exStates.get(editor) | |
33 | vimState.activateNormalMode() | |
34 | vimState.resetNormalMode() | |
35 | editor.setText("abc\ndef\nabc\ndef") | |
36 | ||
37 | afterEach -> | |
38 | fs.removeSync(dir) | |
39 | fs.removeSync(dir2) | |
40 | ||
41 | keydown = (key, options={}) -> | |
42 | options.element ?= editorElement | |
43 | helpers.keydown(key, options) | |
44 | ||
45 | normalModeInputKeydown = (key, opts = {}) -> | |
46 | editor.normalModeInputView.editorElement.getModel().setText(key) | |
47 | ||
48 | submitNormalModeInputText = (text) -> | |
49 | commandEditor = editor.normalModeInputView.editorElement | |
50 | commandEditor.getModel().setText(text) | |
51 | atom.commands.dispatch(commandEditor, "core:confirm") | |
52 | ||
53 | describe ":write", -> | |
54 | describe "when editing a new file", -> | |
55 | beforeEach -> | |
56 | editor.getBuffer().setText('abc\ndef') | |
57 | ||
58 | it "opens the save dialog", -> | |
59 | spyOn(atom, 'showSaveDialogSync') | |
60 | keydown(':') | |
61 | submitNormalModeInputText('write') | |
62 | expect(atom.showSaveDialogSync).toHaveBeenCalled() | |
63 | ||
64 | it "saves when a path is specified in the save dialog", -> | |
65 | filePath = projectPath('write-from-save-dialog') | |
66 | spyOn(atom, 'showSaveDialogSync').andReturn(filePath) | |
67 | keydown(':') | |
68 | submitNormalModeInputText('write') | |
69 | expect(fs.existsSync(filePath)).toBe(true) | |
70 | expect(fs.readFileSync(filePath, 'utf-8')).toEqual('abc\ndef') | |
71 | ||
72 | it "saves when a path is specified in the save dialog", -> | |
73 | spyOn(atom, 'showSaveDialogSync').andReturn(undefined) | |
74 | spyOn(fs, 'writeFileSync') | |
75 | keydown(':') | |
76 | submitNormalModeInputText('write') | |
77 | expect(fs.writeFileSync.calls.length).toBe(0) | |
78 | ||
79 | describe "when editing an existing file", -> | |
80 | filePath = '' | |
81 | i = 0 | |
82 | ||
83 | beforeEach -> | |
84 | i++ | |
85 | filePath = projectPath("write-#{i}") | |
86 | editor.setText('abc\ndef') | |
87 | editor.saveAs(filePath) | |
88 | ||
89 | it "saves the file", -> | |
90 | editor.setText('abc') | |
91 | keydown(':') | |
92 | submitNormalModeInputText('write') | |
93 | expect(fs.readFileSync(filePath, 'utf-8')).toEqual('abc') | |
94 | expect(editor.isModified()).toBe(false) | |
95 | ||
96 | describe "with a specified path", -> | |
97 | newPath = '' | |
98 | ||
99 | beforeEach -> | |
100 | newPath = path.relative(dir, "#{filePath}.new") | |
101 | editor.getBuffer().setText('abc') | |
102 | keydown(':') | |
103 | ||
104 | afterEach -> | |
105 | submitNormalModeInputText("write #{newPath}") | |
106 | newPath = path.resolve(dir, fs.normalize(newPath)) | |
107 | expect(fs.existsSync(newPath)).toBe(true) | |
108 | expect(fs.readFileSync(newPath, 'utf-8')).toEqual('abc') | |
109 | expect(editor.isModified()).toBe(true) | |
110 | fs.removeSync(newPath) | |
111 | ||
112 | it "saves to the path", -> | |
113 | ||
114 | it "expands .", -> | |
115 | newPath = path.join('.', newPath) | |
116 | ||
117 | it "expands ..", -> | |
118 | newPath = path.join('..', newPath) | |
119 | ||
120 | it "expands ~", -> | |
121 | newPath = path.join('~', newPath) | |
122 | ||
123 | it "throws an error with more than one path", -> | |
124 | keydown(':') | |
125 | submitNormalModeInputText('write path1 path2') | |
126 | expect(atom.notifications.notifications[0].message).toEqual( | |
127 | 'Command error: Only one file name allowed' | |
128 | ) | |
129 | ||
130 | describe "when the file already exists", -> | |
131 | existsPath = '' | |
132 | ||
133 | beforeEach -> | |
134 | existsPath = projectPath('write-exists') | |
135 | fs.writeFileSync(existsPath, 'abc') | |
136 | ||
137 | afterEach -> | |
138 | fs.removeSync(existsPath) | |
139 | ||
140 | it "throws an error if the file already exists", -> | |
141 | keydown(':') | |
142 | submitNormalModeInputText("write #{existsPath}") | |
143 | expect(atom.notifications.notifications[0].message).toEqual( | |
144 | 'Command error: File exists (add ! to override)' | |
145 | ) | |
146 | expect(fs.readFileSync(existsPath, 'utf-8')).toEqual('abc') | |
147 | ||
148 | it "writes if forced with :write!", -> | |
149 | keydown(':') | |
150 | submitNormalModeInputText("write! #{existsPath}") | |
151 | expect(atom.notifications.notifications).toEqual([]) | |
152 | expect(fs.readFileSync(existsPath, 'utf-8')).toEqual('abc\ndef') | |
153 | ||
154 | describe ":quit", -> | |
155 | pane = null | |
156 | beforeEach -> | |
157 | waitsForPromise -> | |
158 | pane = atom.workspace.getActivePane() | |
159 | spyOn(pane, 'destroyActiveItem').andCallThrough() | |
160 | atom.workspace.open() | |
161 | ||
162 | it "closes the active pane item if not modified", -> | |
163 | keydown(':') | |
164 | submitNormalModeInputText('quit') | |
165 | expect(pane.destroyActiveItem).toHaveBeenCalled() | |
166 | expect(pane.getItems().length).toBe(1) | |
167 | ||
168 | describe "when the active pane item is modified", -> | |
169 | beforeEach -> | |
170 | editor.getBuffer().setText('def') | |
171 | ||
172 | it "opens the prompt to save", -> | |
173 | spyOn(pane, 'promptToSaveItem') | |
174 | keydown(':') | |
175 | submitNormalModeInputText('quit') | |
176 | expect(pane.promptToSaveItem).toHaveBeenCalled() | |
177 | ||
178 | describe ":tabclose", -> | |
179 | it "acts as an alias to :quit", -> | |
180 | spyOn(Ex, 'tabclose').andCallThrough() | |
181 | spyOn(Ex, 'quit').andCallThrough() | |
182 | keydown(':') | |
183 | submitNormalModeInputText('tabclose') | |
184 | expect(Ex.quit).toHaveBeenCalledWith(Ex.tabclose.calls[0].args...) | |
185 | ||
186 | describe ":tabnext", -> | |
187 | pane = null | |
188 | beforeEach -> | |
189 | waitsForPromise -> | |
190 | pane = atom.workspace.getActivePane() | |
191 | atom.workspace.open().then -> atom.workspace.open() | |
192 | .then -> atom.workspace.open() | |
193 | ||
194 | it "switches to the next tab", -> | |
195 | pane.activateItemAtIndex(1) | |
196 | keydown(':') | |
197 | submitNormalModeInputText('tabnext') | |
198 | expect(pane.getActiveItemIndex()).toBe(2) | |
199 | ||
200 | it "wraps around", -> | |
201 | pane.activateItemAtIndex(pane.getItems().length - 1) | |
202 | keydown(':') | |
203 | submitNormalModeInputText('tabnext') | |
204 | expect(pane.getActiveItemIndex()).toBe(0) | |
205 | ||
206 | describe ":tabprevious", -> | |
207 | pane = null | |
208 | beforeEach -> | |
209 | waitsForPromise -> | |
210 | pane = atom.workspace.getActivePane() | |
211 | atom.workspace.open().then -> atom.workspace.open() | |
212 | .then -> atom.workspace.open() | |
213 | ||
214 | it "switches to the previous tab", -> | |
215 | pane.activateItemAtIndex(1) | |
216 | keydown(':') | |
217 | submitNormalModeInputText('tabprevious') | |
218 | expect(pane.getActiveItemIndex()).toBe(0) | |
219 | ||
220 | it "wraps around", -> | |
221 | pane.activateItemAtIndex(0) | |
222 | keydown(':') | |
223 | submitNormalModeInputText('tabprevious') | |
224 | expect(pane.getActiveItemIndex()).toBe(pane.getItems().length - 1) | |
225 | ||
226 | describe ":wq", -> | |
227 | beforeEach -> | |
228 | spyOn(Ex, 'write').andCallThrough() | |
229 | spyOn(Ex, 'quit') | |
230 | ||
231 | it "writes the file, then quits", -> | |
232 | spyOn(atom, 'showSaveDialogSync').andReturn(projectPath('wq-1')) | |
233 | keydown(':') | |
234 | submitNormalModeInputText('wq') | |
235 | expect(Ex.write).toHaveBeenCalled() | |
236 | # Since `:wq` only calls `:quit` after `:write` is finished, we need to | |
237 | # wait a bit for the `:quit` call to occur | |
238 | waitsFor((-> Ex.quit.wasCalled), "the :quit command to be called", 100) | |
239 | ||
240 | it "doesn't quit when the file is new and no path is specified in the save dialog", -> | |
241 | spyOn(atom, 'showSaveDialogSync').andReturn(undefined) | |
242 | keydown(':') | |
243 | submitNormalModeInputText('wq') | |
244 | expect(Ex.write).toHaveBeenCalled() | |
245 | wasNotCalled = false | |
246 | # FIXME: This seems dangerous, but setTimeout somehow doesn't work. | |
247 | setImmediate((-> | |
248 | wasNotCalled = not Ex.quit.wasCalled)) | |
249 | waitsFor((-> wasNotCalled), 100) | |
250 | ||
251 | it "passes the file name", -> | |
252 | keydown(':') | |
253 | submitNormalModeInputText('wq wq-2') | |
254 | expect(Ex.write) | |
255 | .toHaveBeenCalled() | |
256 | expect(Ex.write.calls[0].args[1].trim()).toEqual('wq-2') | |
257 | waitsFor((-> Ex.quit.wasCalled), "the :quit command to be called", 100) | |
258 | ||
259 | describe ":xit", -> | |
260 | it "acts as an alias to :wq", -> | |
261 | spyOn(Ex, 'wq') | |
262 | keydown(':') | |
263 | submitNormalModeInputText('xit') | |
264 | expect(Ex.wq).toHaveBeenCalled() | |
265 | ||
266 | describe ":edit", -> | |
267 | describe "without a file name", -> | |
268 | it "reloads the file from the disk", -> | |
269 | filePath = projectPath("edit-1") | |
270 | editor.getBuffer().setText('abc') | |
271 | editor.saveAs(filePath) | |
272 | fs.writeFileSync(filePath, 'def') | |
273 | keydown(':') | |
274 | submitNormalModeInputText('edit') | |
275 | # Reloading takes a bit | |
276 | waitsFor((-> editor.getText() is 'def'), | |
277 | "the editor's content to change", 100) | |
278 | ||
279 | it "doesn't reload when the file has been modified", -> | |
280 | filePath = projectPath("edit-2") | |
281 | editor.getBuffer().setText('abc') | |
282 | editor.saveAs(filePath) | |
283 | editor.getBuffer().setText('abcd') | |
284 | fs.writeFileSync(filePath, 'def') | |
285 | keydown(':') | |
286 | submitNormalModeInputText('edit') | |
287 | expect(atom.notifications.notifications[0].message).toEqual( | |
288 | 'Command error: No write since last change (add ! to override)') | |
289 | isntDef = false | |
290 | setImmediate(-> isntDef = editor.getText() isnt 'def') | |
291 | waitsFor((-> isntDef), "the editor's content not to change", 50) | |
292 | ||
293 | it "reloads when the file has been modified and it is forced", -> | |
294 | filePath = projectPath("edit-3") | |
295 | editor.getBuffer().setText('abc') | |
296 | editor.saveAs(filePath) | |
297 | editor.getBuffer().setText('abcd') | |
298 | fs.writeFileSync(filePath, 'def') | |
299 | keydown(':') | |
300 | submitNormalModeInputText('edit!') | |
301 | expect(atom.notifications.notifications.length).toBe(0) | |
302 | waitsFor((-> editor.getText() is 'def') | |
303 | "the editor's content to change", 50) | |
304 | ||
305 | it "throws an error when editing a new file", -> | |
306 | editor.getBuffer().reload() | |
307 | keydown(':') | |
308 | submitNormalModeInputText('edit') | |
309 | expect(atom.notifications.notifications[0].message).toEqual( | |
310 | 'Command error: No file name') | |
311 | atom.commands.dispatch(editorElement, 'ex-mode:open') | |
312 | submitNormalModeInputText('edit!') | |
313 | expect(atom.notifications.notifications[1].message).toEqual( | |
314 | 'Command error: No file name') | |
315 | ||
316 | describe "with a file name", -> | |
317 | beforeEach -> | |
318 | spyOn(atom.workspace, 'open') | |
319 | editor.getBuffer().reload() | |
320 | ||
321 | it "opens the specified path", -> | |
322 | filePath = projectPath('edit-new-test') | |
323 | keydown(':') | |
324 | submitNormalModeInputText("edit #{filePath}") | |
325 | expect(atom.workspace.open).toHaveBeenCalledWith(filePath) | |
326 | ||
327 | it "opens a relative path", -> | |
328 | keydown(':') | |
329 | submitNormalModeInputText('edit edit-relative-test') | |
330 | expect(atom.workspace.open).toHaveBeenCalledWith( | |
331 | projectPath('edit-relative-test')) | |
332 | ||
333 | it "throws an error if trying to open more than one file", -> | |
334 | keydown(':') | |
335 | submitNormalModeInputText('edit edit-new-test-1 edit-new-test-2') | |
336 | expect(atom.workspace.open.callCount).toBe(0) | |
337 | expect(atom.notifications.notifications[0].message).toEqual( | |
338 | 'Command error: Only one file name allowed') | |
339 | ||
340 | describe ":tabedit", -> | |
341 | it "acts as an alias to :edit if supplied with a path", -> | |
342 | spyOn(Ex, 'tabedit').andCallThrough() | |
343 | spyOn(Ex, 'edit') | |
344 | keydown(':') | |
345 | submitNormalModeInputText('tabedit tabedit-test') | |
346 | expect(Ex.edit).toHaveBeenCalledWith(Ex.tabedit.calls[0].args...) | |
347 | ||
348 | it "acts as an alias to :tabnew if not supplied with a path", -> | |
349 | spyOn(Ex, 'tabedit').andCallThrough() | |
350 | spyOn(Ex, 'tabnew') | |
351 | keydown(':') | |
352 | submitNormalModeInputText('tabedit ') | |
353 | expect(Ex.tabnew) | |
354 | .toHaveBeenCalledWith(Ex.tabedit.calls[0].args...) | |
355 | ||
356 | describe ":tabnew", -> | |
357 | it "opens a new tab", -> | |
358 | spyOn(atom.workspace, 'open') | |
359 | keydown(':') | |
360 | submitNormalModeInputText('tabnew') | |
361 | expect(atom.workspace.open).toHaveBeenCalled() | |
362 | ||
363 | describe ":split", -> | |
364 | it "splits the current file upwards", -> | |
365 | pane = atom.workspace.getActivePane() | |
366 | spyOn(pane, 'splitUp').andCallThrough() | |
367 | filePath = projectPath('split') | |
368 | editor.saveAs(filePath) | |
369 | keydown(':') | |
370 | submitNormalModeInputText('split') | |
371 | expect(pane.splitUp).toHaveBeenCalled() | |
372 | # FIXME: Should test whether the new pane contains a TextEditor | |
373 | # pointing to the same path | |
374 | ||
375 | describe ":vsplit", -> | |
376 | it "splits the current file to the left", -> | |
377 | pane = atom.workspace.getActivePane() | |
378 | spyOn(pane, 'splitLeft').andCallThrough() | |
379 | filePath = projectPath('vsplit') | |
380 | editor.saveAs(filePath) | |
381 | keydown(':') | |
382 | submitNormalModeInputText('vsplit') | |
383 | expect(pane.splitLeft).toHaveBeenCalled() | |
384 | # FIXME: Should test whether the new pane contains a TextEditor | |
385 | # pointing to the same path | |
386 | ||
387 | describe ":delete", -> | |
388 | beforeEach -> | |
389 | editor.setText('abc\ndef\nghi\njkl') | |
390 | editor.setCursorBufferPosition([2, 0]) | |
391 | ||
392 | it "deletes the current line", -> | |
393 | keydown(':') | |
394 | submitNormalModeInputText('delete') | |
395 | expect(editor.getText()).toEqual('abc\ndef\njkl') | |
396 | ||
397 | it "deletes the lines in the given range", -> | |
398 | processedOpStack = false | |
399 | exState.onDidProcessOpStack -> processedOpStack = true | |
400 | keydown(':') | |
401 | submitNormalModeInputText('1,2delete') | |
402 | expect(editor.getText()).toEqual('ghi\njkl') | |
403 | ||
404 | waitsFor -> processedOpStack | |
405 | editor.setText('abc\ndef\nghi\njkl') | |
406 | editor.setCursorBufferPosition([1, 1]) | |
407 | # For some reason, keydown(':') doesn't work here :/ | |
408 | atom.commands.dispatch(editorElement, 'ex-mode:open') | |
409 | submitNormalModeInputText(',/k/delete') | |
410 | expect(editor.getText()).toEqual('abc\n') | |
411 | ||
412 | it "undos deleting several lines at once", -> | |
413 | keydown(':') | |
414 | submitNormalModeInputText('-1,.delete') | |
415 | expect(editor.getText()).toEqual('abc\njkl') | |
416 | atom.commands.dispatch(editorElement, 'core:undo') | |
417 | expect(editor.getText()).toEqual('abc\ndef\nghi\njkl') | |
418 | ||
419 | describe ":substitute", -> | |
420 | beforeEach -> | |
421 | editor.setText('abcaABC\ndefdDEF\nabcaABC') | |
422 | editor.setCursorBufferPosition([0, 0]) | |
423 | ||
424 | it "replaces a character on the current line", -> | |
425 | keydown(':') | |
426 | submitNormalModeInputText(':substitute /a/x') | |
427 | expect(editor.getText()).toEqual('xbcaABC\ndefdDEF\nabcaABC') | |
428 | ||
429 | it "doesn't need a space before the arguments", -> | |
430 | keydown(':') | |
431 | submitNormalModeInputText(':substitute/a/x') | |
432 | expect(editor.getText()).toEqual('xbcaABC\ndefdDEF\nabcaABC') | |
433 | ||
434 | it "respects modifiers passed to it", -> | |
435 | keydown(':') | |
436 | submitNormalModeInputText(':substitute/a/x/g') | |
437 | expect(editor.getText()).toEqual('xbcxABC\ndefdDEF\nabcaABC') | |
438 | ||
439 | atom.commands.dispatch(editorElement, 'ex-mode:open') | |
440 | submitNormalModeInputText(':substitute/a/x/gi') | |
441 | expect(editor.getText()).toEqual('xbcxxBC\ndefdDEF\nabcaABC') | |
442 | ||
443 | it "replaces on multiple lines", -> | |
444 | keydown(':') | |
445 | submitNormalModeInputText(':%substitute/abc/ghi') | |
446 | expect(editor.getText()).toEqual('ghiaABC\ndefdDEF\nghiaABC') | |
447 | ||
448 | atom.commands.dispatch(editorElement, 'ex-mode:open') | |
449 | submitNormalModeInputText(':%substitute/abc/ghi/ig') | |
450 | expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nghiaghi') | |
451 | ||
452 | it "can't be delimited by letters", -> | |
453 | keydown(':') | |
454 | submitNormalModeInputText(':substitute nanxngi') | |
455 | expect(atom.notifications.notifications[0].message).toEqual( | |
456 | "Command error: Regular expressions can't be delimited by letters") | |
457 | expect(editor.getText()).toEqual('abcaABC\ndefdDEF\nabcaABC') | |
458 | ||
459 | describe "capturing groups", -> | |
460 | beforeEach -> | |
461 | editor.setText('abcaABC\ndefdDEF\nabcaABC') | |
462 | ||
463 | it "replaces \\1 with the first group", -> | |
464 | keydown(':') | |
465 | submitNormalModeInputText(':substitute/bc(.{2})/X\\1X') | |
466 | expect(editor.getText()).toEqual('aXaAXBC\ndefdDEF\nabcaABC') | |
467 | ||
468 | it "replaces multiple groups", -> | |
469 | keydown(':') | |
470 | submitNormalModeInputText(':substitute/a([a-z]*)aA([A-Z]*)/X\\1XY\\2Y') | |
471 | expect(editor.getText()).toEqual('XbcXYBCY\ndefdDEF\nabcaABC') | |
472 | ||
473 | it "replaces \\0 with the entire match", -> | |
474 | keydown(':') | |
475 | submitNormalModeInputText(':substitute/ab(ca)AB/X\\0X') | |
476 | expect(editor.getText()).toEqual('XabcaABXC\ndefdDEF\nabcaABC') | |
477 | ||
478 | describe ":set", -> | |
479 | it "throws an error without a specified option", -> | |
480 | keydown(':') | |
481 | submitNormalModeInputText(':set') | |
482 | expect(atom.notifications.notifications[0].message).toEqual( | |
483 | 'Command error: No option specified') | |
484 | ||
485 | it "sets multiple options at once", -> | |
486 | atom.config.set('editor.showInvisibles', false) | |
487 | atom.config.set('editor.showLineNumbers', false) | |
488 | keydown(':') | |
489 | submitNormalModeInputText(':set list number') | |
490 | expect(atom.config.get('editor.showInvisibles')).toBe(true) | |
491 | expect(atom.config.get('editor.showLineNumbers')).toBe(true) | |
492 | ||
493 | describe "the options", -> | |
494 | beforeEach -> | |
495 | atom.config.set('editor.showInvisibles', false) | |
496 | atom.config.set('editor.showLineNumbers', false) | |
497 | ||
498 | it "sets (no)list", -> | |
499 | keydown(':') | |
500 | submitNormalModeInputText(':set list') | |
501 | expect(atom.config.get('editor.showInvisibles')).toBe(true) | |
502 | atom.commands.dispatch(editorElement, 'ex-mode:open') | |
503 | submitNormalModeInputText(':set nolist') | |
504 | expect(atom.config.get('editor.showInvisibles')).toBe(false) | |
505 | ||
506 | it "sets (no)nu(mber)", -> | |
507 | keydown(':') | |
508 | submitNormalModeInputText(':set nu') | |
509 | expect(atom.config.get('editor.showLineNumbers')).toBe(true) | |
510 | atom.commands.dispatch(editorElement, 'ex-mode:open') | |
511 | submitNormalModeInputText(':set nonu') | |
512 | expect(atom.config.get('editor.showLineNumbers')).toBe(false) | |
513 | atom.commands.dispatch(editorElement, 'ex-mode:open') | |
514 | submitNormalModeInputText(':set number') | |
515 | expect(atom.config.get('editor.showLineNumbers')).toBe(true) | |
516 | atom.commands.dispatch(editorElement, 'ex-mode:open') | |
517 | submitNormalModeInputText(':set nonumber') | |
518 | expect(atom.config.get('editor.showLineNumbers')).toBe(false) |