From 5f56b62b7ba50a9c5a0f6512cf6097893c73f94a Mon Sep 17 00:00:00 2001 From: jazzpi Date: Mon, 15 Aug 2016 21:21:52 +0200 Subject: [PATCH] Fix specs Apparently `keydown(':')` was never actually opening the Ex line (and `keydown('escape')` wasn't closing it so it wasn't noticeable), so now we open it directly with `ex-mode:open`. --- spec/ex-commands-spec.coffee | 163 ++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 80 deletions(-) diff --git a/spec/ex-commands-spec.coffee b/spec/ex-commands-spec.coffee index 1dd24a6..f4c30cf 100644 --- a/spec/ex-commands-spec.coffee +++ b/spec/ex-commands-spec.coffee @@ -62,6 +62,9 @@ describe "the commands", -> commandEditor.getModel().setText(text) atom.commands.dispatch(commandEditor, "core:confirm") + openEx = -> + atom.commands.dispatch(editorElement, "ex-mode:open") + describe ":write", -> describe "when editing a new file", -> beforeEach -> @@ -69,14 +72,14 @@ describe "the commands", -> it "opens the save dialog", -> spyOn(atom, 'showSaveDialogSync') - keydown(':') + openEx() submitNormalModeInputText('write') expect(atom.showSaveDialogSync).toHaveBeenCalled() it "saves when a path is specified in the save dialog", -> filePath = projectPath('write-from-save-dialog') spyOn(atom, 'showSaveDialogSync').andReturn(filePath) - keydown(':') + openEx() submitNormalModeInputText('write') expect(fs.existsSync(filePath)).toBe(true) expect(fs.readFileSync(filePath, 'utf-8')).toEqual('abc\ndef') @@ -85,7 +88,7 @@ describe "the commands", -> it "saves when a path is specified in the save dialog", -> spyOn(atom, 'showSaveDialogSync').andReturn(undefined) spyOn(fs, 'writeFileSync') - keydown(':') + openEx() submitNormalModeInputText('write') expect(fs.writeFileSync.calls.length).toBe(0) @@ -101,7 +104,7 @@ describe "the commands", -> it "saves the file", -> editor.setText('abc') - keydown(':') + openEx() submitNormalModeInputText('write') expect(fs.readFileSync(filePath, 'utf-8')).toEqual('abc') expect(editor.isModified()).toBe(false) @@ -112,7 +115,7 @@ describe "the commands", -> beforeEach -> newPath = path.relative(dir, "#{filePath}.new") editor.getBuffer().setText('abc') - keydown(':') + openEx() afterEach -> submitNormalModeInputText("write #{newPath}") @@ -134,7 +137,7 @@ describe "the commands", -> newPath = path.join('~', newPath) it "throws an error with more than one path", -> - keydown(':') + openEx() submitNormalModeInputText('write path1 path2') expect(atom.notifications.notifications[0].message).toEqual( 'Command error: Only one file name allowed' @@ -151,7 +154,7 @@ describe "the commands", -> fs.removeSync(existsPath) it "throws an error if the file already exists", -> - keydown(':') + openEx() submitNormalModeInputText("write #{existsPath}") expect(atom.notifications.notifications[0].message).toEqual( 'Command error: File exists (add ! to override)' @@ -159,7 +162,7 @@ describe "the commands", -> expect(fs.readFileSync(existsPath, 'utf-8')).toEqual('abc') it "writes if forced with :write!", -> - keydown(':') + openEx() submitNormalModeInputText("write! #{existsPath}") expect(atom.notifications.notifications).toEqual([]) expect(fs.readFileSync(existsPath, 'utf-8')).toEqual('abc\ndef') @@ -167,7 +170,7 @@ describe "the commands", -> describe ":wall", -> it "saves all", -> spyOn(atom.workspace, 'saveAll') - keydown(':') + openEx() submitNormalModeInputText('wall') expect(atom.workspace.saveAll).toHaveBeenCalled() @@ -178,14 +181,14 @@ describe "the commands", -> it "opens the save dialog", -> spyOn(atom, 'showSaveDialogSync') - keydown(':') + openEx() submitNormalModeInputText('saveas') expect(atom.showSaveDialogSync).toHaveBeenCalled() it "saves when a path is specified in the save dialog", -> filePath = projectPath('saveas-from-save-dialog') spyOn(atom, 'showSaveDialogSync').andReturn(filePath) - keydown(':') + openEx() submitNormalModeInputText('saveas') expect(fs.existsSync(filePath)).toBe(true) expect(fs.readFileSync(filePath, 'utf-8')).toEqual('abc\ndef') @@ -193,7 +196,7 @@ describe "the commands", -> it "saves when a path is specified in the save dialog", -> spyOn(atom, 'showSaveDialogSync').andReturn(undefined) spyOn(fs, 'writeFileSync') - keydown(':') + openEx() submitNormalModeInputText('saveas') expect(fs.writeFileSync.calls.length).toBe(0) @@ -209,7 +212,7 @@ describe "the commands", -> it "complains if no path given", -> editor.setText('abc') - keydown(':') + openEx() submitNormalModeInputText('saveas') expect(atom.notifications.notifications[0].message).toEqual( 'Command error: Argument required' @@ -221,7 +224,7 @@ describe "the commands", -> beforeEach -> newPath = path.relative(dir, "#{filePath}.new") editor.getBuffer().setText('abc') - keydown(':') + openEx() afterEach -> submitNormalModeInputText("saveas #{newPath}") @@ -243,7 +246,7 @@ describe "the commands", -> newPath = path.join('~', newPath) it "throws an error with more than one path", -> - keydown(':') + openEx() submitNormalModeInputText('saveas path1 path2') expect(atom.notifications.notifications[0].message).toEqual( 'Command error: Only one file name allowed' @@ -260,7 +263,7 @@ describe "the commands", -> fs.removeSync(existsPath) it "throws an error if the file already exists", -> - keydown(':') + openEx() submitNormalModeInputText("saveas #{existsPath}") expect(atom.notifications.notifications[0].message).toEqual( 'Command error: File exists (add ! to override)' @@ -268,7 +271,7 @@ describe "the commands", -> expect(fs.readFileSync(existsPath, 'utf-8')).toEqual('abc') it "writes if forced with :saveas!", -> - keydown(':') + openEx() submitNormalModeInputText("saveas! #{existsPath}") expect(atom.notifications.notifications).toEqual([]) expect(fs.readFileSync(existsPath, 'utf-8')).toEqual('abc\ndef') @@ -282,7 +285,7 @@ describe "the commands", -> atom.workspace.open() it "closes the active pane item if not modified", -> - keydown(':') + openEx() submitNormalModeInputText('quit') expect(pane.destroyActiveItem).toHaveBeenCalled() expect(pane.getItems().length).toBe(1) @@ -293,14 +296,14 @@ describe "the commands", -> it "opens the prompt to save", -> spyOn(pane, 'promptToSaveItem') - keydown(':') + openEx() submitNormalModeInputText('quit') expect(pane.promptToSaveItem).toHaveBeenCalled() describe ":quitall", -> it "closes Atom", -> spyOn(atom, 'close') - keydown(':') + openEx() submitNormalModeInputText('quitall') expect(atom.close).toHaveBeenCalled() @@ -308,7 +311,7 @@ describe "the commands", -> it "acts as an alias to :quit", -> spyOn(Ex, 'tabclose').andCallThrough() spyOn(Ex, 'quit').andCallThrough() - keydown(':') + openEx() submitNormalModeInputText('tabclose') expect(Ex.quit).toHaveBeenCalledWith(Ex.tabclose.calls[0].args...) @@ -322,13 +325,13 @@ describe "the commands", -> it "switches to the next tab", -> pane.activateItemAtIndex(1) - keydown(':') + openEx() submitNormalModeInputText('tabnext') expect(pane.getActiveItemIndex()).toBe(2) it "wraps around", -> pane.activateItemAtIndex(pane.getItems().length - 1) - keydown(':') + openEx() submitNormalModeInputText('tabnext') expect(pane.getActiveItemIndex()).toBe(0) @@ -342,13 +345,13 @@ describe "the commands", -> it "switches to the previous tab", -> pane.activateItemAtIndex(1) - keydown(':') + openEx() submitNormalModeInputText('tabprevious') expect(pane.getActiveItemIndex()).toBe(0) it "wraps around", -> pane.activateItemAtIndex(0) - keydown(':') + openEx() submitNormalModeInputText('tabprevious') expect(pane.getActiveItemIndex()).toBe(pane.getItems().length - 1) @@ -359,7 +362,7 @@ describe "the commands", -> it "writes the file, then quits", -> spyOn(atom, 'showSaveDialogSync').andReturn(projectPath('wq-1')) - keydown(':') + openEx() submitNormalModeInputText('wq') expect(Ex.write).toHaveBeenCalled() # Since `:wq` only calls `:quit` after `:write` is finished, we need to @@ -368,7 +371,7 @@ describe "the commands", -> it "doesn't quit when the file is new and no path is specified in the save dialog", -> spyOn(atom, 'showSaveDialogSync').andReturn(undefined) - keydown(':') + openEx() submitNormalModeInputText('wq') expect(Ex.write).toHaveBeenCalled() wasNotCalled = false @@ -378,7 +381,7 @@ describe "the commands", -> waitsFor((-> wasNotCalled), 100) it "passes the file name", -> - keydown(':') + openEx() submitNormalModeInputText('wq wq-2') expect(Ex.write) .toHaveBeenCalled() @@ -388,7 +391,7 @@ describe "the commands", -> describe ":xit", -> it "acts as an alias to :wq", -> spyOn(Ex, 'wq') - keydown(':') + openEx() submitNormalModeInputText('xit') expect(Ex.wq).toHaveBeenCalled() @@ -396,7 +399,7 @@ describe "the commands", -> it "calls :wall, then :quitall", -> spyOn(Ex, 'wall') spyOn(Ex, 'quitall') - keydown(':') + openEx() submitNormalModeInputText('wqall') expect(Ex.wall).toHaveBeenCalled() expect(Ex.quitall).toHaveBeenCalled() @@ -408,7 +411,7 @@ describe "the commands", -> editor.getBuffer().setText('abc') editor.saveAs(filePath) fs.writeFileSync(filePath, 'def') - keydown(':') + openEx() submitNormalModeInputText('edit') # Reloading takes a bit waitsFor((-> editor.getText() is 'def'), @@ -420,7 +423,7 @@ describe "the commands", -> editor.saveAs(filePath) editor.getBuffer().setText('abcd') fs.writeFileSync(filePath, 'def') - keydown(':') + openEx() submitNormalModeInputText('edit') expect(atom.notifications.notifications[0].message).toEqual( 'Command error: No write since last change (add ! to override)') @@ -434,7 +437,7 @@ describe "the commands", -> editor.saveAs(filePath) editor.getBuffer().setText('abcd') fs.writeFileSync(filePath, 'def') - keydown(':') + openEx() submitNormalModeInputText('edit!') expect(atom.notifications.notifications.length).toBe(0) waitsFor((-> editor.getText() is 'def') @@ -442,7 +445,7 @@ describe "the commands", -> it "throws an error when editing a new file", -> editor.getBuffer().reload() - keydown(':') + openEx() submitNormalModeInputText('edit') expect(atom.notifications.notifications[0].message).toEqual( 'Command error: No file name') @@ -458,18 +461,18 @@ describe "the commands", -> it "opens the specified path", -> filePath = projectPath('edit-new-test') - keydown(':') + openEx() submitNormalModeInputText("edit #{filePath}") expect(atom.workspace.open).toHaveBeenCalledWith(filePath) it "opens a relative path", -> - keydown(':') + openEx() submitNormalModeInputText('edit edit-relative-test') expect(atom.workspace.open).toHaveBeenCalledWith( projectPath('edit-relative-test')) it "throws an error if trying to open more than one file", -> - keydown(':') + openEx() submitNormalModeInputText('edit edit-new-test-1 edit-new-test-2') expect(atom.workspace.open.callCount).toBe(0) expect(atom.notifications.notifications[0].message).toEqual( @@ -479,14 +482,14 @@ describe "the commands", -> it "acts as an alias to :edit if supplied with a path", -> spyOn(Ex, 'tabedit').andCallThrough() spyOn(Ex, 'edit') - keydown(':') + openEx() submitNormalModeInputText('tabedit tabedit-test') expect(Ex.edit).toHaveBeenCalledWith(Ex.tabedit.calls[0].args...) it "acts as an alias to :tabnew if not supplied with a path", -> spyOn(Ex, 'tabedit').andCallThrough() spyOn(Ex, 'tabnew') - keydown(':') + openEx() submitNormalModeInputText('tabedit ') expect(Ex.tabnew) .toHaveBeenCalledWith(Ex.tabedit.calls[0].args...) @@ -494,14 +497,14 @@ describe "the commands", -> describe ":tabnew", -> it "opens a new tab", -> spyOn(atom.workspace, 'open') - keydown(':') + openEx() submitNormalModeInputText('tabnew') expect(atom.workspace.open).toHaveBeenCalled() it "opens a new tab for editing when provided an argument", -> spyOn(Ex, 'tabnew').andCallThrough() spyOn(Ex, 'tabedit') - keydown(':') + openEx() submitNormalModeInputText('tabnew tabnew-test') expect(Ex.tabedit) .toHaveBeenCalledWith(Ex.tabnew.calls[0].args...) @@ -513,14 +516,14 @@ describe "the commands", -> spyOn(pane, 'splitDown').andCallThrough() filePath = projectPath('split') editor.saveAs(filePath) - keydown(':') + openEx() submitNormalModeInputText('split') expect(pane.splitDown).toHaveBeenCalled() else spyOn(pane, 'splitUp').andCallThrough() filePath = projectPath('split') editor.saveAs(filePath) - keydown(':') + openEx() submitNormalModeInputText('split') expect(pane.splitUp).toHaveBeenCalled() # FIXME: Should test whether the new pane contains a TextEditor @@ -533,7 +536,7 @@ describe "the commands", -> spyOn(pane, 'splitRight').andCallThrough() filePath = projectPath('vsplit') editor.saveAs(filePath) - keydown(':') + openEx() submitNormalModeInputText('vsplit') expect(pane.splitLeft).toHaveBeenCalled() else @@ -541,7 +544,7 @@ describe "the commands", -> spyOn(pane, 'splitLeft').andCallThrough() filePath = projectPath('vsplit') editor.saveAs(filePath) - keydown(':') + openEx() submitNormalModeInputText('vsplit') expect(pane.splitLeft).toHaveBeenCalled() # FIXME: Should test whether the new pane contains a TextEditor @@ -553,19 +556,19 @@ describe "the commands", -> editor.setCursorBufferPosition([2, 0]) it "deletes the current line", -> - keydown(':') + openEx() submitNormalModeInputText('delete') expect(editor.getText()).toEqual('abc\ndef\njkl') it "copies the deleted text", -> - keydown(':') + openEx() submitNormalModeInputText('delete') expect(atom.clipboard.read()).toEqual('ghi\n') it "deletes the lines in the given range", -> processedOpStack = false exState.onDidProcessOpStack -> processedOpStack = true - keydown(':') + openEx() submitNormalModeInputText('1,2delete') expect(editor.getText()).toEqual('ghi\njkl') @@ -578,7 +581,7 @@ describe "the commands", -> expect(editor.getText()).toEqual('abc\n') it "undos deleting several lines at once", -> - keydown(':') + openEx() submitNormalModeInputText('-1,.delete') expect(editor.getText()).toEqual('abc\njkl') atom.commands.dispatch(editorElement, 'core:undo') @@ -590,17 +593,17 @@ describe "the commands", -> editor.setCursorBufferPosition([0, 0]) it "replaces a character on the current line", -> - keydown(':') + openEx() submitNormalModeInputText(':substitute /a/x') expect(editor.getText()).toEqual('xbcaABC\ndefdDEF\nabcaABC') it "doesn't need a space before the arguments", -> - keydown(':') + openEx() submitNormalModeInputText(':substitute/a/x') expect(editor.getText()).toEqual('xbcaABC\ndefdDEF\nabcaABC') it "respects modifiers passed to it", -> - keydown(':') + openEx() submitNormalModeInputText(':substitute/a/x/g') expect(editor.getText()).toEqual('xbcxABC\ndefdDEF\nabcaABC') @@ -609,7 +612,7 @@ describe "the commands", -> expect(editor.getText()).toEqual('xbcxxBC\ndefdDEF\nabcaABC') it "replaces on multiple lines", -> - keydown(':') + openEx() submitNormalModeInputText(':%substitute/abc/ghi') expect(editor.getText()).toEqual('ghiaABC\ndefdDEF\nghiaABC') @@ -623,18 +626,18 @@ describe "the commands", -> editor.setCursorBufferPosition([2, 0]) it "yanks the current line", -> - keydown(':') + openEx() submitNormalModeInputText('yank') expect(atom.clipboard.read()).toEqual('ghi\n') it "yanks the lines in the given range", -> - keydown(':') + openEx() submitNormalModeInputText('1,2yank') expect(atom.clipboard.read()).toEqual('abc\ndef\n') describe "illegal delimiters", -> test = (delim) -> - keydown(':') + openEx() submitNormalModeInputText(":substitute #{delim}a#{delim}x#{delim}gi") expect(atom.notifications.notifications[0].message).toEqual( "Command error: Regular expressions can't be delimited by alphanumeric characters, '\\', '\"' or '|'") @@ -651,12 +654,12 @@ describe "the commands", -> editor.setText('abcabc\nabcabc') it "removes the pattern without modifiers", -> - keydown(':') + openEx() submitNormalModeInputText(":substitute/abc//") expect(editor.getText()).toEqual('abc\nabcabc') it "removes the pattern with modifiers", -> - keydown(':') + openEx() submitNormalModeInputText(":substitute/abc//g") expect(editor.getText()).toEqual('\nabcabc') @@ -665,7 +668,7 @@ describe "the commands", -> editor.setText('abc,def,ghi') test = (escapeChar, escaped) -> - keydown(':') + openEx() submitNormalModeInputText(":substitute/,/\\#{escapeChar}/g") expect(editor.getText()).toEqual("abc#{escaped}def#{escaped}ghi") @@ -680,26 +683,26 @@ describe "the commands", -> it "uses case sensitive search if smartcase is off and the pattern is lowercase", -> atom.config.set('vim-mode.useSmartcaseForSearch', false) - keydown(':') + openEx() submitNormalModeInputText(':substitute/abc/ghi/g') expect(editor.getText()).toEqual('ghiaABC\ndefdDEF\nabcaABC') it "uses case sensitive search if smartcase is off and the pattern is uppercase", -> editor.setText('abcaABC\ndefdDEF\nabcaABC') - keydown(':') + openEx() submitNormalModeInputText(':substitute/ABC/ghi/g') expect(editor.getText()).toEqual('abcaghi\ndefdDEF\nabcaABC') it "uses case insensitive search if smartcase is on and the pattern is lowercase", -> editor.setText('abcaABC\ndefdDEF\nabcaABC') atom.config.set('vim-mode.useSmartcaseForSearch', true) - keydown(':') + openEx() submitNormalModeInputText(':substitute/abc/ghi/g') expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC') it "uses case sensitive search if smartcase is on and the pattern is uppercase", -> editor.setText('abcaABC\ndefdDEF\nabcaABC') - keydown(':') + openEx() submitNormalModeInputText(':substitute/ABC/ghi/g') expect(editor.getText()).toEqual('abcaghi\ndefdDEF\nabcaABC') @@ -709,37 +712,37 @@ describe "the commands", -> it "uses case insensitive search if smartcase is off and \c is in the pattern", -> atom.config.set('vim-mode.useSmartcaseForSearch', false) - keydown(':') + openEx() submitNormalModeInputText(':substitute/abc\\c/ghi/g') expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC') it "doesn't matter where in the pattern \\c is", -> atom.config.set('vim-mode.useSmartcaseForSearch', false) - keydown(':') + openEx() submitNormalModeInputText(':substitute/a\\cbc/ghi/g') expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC') it "uses case sensitive search if smartcase is on, \\C is in the pattern and the pattern is lowercase", -> atom.config.set('vim-mode.useSmartcaseForSearch', true) - keydown(':') + openEx() submitNormalModeInputText(':substitute/a\\Cbc/ghi/g') expect(editor.getText()).toEqual('ghiaABC\ndefdDEF\nabcaABC') it "overrides \\C with \\c if \\C comes first", -> atom.config.set('vim-mode.useSmartcaseForSearch', true) - keydown(':') + openEx() submitNormalModeInputText(':substitute/a\\Cb\\cc/ghi/g') expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC') it "overrides \\C with \\c if \\c comes first", -> atom.config.set('vim-mode.useSmartcaseForSearch', true) - keydown(':') + openEx() submitNormalModeInputText(':substitute/a\\cb\\Cc/ghi/g') expect(editor.getText()).toEqual('ghiaghi\ndefdDEF\nabcaABC') it "overrides an appended /i flag with \\C", -> atom.config.set('vim-mode.useSmartcaseForSearch', true) - keydown(':') + openEx() submitNormalModeInputText(':substitute/ab\\Cc/ghi/gi') expect(editor.getText()).toEqual('ghiaABC\ndefdDEF\nabcaABC') @@ -748,23 +751,23 @@ describe "the commands", -> editor.setText('abcaABC\ndefdDEF\nabcaABC') it "replaces \\1 with the first group", -> - keydown(':') + openEx() submitNormalModeInputText(':substitute/bc(.{2})/X\\1X') expect(editor.getText()).toEqual('aXaAXBC\ndefdDEF\nabcaABC') it "replaces multiple groups", -> - keydown(':') + openEx() submitNormalModeInputText(':substitute/a([a-z]*)aA([A-Z]*)/X\\1XY\\2Y') expect(editor.getText()).toEqual('XbcXYBCY\ndefdDEF\nabcaABC') it "replaces \\0 with the entire match", -> - keydown(':') + openEx() submitNormalModeInputText(':substitute/ab(ca)AB/X\\0X') expect(editor.getText()).toEqual('XabcaABXC\ndefdDEF\nabcaABC') describe ":set", -> it "throws an error without a specified option", -> - keydown(':') + openEx() submitNormalModeInputText(':set') expect(atom.notifications.notifications[0].message).toEqual( 'Command error: No option specified') @@ -772,7 +775,7 @@ describe "the commands", -> it "sets multiple options at once", -> atom.config.set('editor.showInvisibles', false) atom.config.set('editor.showLineNumbers', false) - keydown(':') + openEx() submitNormalModeInputText(':set list number') expect(atom.config.get('editor.showInvisibles')).toBe(true) expect(atom.config.get('editor.showLineNumbers')).toBe(true) @@ -783,7 +786,7 @@ describe "the commands", -> atom.config.set('editor.showLineNumbers', false) it "sets (no)list", -> - keydown(':') + openEx() submitNormalModeInputText(':set list') expect(atom.config.get('editor.showInvisibles')).toBe(true) atom.commands.dispatch(editorElement, 'ex-mode:open') @@ -791,7 +794,7 @@ describe "the commands", -> expect(atom.config.get('editor.showInvisibles')).toBe(false) it "sets (no)nu(mber)", -> - keydown(':') + openEx() submitNormalModeInputText(':set nu') expect(atom.config.get('editor.showLineNumbers')).toBe(true) atom.commands.dispatch(editorElement, 'ex-mode:open') @@ -805,7 +808,7 @@ describe "the commands", -> expect(atom.config.get('editor.showLineNumbers')).toBe(false) it "sets (no)sp(lit)r(ight)", -> - keydown(':') + openEx() submitNormalModeInputText(':set spr') expect(atom.config.get('ex-mode.splitright')).toBe(true) atom.commands.dispatch(editorElement, 'ex-mode:open') @@ -819,7 +822,7 @@ describe "the commands", -> expect(atom.config.get('ex-mode.splitright')).toBe(false) it "sets (no)s(plit)b(elow)", -> - keydown(':') + openEx() submitNormalModeInputText(':set sb') expect(atom.config.get('ex-mode.splitbelow')).toBe(true) atom.commands.dispatch(editorElement, 'ex-mode:open') @@ -836,7 +839,7 @@ describe "the commands", -> it "calls the aliased function without arguments", -> ExClass.registerAlias('W', 'w') spyOn(Ex, 'write') - keydown(':') + openEx() submitNormalModeInputText('W') expect(Ex.write).toHaveBeenCalled() @@ -844,7 +847,7 @@ describe "the commands", -> ExClass.registerAlias('W', 'write') spyOn(Ex, 'W').andCallThrough() spyOn(Ex, 'write') - keydown(':') + openEx() submitNormalModeInputText('W') WArgs = Ex.W.calls[0].args[0] writeArgs = Ex.write.calls[0].args[0]