From eb14239e260761ce6b18e5755d7930957470890d Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Thu, 3 Mar 2016 22:06:48 +0000 Subject: [PATCH 01/47] Runner skeleton --- keymaps/ava.cson | 2 ++ lib/main.coffee | 27 +++++++++++++++++++++++++++ lib/panel.coffee | 22 ++++++++++++++++++++++ package.json | 4 ++++ spec/main-spec.coffee | 28 ++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+) create mode 100644 keymaps/ava.cson create mode 100644 lib/main.coffee create mode 100644 lib/panel.coffee create mode 100644 spec/main-spec.coffee diff --git a/keymaps/ava.cson b/keymaps/ava.cson new file mode 100644 index 0000000..ff01f26 --- /dev/null +++ b/keymaps/ava.cson @@ -0,0 +1,2 @@ +'atom-workspace': + 'ctrl-alt-a': 'ava:toggle' diff --git a/lib/main.coffee b/lib/main.coffee new file mode 100644 index 0000000..274abd0 --- /dev/null +++ b/lib/main.coffee @@ -0,0 +1,27 @@ +{CompositeDisposable} = require 'atom' +Panel = require './panel' + +module.exports = TestingForAva = + testingForAvaView: null + modalPanel: null + subscriptions: null + + activate: (state) -> + @panel = new Panel(state.testingForAvaViewState) + @atomPanel = atom.workspace.addRightPanel(item: @panel, visible: false) + + @subscriptions = new CompositeDisposable + @subscriptions.add atom.commands.add 'atom-workspace', 'ava:toggle': => @toggle() + + deactivate: -> + @subscriptions.dispose() + @panel.destroy() + + serialize: -> + atomAva: @panel.serialize() + + toggle: -> + if @atomPanel.isVisible() + @atomPanel.hide() + else + @atomPanel.show() diff --git a/lib/panel.coffee b/lib/panel.coffee new file mode 100644 index 0000000..b873af6 --- /dev/null +++ b/lib/panel.coffee @@ -0,0 +1,22 @@ +module.exports = + class Panel + constructor: (serializedState) -> + # Create root element + @element = document.createElement('div') + @element.classList.add('ava') + + # Create message element + message = document.createElement('div') + message.textContent = "The TestingForAva package is Alive! It's ALIVE!" + message.classList.add('message') + @element.appendChild(message) + + # Returns an object that can be retrieved when package is activated + serialize: -> + + # Tear down any state and detach + destroy: -> + @element.remove() + + getElement: -> + @element diff --git a/package.json b/package.json index d8e5777..b178402 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "ava", "version": "0.2.0", "description": "Snippets for AVA", + "main": "./lib/main", "license": "MIT", "repository": "sindresorhus/atom-ava", "private": true, @@ -28,6 +29,9 @@ "scripts": { "test": "xo" }, + "activationCommands": { + "atom-workspace": "ava:toggle" + }, "keywords": [ "snippets", "test", diff --git a/spec/main-spec.coffee b/spec/main-spec.coffee new file mode 100644 index 0000000..b856df3 --- /dev/null +++ b/spec/main-spec.coffee @@ -0,0 +1,28 @@ +Main = require '../lib/main' + +describe "TestingForAva", -> + packageName = 'ava' + mainSelector = '.ava' + toggleCommand = 'ava:toggle' + [workspaceElement, activationPromise] = [] + + beforeEach -> + workspaceElement = atom.views.getView(atom.workspace) + activationPromise = atom.packages.activatePackage(packageName) + + describe "when the ava:toggle event is triggered", -> + it "hides and shows the view", -> + jasmine.attachToDOM(workspaceElement) + + expect(workspaceElement.querySelector(mainSelector)).not.toExist() + + atom.commands.dispatch workspaceElement, toggleCommand + + waitsForPromise -> + activationPromise + + runs -> + mainElement = workspaceElement.querySelector(mainSelector) + expect(mainElement).toBeVisible() + atom.commands.dispatch workspaceElement, toggleCommand + expect(mainElement).not.toBeVisible() From 4d02424378e73c4e8d16a7cb27c75cbf7d973690 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sat, 5 Mar 2016 21:16:50 +0000 Subject: [PATCH 02/47] Foundations of test runner --- keymaps/ava.cson | 1 + lib/main.coffee | 5 ++- lib/panel.coffee | 4 ++ lib/terminal-command-executor.coffee | 43 ++++++++++++++++++++++ lib/test-runner-process.coffee | 30 +++++++++++++++ package.json | 4 ++ spec/fake-spawn.coffee | 31 ++++++++++++++++ spec/terminal-command-executor-spec.coffee | 38 +++++++++++++++++++ spec/test-runner-process-spec.coffee | 33 +++++++++++++++++ 9 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 lib/terminal-command-executor.coffee create mode 100644 lib/test-runner-process.coffee create mode 100644 spec/fake-spawn.coffee create mode 100644 spec/terminal-command-executor-spec.coffee create mode 100644 spec/test-runner-process-spec.coffee diff --git a/keymaps/ava.cson b/keymaps/ava.cson index ff01f26..b3b4c29 100644 --- a/keymaps/ava.cson +++ b/keymaps/ava.cson @@ -1,2 +1,3 @@ 'atom-workspace': 'ctrl-alt-a': 'ava:toggle' + 'ctrl-alt-n': 'ava:run' diff --git a/lib/main.coffee b/lib/main.coffee index 274abd0..dc050ff 100644 --- a/lib/main.coffee +++ b/lib/main.coffee @@ -11,7 +11,10 @@ module.exports = TestingForAva = @atomPanel = atom.workspace.addRightPanel(item: @panel, visible: false) @subscriptions = new CompositeDisposable - @subscriptions.add atom.commands.add 'atom-workspace', 'ava:toggle': => @toggle() + + @subscriptions.add atom.commands.add 'atom-workspace', + 'ava:toggle': => @toggle() + 'ava:run': => @panel.run() deactivate: -> @subscriptions.dispose() diff --git a/lib/panel.coffee b/lib/panel.coffee index b873af6..6e27719 100644 --- a/lib/panel.coffee +++ b/lib/panel.coffee @@ -1,3 +1,5 @@ +TestRunnerProcess = require './test-runner-process' + module.exports = class Panel constructor: (serializedState) -> @@ -11,6 +13,8 @@ module.exports = message.classList.add('message') @element.appendChild(message) + run: -> + # Returns an object that can be retrieved when package is activated serialize: -> diff --git a/lib/terminal-command-executor.coffee b/lib/terminal-command-executor.coffee new file mode 100644 index 0000000..b1bd962 --- /dev/null +++ b/lib/terminal-command-executor.coffee @@ -0,0 +1,43 @@ +{Emitter} = require 'event-kit' +ChildProcess = require 'child_process' + +module.exports = + class TerminalCommandExecutor + constructor: -> + @emitter = new Emitter + + run: (command, destinyFolder = null) -> + @command = command + @destinyFolder = destinyFolder + + spawn = ChildProcess.spawn + + terminal = spawn("bash", ["-l"]) + terminal.on 'close', @streamClosed + terminal.stdout.on 'data', @stdOutDataReceived + terminal.stderr.on 'data', @stdErrDataReceived + + terminalCommand = if @destinyFolder then "cd \"#{@destinyFolder}\" && #{@command}\n" else "#{@command}\n" + + console.log "Launching command to terminal: #{terminalCommand}" + + terminal.stdin.write(terminalCommand) + terminal.stdin.write("exit\n") + + stdOutDataReceived: (newData) => + @emitter.emit 'onStdOutData', newData.toString() + + stdErrDataReceived: (newData) => + @emitter.emit 'onStdErrData', newData.toString() + + streamClosed: (code) => + @emitter.emit 'onFinishData', code + + onDataReceived: (callback) -> + @emitter.on 'onStdOutData', callback + + onDataFinished: (callback) -> + @emitter.on 'onFinishData', callback + + destroy: -> + @emitter.dispose() diff --git a/lib/test-runner-process.coffee b/lib/test-runner-process.coffee new file mode 100644 index 0000000..3553fec --- /dev/null +++ b/lib/test-runner-process.coffee @@ -0,0 +1,30 @@ +TerminalCommandExecutor = require './terminal-command-executor' +Parser = require 'tap-parser' + +module.exports = + class TestRunnerProcess + constructor: ( + executor = new TerminalCommandExecutor, + parser = Parser) -> + @avaParserStream = parser + @terminalCommandExecutor = executor + + onCompleteEvent: (callback) -> + @avaParserStream.on('complete', callback) + + onAssertEvent: (callback) -> + @avaParserStream.on('assert', callback) + + run: (file) -> + @terminalCommandExecutor.onDataReceived (data) => @addAvaOutput(data) + @terminalCommandExecutor.onDataFinished => @endAvaOutput() + + command = "ava #{file} --tap" + + @terminalCommandExecutor.run(command, atom.project.getPaths()[0]) + + addAvaOutput: (data) -> + @avaParserStream.write(data) + + endAvaOutput: -> + @avaParserStream.end() diff --git a/package.json b/package.json index b178402..001bbb7 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,10 @@ "ava", "mocha" ], + "dependencies": { + "event-kit": "^1.1.0", + "tap-parser": ">=v1.2.2" + }, "devDependencies": { "xo": "*" }, diff --git a/spec/fake-spawn.coffee b/spec/fake-spawn.coffee new file mode 100644 index 0000000..de13b49 --- /dev/null +++ b/spec/fake-spawn.coffee @@ -0,0 +1,31 @@ +module.exports = + class FakeSpawn + self = [] + constructor: -> + self = @ + @commandsReceived = [] + + on: (event, callback) -> + @mainCallBack = callback + + emulateClose: -> + @mainCallBack(0) + + stdout: { + write: (data) -> + @stdOutCallBack data + on: (event, callback) -> + @stdOutCallBack = callback + } + + stderr: { + write: (data) -> + @stdErrCallBack data + on: (event, callback) -> + @stdErrCallBack = callback + } + + stdin: { + write: (command) => + self.commandsReceived.push command + } diff --git a/spec/terminal-command-executor-spec.coffee b/spec/terminal-command-executor-spec.coffee new file mode 100644 index 0000000..ae43489 --- /dev/null +++ b/spec/terminal-command-executor-spec.coffee @@ -0,0 +1,38 @@ +TerminalCommandExecutor = require '../lib/terminal-command-executor' +ChildProcess = require 'child_process' +FakeSpawn = require './fake-spawn' + +describe 'TerminalCommandExecutor', -> + [executor, fake, exec, stdOutData, exitCode] = {} + + beforeEach -> + stdOutData = '' + exitCode = -1 + fake = new FakeSpawn + executor = new TerminalCommandExecutor + spyOn(ChildProcess, 'spawn').andReturn(fake) + + it 'can be created', -> + expect(executor).not.toBeNull() + + it 'writes the command and exits if not destination folder is provided', -> + executor.run 'command' + expect(fake.commandsReceived[0]).toBe('command\n') + expect(fake.commandsReceived[1]).toBe('exit\n') + + it 'writes the folder, command and exits if folder is provided', -> + executor.run 'command', 'dir' + expect(fake.commandsReceived[0]).toBe('cd "dir" && command\n') + expect(fake.commandsReceived[1]).toBe('exit\n') + + it 'calls the callback when new data appears in stdout', -> + executor.run 'command' + executor.onDataReceived (data) -> stdOutData = data + fake.stdout.write('some data') + expect(stdOutData).toBe('some data') + + it 'calls the callback when the stream is closed', -> + executor.run 'command' + executor.onDataFinished (code) -> exitCode = code + fake.emulateClose() + expect(exitCode).toBe(0) diff --git a/spec/test-runner-process-spec.coffee b/spec/test-runner-process-spec.coffee new file mode 100644 index 0000000..a18c406 --- /dev/null +++ b/spec/test-runner-process-spec.coffee @@ -0,0 +1,33 @@ +TestRunnerProcess = require '../lib/test-runner-process' +TerminalCommandExecutor = require '../lib/terminal-command-executor' + +describe 'TestRunnerProcess', -> + [runner, executor, parser] = {} + + beforeEach -> + executor = new TerminalCommandExecutor + parser = ((completeCallBack) -> + write: -> + end: () ->)() + runner = new TestRunnerProcess(executor, parser) + + it 'can be created', -> + expect(runner).not.toBeNull() + + it 'runs the executor with the appropriate parameters', -> + spyOn(atom.project, 'getPaths').andReturn(['path']) + spyOn(executor, 'run') + runner.run('filename') + expect(executor.run).toHaveBeenCalledWith('ava filename --tap', 'path') + + it 'redirects the output for the parser when is received', -> + spyOn(parser, 'write') + runner.run() + executor.stdOutDataReceived 'newdata' + expect(parser.write).toHaveBeenCalledWith('newdata') + + it 'closes the parser stream when the output is over', -> + spyOn(parser, 'end') + runner.run() + executor.streamClosed 0 + expect(parser.end).toHaveBeenCalled() From ca43e68723af5cb9693ed4c81b1de25e6f8ca459 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sun, 6 Mar 2016 01:21:38 +0000 Subject: [PATCH 03/47] First version of working UI without format --- lib/panel.coffee | 58 +++++++++++++++++++++++----- lib/test-runner-process.coffee | 33 ++++++++-------- spec/test-runner-process-spec.coffee | 13 ++++--- styles/ava.less | 11 ++++++ 4 files changed, 85 insertions(+), 30 deletions(-) create mode 100644 styles/ava.less diff --git a/lib/panel.coffee b/lib/panel.coffee index 6e27719..e013046 100644 --- a/lib/panel.coffee +++ b/lib/panel.coffee @@ -2,18 +2,58 @@ TestRunnerProcess = require './test-runner-process' module.exports = class Panel - constructor: (serializedState) -> - # Create root element - @element = document.createElement('div') - @element.classList.add('ava') - - # Create message element - message = document.createElement('div') - message.textContent = "The TestingForAva package is Alive! It's ALIVE!" - message.classList.add('message') + constructor: ( + serializedState, + testRunnerProcess = new TestRunnerProcess) -> + @testRunnerProcess = testRunnerProcess + @renderBase() + + renderBase: -> + @element = @createElement('div', 'ava') + + message = @createElement('div', 'message') + message.textContent = "AVA test runner" @element.appendChild(message) + @executing = @createElement('div', 'executing') + @executing.textContent = 'Loading' + @executing.style.display = 'none' + @element.appendChild(@executing) + + @testsContainer = @createElement('div', 'test-container') + @element.appendChild(@testsContainer) + run: -> + @toggleExecutingIndicator() + @testsContainer.innerHTML = '' + editor = atom.workspace.getActiveTextEditor() + currentFileName = editor.buffer.file.path + @testRunnerProcess.run currentFileName, @renderAssert, @renderFinalReport + + renderAssert: (result) => + newTest = @createElement('div', 'test') + status = if result.ok then 'OK' else 'NO' + newTest.textContent = "#{status} - #{result.name}" + @testsContainer.appendChild newTest + + renderFinalReport: (results) => + @toggleExecutingIndicator() + summary = @createElement('div', 'summary') + percentage = Math.round((results.pass/results.count)*100) + summary.textContent = "#{results.count} total - #{percentage}% passed" + @testsContainer.appendChild summary + + createElement: (elementType, cssClass = null) -> + element = document.createElement(elementType) + if (cssClass?) + element.classList.add(cssClass) + element + + toggleExecutingIndicator: => + if (@executing.style.display is 'block') + @executing.style.display = 'none' + else + @executing.style.display = 'block' # Returns an object that can be retrieved when package is activated serialize: -> diff --git a/lib/test-runner-process.coffee b/lib/test-runner-process.coffee index 3553fec..8d05efa 100644 --- a/lib/test-runner-process.coffee +++ b/lib/test-runner-process.coffee @@ -1,30 +1,31 @@ -TerminalCommandExecutor = require './terminal-command-executor' Parser = require 'tap-parser' +TerminalCommandExecutor = require './terminal-command-executor' module.exports = class TestRunnerProcess - constructor: ( - executor = new TerminalCommandExecutor, - parser = Parser) -> - @avaParserStream = parser + constructor: (executor = new TerminalCommandExecutor) -> @terminalCommandExecutor = executor - - onCompleteEvent: (callback) -> - @avaParserStream.on('complete', callback) - - onAssertEvent: (callback) -> - @avaParserStream.on('assert', callback) - - run: (file) -> @terminalCommandExecutor.onDataReceived (data) => @addAvaOutput(data) @terminalCommandExecutor.onDataFinished => @endAvaOutput() + run: (filePath, assertCallback, completeCallback) -> + @parser = @getParser() + @parser.on('assert', assertCallback) + @parser.on('complete', completeCallback) + + #TODO: Fix parsing of folders and files + folder = filePath.substring(0, filePath.lastIndexOf("/") + 1); + file = filePath.split("/").pop() + command = "ava #{file} --tap" - @terminalCommandExecutor.run(command, atom.project.getPaths()[0]) + @terminalCommandExecutor.run(command, folder) addAvaOutput: (data) -> - @avaParserStream.write(data) + @parser.write(data) endAvaOutput: -> - @avaParserStream.end() + @parser.end() + + getParser: -> + Parser() diff --git a/spec/test-runner-process-spec.coffee b/spec/test-runner-process-spec.coffee index a18c406..c7eb026 100644 --- a/spec/test-runner-process-spec.coffee +++ b/spec/test-runner-process-spec.coffee @@ -7,9 +7,12 @@ describe 'TestRunnerProcess', -> beforeEach -> executor = new TerminalCommandExecutor parser = ((completeCallBack) -> + on: (eventName, callBack) -> write: -> end: () ->)() - runner = new TestRunnerProcess(executor, parser) + + runner = new TestRunnerProcess(executor) + spyOn(runner, 'getParser').andReturn(parser) it 'can be created', -> expect(runner).not.toBeNull() @@ -17,17 +20,17 @@ describe 'TestRunnerProcess', -> it 'runs the executor with the appropriate parameters', -> spyOn(atom.project, 'getPaths').andReturn(['path']) spyOn(executor, 'run') - runner.run('filename') - expect(executor.run).toHaveBeenCalledWith('ava filename --tap', 'path') + runner.run('/somefolder/filename') + expect(executor.run).toHaveBeenCalledWith('ava filename --tap', '/somefolder/') it 'redirects the output for the parser when is received', -> spyOn(parser, 'write') - runner.run() + runner.run('/somefolder/filename') executor.stdOutDataReceived 'newdata' expect(parser.write).toHaveBeenCalledWith('newdata') it 'closes the parser stream when the output is over', -> spyOn(parser, 'end') - runner.run() + runner.run('/somefolder/filename') executor.streamClosed 0 expect(parser.end).toHaveBeenCalled() diff --git a/styles/ava.less b/styles/ava.less new file mode 100644 index 0000000..ad363d0 --- /dev/null +++ b/styles/ava.less @@ -0,0 +1,11 @@ +@import "ui-variables"; + +.ava { + padding: 30px; + font-size: 15px; + + .message { padding-bottom: 10px; } + .summary { font-size: 12px; padding-top: 10px; } + .test { font-size: 11px; font-weight: bold; } + .executing { font-size: 12px; font-weight: bold; padding-bottom: 10px;} +} From f58fde702051bafea018c270f668e6fb5fd83eb1 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sun, 6 Mar 2016 01:34:04 +0000 Subject: [PATCH 04/47] Assigning x as key for running tests --- keymaps/ava.cson | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keymaps/ava.cson b/keymaps/ava.cson index b3b4c29..6742a6a 100644 --- a/keymaps/ava.cson +++ b/keymaps/ava.cson @@ -1,3 +1,3 @@ 'atom-workspace': 'ctrl-alt-a': 'ava:toggle' - 'ctrl-alt-n': 'ava:run' + 'ctrl-alt-x': 'ava:run' From d1ac4308947d24b8c09789a58e1d15589d5c6b74 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Tue, 8 Mar 2016 19:34:16 +0000 Subject: [PATCH 05/47] Conversion of main.js to ES6 --- lib/main.coffee | 30 ------------------------------ lib/main.js | 33 +++++++++++++++++++++++++++++++++ spec/main-spec.coffee | 28 ---------------------------- spec/main-spec.js | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 58 deletions(-) delete mode 100644 lib/main.coffee create mode 100644 lib/main.js delete mode 100644 spec/main-spec.coffee create mode 100644 spec/main-spec.js diff --git a/lib/main.coffee b/lib/main.coffee deleted file mode 100644 index dc050ff..0000000 --- a/lib/main.coffee +++ /dev/null @@ -1,30 +0,0 @@ -{CompositeDisposable} = require 'atom' -Panel = require './panel' - -module.exports = TestingForAva = - testingForAvaView: null - modalPanel: null - subscriptions: null - - activate: (state) -> - @panel = new Panel(state.testingForAvaViewState) - @atomPanel = atom.workspace.addRightPanel(item: @panel, visible: false) - - @subscriptions = new CompositeDisposable - - @subscriptions.add atom.commands.add 'atom-workspace', - 'ava:toggle': => @toggle() - 'ava:run': => @panel.run() - - deactivate: -> - @subscriptions.dispose() - @panel.destroy() - - serialize: -> - atomAva: @panel.serialize() - - toggle: -> - if @atomPanel.isVisible() - @atomPanel.hide() - else - @atomPanel.show() diff --git a/lib/main.js b/lib/main.js new file mode 100644 index 0000000..17ce8c6 --- /dev/null +++ b/lib/main.js @@ -0,0 +1,33 @@ +'use babel'; + +const Panel = require('./panel'); +const { CompositeDisposable } = require('atom'); + +module.exports = { + activate(state) { + this.panel = new Panel(state.testingForAvaViewState); + this.atomPanel = atom.workspace.addRightPanel({item: this.panel, visible: false}); + this.subscriptions = new CompositeDisposable(); + + this.subscriptions.add( + atom.commands.add('atom-workspace', 'ava:toggle', () => this.toggle())); + + this.subscriptions.add( + atom.commands.add('atom-workspace', 'ava:run', () => this.panel.run())); + }, + deactivate() { + this.subscriptions.dispose(); + this.panel.destroy(); + }, + serialize() { + this.atomAva = this.panel.serialize(); + }, + toggle() { + if (this.atomPanel.isVisible()) { + this.atomPanel.hide(); + } + else { + this.atomPanel.show(); + } + } +}; diff --git a/spec/main-spec.coffee b/spec/main-spec.coffee deleted file mode 100644 index b856df3..0000000 --- a/spec/main-spec.coffee +++ /dev/null @@ -1,28 +0,0 @@ -Main = require '../lib/main' - -describe "TestingForAva", -> - packageName = 'ava' - mainSelector = '.ava' - toggleCommand = 'ava:toggle' - [workspaceElement, activationPromise] = [] - - beforeEach -> - workspaceElement = atom.views.getView(atom.workspace) - activationPromise = atom.packages.activatePackage(packageName) - - describe "when the ava:toggle event is triggered", -> - it "hides and shows the view", -> - jasmine.attachToDOM(workspaceElement) - - expect(workspaceElement.querySelector(mainSelector)).not.toExist() - - atom.commands.dispatch workspaceElement, toggleCommand - - waitsForPromise -> - activationPromise - - runs -> - mainElement = workspaceElement.querySelector(mainSelector) - expect(mainElement).toBeVisible() - atom.commands.dispatch workspaceElement, toggleCommand - expect(mainElement).not.toBeVisible() diff --git a/spec/main-spec.js b/spec/main-spec.js new file mode 100644 index 0000000..b4b87c9 --- /dev/null +++ b/spec/main-spec.js @@ -0,0 +1,33 @@ +'use babel'; + +describe('TestingForAva', () => { + let packageName = 'ava'; + let mainSelector = '.ava'; + let toggleCommand = 'ava:toggle'; + let workspaceElement = []; + let activationPromise = []; + + beforeEach(() => { + workspaceElement = atom.views.getView(atom.workspace); + activationPromise = atom.packages.activatePackage(packageName); + }); + + describe('when the ava:toggle event is triggered', () => { + it('hides and shows the view', () => { + jasmine.attachToDOM(workspaceElement); + + expect(workspaceElement.querySelector(mainSelector)).not.toExist(); + + atom.commands.dispatch(workspaceElement, toggleCommand); + + waitsForPromise(() => activationPromise); + + runs(() => { + let mainElement = workspaceElement.querySelector(mainSelector); + expect(mainElement).toBeVisible(); + atom.commands.dispatch(workspaceElement, toggleCommand); + expect(mainElement).not.toBeVisible(); + }); + }); + }); +}); From 7986bbdab3536148b8900789ca5dcf37dad23ae7 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Tue, 8 Mar 2016 20:14:24 +0000 Subject: [PATCH 06/47] Conversion of panel.js to ES6 --- lib/panel.coffee | 66 -------------------------------------------- lib/panel.js | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 66 deletions(-) delete mode 100644 lib/panel.coffee create mode 100644 lib/panel.js diff --git a/lib/panel.coffee b/lib/panel.coffee deleted file mode 100644 index e013046..0000000 --- a/lib/panel.coffee +++ /dev/null @@ -1,66 +0,0 @@ -TestRunnerProcess = require './test-runner-process' - -module.exports = - class Panel - constructor: ( - serializedState, - testRunnerProcess = new TestRunnerProcess) -> - @testRunnerProcess = testRunnerProcess - @renderBase() - - renderBase: -> - @element = @createElement('div', 'ava') - - message = @createElement('div', 'message') - message.textContent = "AVA test runner" - @element.appendChild(message) - - @executing = @createElement('div', 'executing') - @executing.textContent = 'Loading' - @executing.style.display = 'none' - @element.appendChild(@executing) - - @testsContainer = @createElement('div', 'test-container') - @element.appendChild(@testsContainer) - - run: -> - @toggleExecutingIndicator() - @testsContainer.innerHTML = '' - editor = atom.workspace.getActiveTextEditor() - currentFileName = editor.buffer.file.path - @testRunnerProcess.run currentFileName, @renderAssert, @renderFinalReport - - renderAssert: (result) => - newTest = @createElement('div', 'test') - status = if result.ok then 'OK' else 'NO' - newTest.textContent = "#{status} - #{result.name}" - @testsContainer.appendChild newTest - - renderFinalReport: (results) => - @toggleExecutingIndicator() - summary = @createElement('div', 'summary') - percentage = Math.round((results.pass/results.count)*100) - summary.textContent = "#{results.count} total - #{percentage}% passed" - @testsContainer.appendChild summary - - createElement: (elementType, cssClass = null) -> - element = document.createElement(elementType) - if (cssClass?) - element.classList.add(cssClass) - element - - toggleExecutingIndicator: => - if (@executing.style.display is 'block') - @executing.style.display = 'none' - else - @executing.style.display = 'block' - - # Returns an object that can be retrieved when package is activated - serialize: -> - - # Tear down any state and detach - destroy: -> - @element.remove() - - getElement: -> - @element diff --git a/lib/panel.js b/lib/panel.js new file mode 100644 index 0000000..e994e5b --- /dev/null +++ b/lib/panel.js @@ -0,0 +1,71 @@ +'use babel'; + +const TestRunnerProcess = require('./test-runner-process'); + +class Panel { + constructor(serializedState, testRunnerProcess = new TestRunnerProcess) { + this.testRunnerProcess = testRunnerProcess; + this.renderBase(); + } + + renderBase() { + this.element = this.createElement('div', 'ava'); + let message = this.createElement('div', 'message'); + message.textContent = 'AVA test runner'; + this.element.appendChild(message); + + this.executing = this.createElement('div', 'executing'); + this.executing.textContent = 'Loading'; + this.executing.style.display = 'none'; + this.element.appendChild(this.executing); + + this.testsContainer = this.createElement('div', 'test-container'); + this.element.appendChild(this.testsContainer); + } + + run() { + this.toggleExecutingIndicator(); + this.testsContainer.innerHTML = ''; + let editor = atom.workspace.getActiveTextEditor(); + let currentFileName = editor.buffer.file.path; + this.testRunnerProcess.run(currentFileName, this.renderAssert, this.renderFinalReport); + } + + renderAssert(result) { + let newTest = this.createElement('div', 'test'); + let status = (result.ok) ? 'OK' : 'NO'; + newTest.textContent = `${status} - ${result.name}`; + this.testsContainer.appendChild(newTest); + } + + renderFinalReport(results) { + this.toggleExecutingIndicator(); + + let summary = this.createElement('div', 'summary'); + let percentage = Math.round((results.pass/results.count)*100); + summary.textContent = `${results.count} total - ${percentage}% passed`; + + this.testsContainer.appendChild(summary); + } + + createElement(elementType, cssClass = null) { + let element = document.createElement(elementType); + if (cssClass) { + element.classList.add(cssClass); + } + return element; + } + + toggleExecutingIndicator() { + this.executing.style.display = + (this.executing.style.display === 'block') ? 'none' : 'block'; + } + + serialize() { } + + destroy() { + this.element.remove(); + } +} + +module.exports = Panel; From 7d8bec9aef3c40ed67616d11f43ac44481b9871c Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Tue, 8 Mar 2016 22:32:22 +0000 Subject: [PATCH 07/47] Conversion of test-runner-process.js to ES6 --- lib/panel.js | 13 +++++++---- lib/test-runner-process.coffee | 31 -------------------------- lib/test-runner-process.js | 40 ++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 35 deletions(-) delete mode 100644 lib/test-runner-process.coffee create mode 100644 lib/test-runner-process.js diff --git a/lib/panel.js b/lib/panel.js index e994e5b..fa78f75 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -28,13 +28,18 @@ class Panel { this.testsContainer.innerHTML = ''; let editor = atom.workspace.getActiveTextEditor(); let currentFileName = editor.buffer.file.path; - this.testRunnerProcess.run(currentFileName, this.renderAssert, this.renderFinalReport); + + //TODO: Fix events + this.testRunnerProcess.run( + currentFileName, + (result) => this.renderAssert(result), + (results) => this.renderFinalReport(results)); } - renderAssert(result) { + renderAssert(assert) { let newTest = this.createElement('div', 'test'); - let status = (result.ok) ? 'OK' : 'NO'; - newTest.textContent = `${status} - ${result.name}`; + let status = (assert.ok) ? 'OK' : 'NO'; + newTest.textContent = `${status} - ${assert.name}`; this.testsContainer.appendChild(newTest); } diff --git a/lib/test-runner-process.coffee b/lib/test-runner-process.coffee deleted file mode 100644 index 8d05efa..0000000 --- a/lib/test-runner-process.coffee +++ /dev/null @@ -1,31 +0,0 @@ -Parser = require 'tap-parser' -TerminalCommandExecutor = require './terminal-command-executor' - -module.exports = - class TestRunnerProcess - constructor: (executor = new TerminalCommandExecutor) -> - @terminalCommandExecutor = executor - @terminalCommandExecutor.onDataReceived (data) => @addAvaOutput(data) - @terminalCommandExecutor.onDataFinished => @endAvaOutput() - - run: (filePath, assertCallback, completeCallback) -> - @parser = @getParser() - @parser.on('assert', assertCallback) - @parser.on('complete', completeCallback) - - #TODO: Fix parsing of folders and files - folder = filePath.substring(0, filePath.lastIndexOf("/") + 1); - file = filePath.split("/").pop() - - command = "ava #{file} --tap" - - @terminalCommandExecutor.run(command, folder) - - addAvaOutput: (data) -> - @parser.write(data) - - endAvaOutput: -> - @parser.end() - - getParser: -> - Parser() diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js new file mode 100644 index 0000000..da55fe0 --- /dev/null +++ b/lib/test-runner-process.js @@ -0,0 +1,40 @@ +'use babel'; + +const Parser = require('tap-parser'); +const TerminalCommandExecutor = require('./terminal-command-executor'); + +class TestRunnerProcess +{ + constructor(executor = new TerminalCommandExecutor) { + this.terminalCommandExecutor = executor; + this.terminalCommandExecutor.onDataReceived((data) => this.addAvaOutput(data)); + this.terminalCommandExecutor.onDataFinished(() => this.endAvaOutput()); + } + + run(filePath, assertCallback, completeCallback) { + this.parser = this.getParser(); + this.parser.on('assert', (assert) => assertCallback(assert)); + this.parser.on('complete', (results) => completeCallback(results)); + + //TODO: Fix parsing of folders and files + let folder = filePath.substring(0, filePath.lastIndexOf('/') + 1); + let file = filePath.split('/').pop(); + + let command = `ava ${file} --tap`; + this.terminalCommandExecutor.run(command, folder); + } + + addAvaOutput(data) { + this.parser.write(data); + } + + endAvaOutput() { + this.parser.end(); + } + + getParser() { + return Parser(); + } +} + +module.exports = TestRunnerProcess; From b872b854be4c7e3b8ad88ce48aa518dc4cd9acb0 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Tue, 8 Mar 2016 22:32:57 +0000 Subject: [PATCH 08/47] Conversion of test-runner-process.js to ES6 --- spec/test-runner-process-spec.coffee | 36 ---------------------- spec/test-runner-process-spec.js | 46 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 36 deletions(-) delete mode 100644 spec/test-runner-process-spec.coffee create mode 100644 spec/test-runner-process-spec.js diff --git a/spec/test-runner-process-spec.coffee b/spec/test-runner-process-spec.coffee deleted file mode 100644 index c7eb026..0000000 --- a/spec/test-runner-process-spec.coffee +++ /dev/null @@ -1,36 +0,0 @@ -TestRunnerProcess = require '../lib/test-runner-process' -TerminalCommandExecutor = require '../lib/terminal-command-executor' - -describe 'TestRunnerProcess', -> - [runner, executor, parser] = {} - - beforeEach -> - executor = new TerminalCommandExecutor - parser = ((completeCallBack) -> - on: (eventName, callBack) -> - write: -> - end: () ->)() - - runner = new TestRunnerProcess(executor) - spyOn(runner, 'getParser').andReturn(parser) - - it 'can be created', -> - expect(runner).not.toBeNull() - - it 'runs the executor with the appropriate parameters', -> - spyOn(atom.project, 'getPaths').andReturn(['path']) - spyOn(executor, 'run') - runner.run('/somefolder/filename') - expect(executor.run).toHaveBeenCalledWith('ava filename --tap', '/somefolder/') - - it 'redirects the output for the parser when is received', -> - spyOn(parser, 'write') - runner.run('/somefolder/filename') - executor.stdOutDataReceived 'newdata' - expect(parser.write).toHaveBeenCalledWith('newdata') - - it 'closes the parser stream when the output is over', -> - spyOn(parser, 'end') - runner.run('/somefolder/filename') - executor.streamClosed 0 - expect(parser.end).toHaveBeenCalled() diff --git a/spec/test-runner-process-spec.js b/spec/test-runner-process-spec.js new file mode 100644 index 0000000..0577b42 --- /dev/null +++ b/spec/test-runner-process-spec.js @@ -0,0 +1,46 @@ +'use babel'; + +const TestRunnerProcess = require('../lib/test-runner-process'); +const TerminalCommandExecutor = require('../lib/terminal-command-executor'); + +describe('TestRunnerProcess', () => { + let runner = {}; + let executor = {}; + let parser = {}; + + beforeEach(() => { + executor = new TerminalCommandExecutor(); + + parser = { + on(eventName, callBack) {}, + write() {}, + end() {} + }; + + runner = new TestRunnerProcess(executor); + spyOn(runner, 'getParser').andReturn(parser); + }); + + it('can be created', () => expect(runner).not.toBeNull()); + + it('runs the executor with the appropriate parameters', () => { + spyOn(atom.project, 'getPaths').andReturn(['path']); + spyOn(executor, 'run'); + runner.run('/somefolder/filename'); + expect(executor.run).toHaveBeenCalledWith('ava filename --tap', '/somefolder/'); + }); + + it('redirects the output for the parser when is received', () => { + spyOn(parser, 'write'); + runner.run('/somefolder/filename'); + executor.stdOutDataReceived('newdata'); + expect(parser.write).toHaveBeenCalledWith('newdata'); + }); + + it('closes the parser stream when the output is over', () => { + spyOn(parser, 'end'); + runner.run('/somefolder/filename'); + executor.streamClosed(0); + expect(parser.end).toHaveBeenCalled(); + }); +}); From 77e54eec25421de5e5dacab6e2b26d2a0f081f4e Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Fri, 11 Mar 2016 23:29:34 +0000 Subject: [PATCH 09/47] Small redesign on runner process events --- lib/panel.js | 11 +++--- lib/test-runner-process.js | 65 +++++++++++++++++++++++--------- spec/test-runner-process-spec.js | 2 +- 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/lib/panel.js b/lib/panel.js index fa78f75..485d11d 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -5,6 +5,10 @@ const TestRunnerProcess = require('./test-runner-process'); class Panel { constructor(serializedState, testRunnerProcess = new TestRunnerProcess) { this.testRunnerProcess = testRunnerProcess; + + this.testRunnerProcess.on('assert', (result) => this.renderAssert(result)); + this.testRunnerProcess.on('complete', (results) => this.renderFinalReport(results)); + this.renderBase(); } @@ -28,12 +32,7 @@ class Panel { this.testsContainer.innerHTML = ''; let editor = atom.workspace.getActiveTextEditor(); let currentFileName = editor.buffer.file.path; - - //TODO: Fix events - this.testRunnerProcess.run( - currentFileName, - (result) => this.renderAssert(result), - (results) => this.renderFinalReport(results)); + this.testRunnerProcess.run(currentFileName); } renderAssert(assert) { diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index da55fe0..f3934ab 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -3,36 +3,67 @@ const Parser = require('tap-parser'); const TerminalCommandExecutor = require('./terminal-command-executor'); -class TestRunnerProcess -{ - constructor(executor = new TerminalCommandExecutor) { +class FilePathParser { + parse(filePath) { + //TODO: Fix parsing of folders and files + let folder = filePath.substring(0, filePath.lastIndexOf('/') + 1); + let file = filePath.split('/').pop(); + return { + folder: folder, + file: file + }; + } +} + +class TestRunnerProcess { + constructor( + executor = new TerminalCommandExecutor, + filePathParser = new FilePathParser) { + + this.eventHandlers = {}; + this.filePathParser = filePathParser; this.terminalCommandExecutor = executor; - this.terminalCommandExecutor.onDataReceived((data) => this.addAvaOutput(data)); - this.terminalCommandExecutor.onDataFinished(() => this.endAvaOutput()); + this.terminalCommandExecutor.onDataReceived((data) => this._addAvaOutput(data)); + this.terminalCommandExecutor.onDataFinished(() => this._endAvaOutput()); } - run(filePath, assertCallback, completeCallback) { - this.parser = this.getParser(); - this.parser.on('assert', (assert) => assertCallback(assert)); - this.parser.on('complete', (results) => completeCallback(results)); + on(event, callBack) { + if (!['assert', 'complete'].some(e => e === event)) { + return; + } - //TODO: Fix parsing of folders and files - let folder = filePath.substring(0, filePath.lastIndexOf('/') + 1); - let file = filePath.split('/').pop(); + this.eventHandlers[event] = callBack; + } + + run(filePath) { + this.parser = this._getParser(); + this._setHandlersOnParser(this.parser); + + let filePathParseResult = this.filePathParser.parse(filePath); + let command = `ava ${filePathParseResult.file} --tap`; + + this.terminalCommandExecutor.run(command, filePathParseResult.folder); + } + + _setHandlersOnParser(parser) { + if (this.eventHandlers['assert']) { + parser.on('assert', (assert) => this.eventHandlers['assert'](assert)); + } - let command = `ava ${file} --tap`; - this.terminalCommandExecutor.run(command, folder); + if (this.eventHandlers['complete']) { + parser.on('complete', (results) => this.eventHandlers['complete'](results)); + } } - addAvaOutput(data) { + _addAvaOutput(data) { this.parser.write(data); } - endAvaOutput() { + _endAvaOutput() { this.parser.end(); } - getParser() { + _getParser() { return Parser(); } } diff --git a/spec/test-runner-process-spec.js b/spec/test-runner-process-spec.js index 0577b42..e50269b 100644 --- a/spec/test-runner-process-spec.js +++ b/spec/test-runner-process-spec.js @@ -18,7 +18,7 @@ describe('TestRunnerProcess', () => { }; runner = new TestRunnerProcess(executor); - spyOn(runner, 'getParser').andReturn(parser); + spyOn(runner, '_getParser').andReturn(parser); }); it('can be created', () => expect(runner).not.toBeNull()); From 596f5e520c29044e9f308f6796c3d4bb2829ba69 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Fri, 11 Mar 2016 23:40:22 +0000 Subject: [PATCH 10/47] Dependencies updated --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 001bbb7..b927c92 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ ], "dependencies": { "event-kit": "^1.1.0", - "tap-parser": ">=v1.2.2" + "tap-parser": "^1.2.2", + "ava": "^0.13.0" }, "devDependencies": { "xo": "*" From 4fabeeca3d5443dd93054b6c483464446120251b Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sat, 12 Mar 2016 13:50:02 +0000 Subject: [PATCH 11/47] TestRunnerProcess emits events now with EventEmitter --- lib/test-runner-process.js | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index f3934ab..39510f3 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -1,6 +1,8 @@ 'use babel'; +// const util = require('util'); const Parser = require('tap-parser'); +const EventEmitter = require('events'); const TerminalCommandExecutor = require('./terminal-command-executor'); class FilePathParser { @@ -15,24 +17,19 @@ class FilePathParser { } } -class TestRunnerProcess { +class TestRunnerProcess extends EventEmitter { constructor( executor = new TerminalCommandExecutor, filePathParser = new FilePathParser) { + super(); this.eventHandlers = {}; this.filePathParser = filePathParser; this.terminalCommandExecutor = executor; this.terminalCommandExecutor.onDataReceived((data) => this._addAvaOutput(data)); this.terminalCommandExecutor.onDataFinished(() => this._endAvaOutput()); - } - - on(event, callBack) { - if (!['assert', 'complete'].some(e => e === event)) { - return; - } - this.eventHandlers[event] = callBack; + EventEmitter.call(this); } run(filePath) { @@ -46,13 +43,8 @@ class TestRunnerProcess { } _setHandlersOnParser(parser) { - if (this.eventHandlers['assert']) { - parser.on('assert', (assert) => this.eventHandlers['assert'](assert)); - } - - if (this.eventHandlers['complete']) { - parser.on('complete', (results) => this.eventHandlers['complete'](results)); - } + parser.on('assert', (assert) => this.emit('assert', assert)); + parser.on('complete', (results) => this.emit('complete', results)); } _addAvaOutput(data) { @@ -66,6 +58,10 @@ class TestRunnerProcess { _getParser() { return Parser(); } + + destroy() { + this.terminalCommandExecutor.destroy(); + } } module.exports = TestRunnerProcess; From ddc1f8e60c9ad360bba7e4fe3f42c101dccfb967 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sat, 12 Mar 2016 16:48:27 +0000 Subject: [PATCH 12/47] Conversion of terminal-command-executor to ES6 --- lib/terminal-command-executor.coffee | 43 ---------------- lib/terminal-command-executor.js | 57 ++++++++++++++++++++++ lib/test-runner-process.js | 1 - spec/terminal-command-executor-spec.coffee | 38 --------------- spec/terminal-command-executor-spec.js | 50 +++++++++++++++++++ 5 files changed, 107 insertions(+), 82 deletions(-) delete mode 100644 lib/terminal-command-executor.coffee create mode 100644 lib/terminal-command-executor.js delete mode 100644 spec/terminal-command-executor-spec.coffee create mode 100644 spec/terminal-command-executor-spec.js diff --git a/lib/terminal-command-executor.coffee b/lib/terminal-command-executor.coffee deleted file mode 100644 index b1bd962..0000000 --- a/lib/terminal-command-executor.coffee +++ /dev/null @@ -1,43 +0,0 @@ -{Emitter} = require 'event-kit' -ChildProcess = require 'child_process' - -module.exports = - class TerminalCommandExecutor - constructor: -> - @emitter = new Emitter - - run: (command, destinyFolder = null) -> - @command = command - @destinyFolder = destinyFolder - - spawn = ChildProcess.spawn - - terminal = spawn("bash", ["-l"]) - terminal.on 'close', @streamClosed - terminal.stdout.on 'data', @stdOutDataReceived - terminal.stderr.on 'data', @stdErrDataReceived - - terminalCommand = if @destinyFolder then "cd \"#{@destinyFolder}\" && #{@command}\n" else "#{@command}\n" - - console.log "Launching command to terminal: #{terminalCommand}" - - terminal.stdin.write(terminalCommand) - terminal.stdin.write("exit\n") - - stdOutDataReceived: (newData) => - @emitter.emit 'onStdOutData', newData.toString() - - stdErrDataReceived: (newData) => - @emitter.emit 'onStdErrData', newData.toString() - - streamClosed: (code) => - @emitter.emit 'onFinishData', code - - onDataReceived: (callback) -> - @emitter.on 'onStdOutData', callback - - onDataFinished: (callback) -> - @emitter.on 'onFinishData', callback - - destroy: -> - @emitter.dispose() diff --git a/lib/terminal-command-executor.js b/lib/terminal-command-executor.js new file mode 100644 index 0000000..a9ee2f2 --- /dev/null +++ b/lib/terminal-command-executor.js @@ -0,0 +1,57 @@ +'use babel'; + +const { Emitter } = require('event-kit'); +const ChildProcess = require('child_process'); + +class TerminalCommandExecutor { + constructor() { + this.emitter = new Emitter(); + } + + run(command, destinyFolder = null) { + this.command = command; + this.destinyFolder = destinyFolder; + + let spawn = ChildProcess.spawn; + + let terminal = spawn('bash', ['-l']); + terminal.on('close', (statusCode) => this.streamClosed(statusCode)); + terminal.stdout.on('data', (data) => this.stdOutDataReceived(data)); + terminal.stderr.on('data', (data) => this.stdErrDataReceived(data)); + + let terminalCommand = (this.destinyFolder) + ? `cd \"${this.destinyFolder}\" && ${this.command}\n` + : `${this.command}\n`; + + console.log('Launching command to terminal: #{terminalCommand}'); + + terminal.stdin.write(terminalCommand); + terminal.stdin.write('exit\n'); + } + + stdOutDataReceived(newData) { + this.emitter.emit('onStdOutData', newData.toString()); + } + + stdErrDataReceived(newData) { + this.emitter.emit('onStdErrData', newData.toString()); + } + + streamClosed(code) { + this.emitter.emit('onFinishData', code); + } + + onDataReceived(callback) { + this.emitter.on('onStdOutData', callback); + } + + onDataFinished(callback) { + this.emitter.on('onFinishData', callback); + } + + destroy() { + this.emitter.dispose(); + } +} + +module.exports = TerminalCommandExecutor; diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index 39510f3..1a87dfc 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -1,6 +1,5 @@ 'use babel'; -// const util = require('util'); const Parser = require('tap-parser'); const EventEmitter = require('events'); const TerminalCommandExecutor = require('./terminal-command-executor'); diff --git a/spec/terminal-command-executor-spec.coffee b/spec/terminal-command-executor-spec.coffee deleted file mode 100644 index ae43489..0000000 --- a/spec/terminal-command-executor-spec.coffee +++ /dev/null @@ -1,38 +0,0 @@ -TerminalCommandExecutor = require '../lib/terminal-command-executor' -ChildProcess = require 'child_process' -FakeSpawn = require './fake-spawn' - -describe 'TerminalCommandExecutor', -> - [executor, fake, exec, stdOutData, exitCode] = {} - - beforeEach -> - stdOutData = '' - exitCode = -1 - fake = new FakeSpawn - executor = new TerminalCommandExecutor - spyOn(ChildProcess, 'spawn').andReturn(fake) - - it 'can be created', -> - expect(executor).not.toBeNull() - - it 'writes the command and exits if not destination folder is provided', -> - executor.run 'command' - expect(fake.commandsReceived[0]).toBe('command\n') - expect(fake.commandsReceived[1]).toBe('exit\n') - - it 'writes the folder, command and exits if folder is provided', -> - executor.run 'command', 'dir' - expect(fake.commandsReceived[0]).toBe('cd "dir" && command\n') - expect(fake.commandsReceived[1]).toBe('exit\n') - - it 'calls the callback when new data appears in stdout', -> - executor.run 'command' - executor.onDataReceived (data) -> stdOutData = data - fake.stdout.write('some data') - expect(stdOutData).toBe('some data') - - it 'calls the callback when the stream is closed', -> - executor.run 'command' - executor.onDataFinished (code) -> exitCode = code - fake.emulateClose() - expect(exitCode).toBe(0) diff --git a/spec/terminal-command-executor-spec.js b/spec/terminal-command-executor-spec.js new file mode 100644 index 0000000..97d3a5d --- /dev/null +++ b/spec/terminal-command-executor-spec.js @@ -0,0 +1,50 @@ +'use babel'; + +const TerminalCommandExecutor = require('../lib/terminal-command-executor'); +const ChildProcess = require('child_process'); +const FakeSpawn = require('./fake-spawn'); + +describe('TerminalCommandExecutor', () => { + let executor = {}; + let fake = {}; + let stdOutData = {}; + let exitCode = {}; + + beforeEach(() => { + stdOutData = ''; + exitCode = -1; + fake = new FakeSpawn(); + executor = new TerminalCommandExecutor(); + spyOn(ChildProcess, 'spawn').andReturn(fake); + }); + + it('can be created', () => expect(executor).not.toBeNull()); + + it('writes the command and exits if not destination folder is provided', () => { + executor.run('command'); + expect(fake.commandsReceived[0]).toBe('command\n'); + expect(fake.commandsReceived[1]).toBe('exit\n'); + }); + + it('writes the folder, command and exits if folder is provided', () => { + executor.run('command', 'dir'); + expect(fake.commandsReceived[0]).toBe('cd "dir" && command\n'); + expect(fake.commandsReceived[1]).toBe('exit\n'); + }); + + + it('calls the callback when new data appears in stdout', () => { + executor.run('command'); + executor.onDataReceived((data) => stdOutData = data); + fake.stdout.write('some data'); + expect(stdOutData).toBe('some data'); + }); + + + it('calls the callback when the stream is closed', () => { + executor.run('command'); + executor.onDataFinished((code) => exitCode = code); + fake.emulateClose(); + expect(exitCode).toBe(0); + }); +}); From af98d9ad1de3959410f658620073b8c6ad581535 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sat, 12 Mar 2016 18:05:29 +0000 Subject: [PATCH 13/47] TerminalCommandExecutor emits events now with EventEmitter --- lib/terminal-command-executor.js | 40 +++++++++++--------------- lib/test-runner-process.js | 5 ++-- spec/terminal-command-executor-spec.js | 6 ++-- spec/test-runner-process-spec.js | 20 +++++++++++-- 4 files changed, 38 insertions(+), 33 deletions(-) diff --git a/lib/terminal-command-executor.js b/lib/terminal-command-executor.js index a9ee2f2..4734231 100644 --- a/lib/terminal-command-executor.js +++ b/lib/terminal-command-executor.js @@ -1,11 +1,15 @@ 'use babel'; -const { Emitter } = require('event-kit'); +const EventEmitter = require('events'); const ChildProcess = require('child_process'); -class TerminalCommandExecutor { +class TerminalCommandExecutor extends EventEmitter { constructor() { - this.emitter = new Emitter(); + super(); + EventEmitter.call(this); + + this.dataReceivedEventName = 'dataReceived'; + this.dataFinishedEventName = 'dataFinished'; } run(command, destinyFolder = null) { @@ -15,9 +19,9 @@ class TerminalCommandExecutor { let spawn = ChildProcess.spawn; let terminal = spawn('bash', ['-l']); - terminal.on('close', (statusCode) => this.streamClosed(statusCode)); - terminal.stdout.on('data', (data) => this.stdOutDataReceived(data)); - terminal.stderr.on('data', (data) => this.stdErrDataReceived(data)); + terminal.on('close', (statusCode) => this._streamClosed(statusCode)); + terminal.stdout.on('data', (data) => this._stdOutDataReceived(data)); + terminal.stderr.on('data', (data) => this._stdErrDataReceived(data)); let terminalCommand = (this.destinyFolder) ? `cd \"${this.destinyFolder}\" && ${this.command}\n` @@ -29,28 +33,16 @@ class TerminalCommandExecutor { terminal.stdin.write('exit\n'); } - stdOutDataReceived(newData) { - this.emitter.emit('onStdOutData', newData.toString()); - } - - stdErrDataReceived(newData) { - this.emitter.emit('onStdErrData', newData.toString()); - } - - streamClosed(code) { - this.emitter.emit('onFinishData', code); - } - - onDataReceived(callback) { - this.emitter.on('onStdOutData', callback); + _stdOutDataReceived(newData) { + this.emit(this.dataReceivedEventName, newData.toString()); } - onDataFinished(callback) { - this.emitter.on('onFinishData', callback); + _stdErrDataReceived(newData) { + this.emit(this.dataReceivedEventName, newData.toString()); } - destroy() { - this.emitter.dispose(); + _streamClosed(code) { + this.emit(this.dataFinishedEventName, code); } } diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index 1a87dfc..a983258 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -22,11 +22,12 @@ class TestRunnerProcess extends EventEmitter { filePathParser = new FilePathParser) { super(); + this.eventHandlers = {}; this.filePathParser = filePathParser; this.terminalCommandExecutor = executor; - this.terminalCommandExecutor.onDataReceived((data) => this._addAvaOutput(data)); - this.terminalCommandExecutor.onDataFinished(() => this._endAvaOutput()); + this.terminalCommandExecutor.on('dataReceived', (data) => this._addAvaOutput(data)); + this.terminalCommandExecutor.on('dataFinished', () => this._endAvaOutput()); EventEmitter.call(this); } diff --git a/spec/terminal-command-executor-spec.js b/spec/terminal-command-executor-spec.js index 97d3a5d..07865e2 100644 --- a/spec/terminal-command-executor-spec.js +++ b/spec/terminal-command-executor-spec.js @@ -32,18 +32,16 @@ describe('TerminalCommandExecutor', () => { expect(fake.commandsReceived[1]).toBe('exit\n'); }); - it('calls the callback when new data appears in stdout', () => { executor.run('command'); - executor.onDataReceived((data) => stdOutData = data); + executor.on('dataReceived', (data) => stdOutData = data); fake.stdout.write('some data'); expect(stdOutData).toBe('some data'); }); - it('calls the callback when the stream is closed', () => { executor.run('command'); - executor.onDataFinished((code) => exitCode = code); + executor.on('dataFinished', (code) => exitCode = code); fake.emulateClose(); expect(exitCode).toBe(0); }); diff --git a/spec/test-runner-process-spec.js b/spec/test-runner-process-spec.js index e50269b..b3f03c9 100644 --- a/spec/test-runner-process-spec.js +++ b/spec/test-runner-process-spec.js @@ -8,8 +8,22 @@ describe('TestRunnerProcess', () => { let executor = {}; let parser = {}; + class TerminalCommandExecutorDouble extends TerminalCommandExecutor { + constructor() { + super(); + } + + emulateDataWrittenStdOut(data) { + this.emit(this.dataReceivedEventName, data); + } + + emulateDataFinished(statusCode) { + this.emit(this.dataFinishedEventName, statusCode); + } + } + beforeEach(() => { - executor = new TerminalCommandExecutor(); + executor = new TerminalCommandExecutorDouble(); parser = { on(eventName, callBack) {}, @@ -33,14 +47,14 @@ describe('TestRunnerProcess', () => { it('redirects the output for the parser when is received', () => { spyOn(parser, 'write'); runner.run('/somefolder/filename'); - executor.stdOutDataReceived('newdata'); + executor.emulateDataWrittenStdOut('newdata'); expect(parser.write).toHaveBeenCalledWith('newdata'); }); it('closes the parser stream when the output is over', () => { spyOn(parser, 'end'); runner.run('/somefolder/filename'); - executor.streamClosed(0); + executor.emulateDataFinished(0); expect(parser.end).toHaveBeenCalled(); }); }); From 469e2bfeeb262cb05773529422f027056197c972 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sat, 12 Mar 2016 19:04:53 +0000 Subject: [PATCH 14/47] Cancellation mechanism when the user toggles the package --- lib/main.js | 1 + lib/panel.js | 4 +++ lib/terminal-command-executor.js | 17 ++++++++----- lib/test-runner-process.js | 4 +++ spec/fake-spawn.coffee | 31 ----------------------- spec/fake-spawn.js | 34 ++++++++++++++++++++++++++ spec/terminal-command-executor-spec.js | 7 ++++++ spec/test-runner-process-spec.js | 6 +++++ 8 files changed, 67 insertions(+), 37 deletions(-) delete mode 100644 spec/fake-spawn.coffee create mode 100644 spec/fake-spawn.js diff --git a/lib/main.js b/lib/main.js index 17ce8c6..de7fd1c 100644 --- a/lib/main.js +++ b/lib/main.js @@ -24,6 +24,7 @@ module.exports = { }, toggle() { if (this.atomPanel.isVisible()) { + this.panel.cancelExecution(); this.atomPanel.hide(); } else { diff --git a/lib/panel.js b/lib/panel.js index 485d11d..810277d 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -35,6 +35,10 @@ class Panel { this.testRunnerProcess.run(currentFileName); } + cancelExecution() { + this.testRunnerProcess.cancelExecution(); + } + renderAssert(assert) { let newTest = this.createElement('div', 'test'); let status = (assert.ok) ? 'OK' : 'NO'; diff --git a/lib/terminal-command-executor.js b/lib/terminal-command-executor.js index 4734231..3662cd7 100644 --- a/lib/terminal-command-executor.js +++ b/lib/terminal-command-executor.js @@ -18,10 +18,10 @@ class TerminalCommandExecutor extends EventEmitter { let spawn = ChildProcess.spawn; - let terminal = spawn('bash', ['-l']); - terminal.on('close', (statusCode) => this._streamClosed(statusCode)); - terminal.stdout.on('data', (data) => this._stdOutDataReceived(data)); - terminal.stderr.on('data', (data) => this._stdErrDataReceived(data)); + this.terminal = spawn('bash', ['-l']); + this.terminal.on('close', (statusCode) => this._streamClosed(statusCode)); + this.terminal.stdout.on('data', (data) => this._stdOutDataReceived(data)); + this.terminal.stderr.on('data', (data) => this._stdErrDataReceived(data)); let terminalCommand = (this.destinyFolder) ? `cd \"${this.destinyFolder}\" && ${this.command}\n` @@ -29,8 +29,13 @@ class TerminalCommandExecutor extends EventEmitter { console.log('Launching command to terminal: #{terminalCommand}'); - terminal.stdin.write(terminalCommand); - terminal.stdin.write('exit\n'); + this.terminal.stdin.write(terminalCommand); + this.terminal.stdin.write('exit\n'); + } + + cancelExecution() { + if (this.terminal) + this.terminal.kill('SIGKILL'); } _stdOutDataReceived(newData) { diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index a983258..e6b4488 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -42,6 +42,10 @@ class TestRunnerProcess extends EventEmitter { this.terminalCommandExecutor.run(command, filePathParseResult.folder); } + cancelExecution() { + this.terminalCommandExecutor.cancelExecution(); + } + _setHandlersOnParser(parser) { parser.on('assert', (assert) => this.emit('assert', assert)); parser.on('complete', (results) => this.emit('complete', results)); diff --git a/spec/fake-spawn.coffee b/spec/fake-spawn.coffee deleted file mode 100644 index de13b49..0000000 --- a/spec/fake-spawn.coffee +++ /dev/null @@ -1,31 +0,0 @@ -module.exports = - class FakeSpawn - self = [] - constructor: -> - self = @ - @commandsReceived = [] - - on: (event, callback) -> - @mainCallBack = callback - - emulateClose: -> - @mainCallBack(0) - - stdout: { - write: (data) -> - @stdOutCallBack data - on: (event, callback) -> - @stdOutCallBack = callback - } - - stderr: { - write: (data) -> - @stdErrCallBack data - on: (event, callback) -> - @stdErrCallBack = callback - } - - stdin: { - write: (command) => - self.commandsReceived.push command - } diff --git a/spec/fake-spawn.js b/spec/fake-spawn.js new file mode 100644 index 0000000..67b43f0 --- /dev/null +++ b/spec/fake-spawn.js @@ -0,0 +1,34 @@ +'use babel'; + +class FakeSpawn { + constructor() { + this.commandsReceived = []; + self = this; + + this.stdout = { + write: (data) => self.stdOutCallBack(data), + on: (event, callback) => { + self.stdOutCallBack = callback; + } + }; + this.stderr = { + write: (data) => self.stdErrCallBack(data), + on: (event, callback) => self.stdErrCallBack = callback + }; + this.stdin = { + write: (command) => self.commandsReceived.push(command) + }; + } + + kill() { } + + on(event, callback) { + this.mainCallBack = callback; + } + + emulateClose() { + this.mainCallBack(0); + } +} + +module.exports = FakeSpawn diff --git a/spec/terminal-command-executor-spec.js b/spec/terminal-command-executor-spec.js index 07865e2..1928843 100644 --- a/spec/terminal-command-executor-spec.js +++ b/spec/terminal-command-executor-spec.js @@ -16,6 +16,7 @@ describe('TerminalCommandExecutor', () => { fake = new FakeSpawn(); executor = new TerminalCommandExecutor(); spyOn(ChildProcess, 'spawn').andReturn(fake); + spyOn(fake, 'kill'); }); it('can be created', () => expect(executor).not.toBeNull()); @@ -45,4 +46,10 @@ describe('TerminalCommandExecutor', () => { fake.emulateClose(); expect(exitCode).toBe(0); }); + + it('cancels the current execution', () => { + executor.run('command'); + executor.cancelExecution(); + expect(fake.kill).toHaveBeenCalled(); + }); }); diff --git a/spec/test-runner-process-spec.js b/spec/test-runner-process-spec.js index b3f03c9..d18a901 100644 --- a/spec/test-runner-process-spec.js +++ b/spec/test-runner-process-spec.js @@ -57,4 +57,10 @@ describe('TestRunnerProcess', () => { executor.emulateDataFinished(0); expect(parser.end).toHaveBeenCalled(); }); + + it('cancels the current test execution', () => { + spyOn(executor, 'cancelExecution'); + runner.cancelExecution(); + expect(executor.cancelExecution).toHaveBeenCalled(); + }); }); From 054827a7360c6cedc5b55402f1a411dce7833f08 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sat, 12 Mar 2016 19:09:44 +0000 Subject: [PATCH 15/47] event-kit dependency removed --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index b927c92..4b5b613 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "mocha" ], "dependencies": { - "event-kit": "^1.1.0", "tap-parser": "^1.2.2", "ava": "^0.13.0" }, From c9ea899efd08429db5e8f84f4588291656d97849 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sat, 12 Mar 2016 19:46:06 +0000 Subject: [PATCH 16/47] Initial cancellation mechanism --- lib/panel.js | 14 +++++++++----- lib/terminal-command-executor.js | 12 +++++++++--- spec/fake-spawn.js | 4 ++-- spec/terminal-command-executor-spec.js | 8 +++++++- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/lib/panel.js b/lib/panel.js index 810277d..52f7947 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -28,7 +28,7 @@ class Panel { } run() { - this.toggleExecutingIndicator(); + this.displayExecutingIndicator(); this.testsContainer.innerHTML = ''; let editor = atom.workspace.getActiveTextEditor(); let currentFileName = editor.buffer.file.path; @@ -36,6 +36,7 @@ class Panel { } cancelExecution() { + this.hideExecutingIndicator(); this.testRunnerProcess.cancelExecution(); } @@ -47,7 +48,7 @@ class Panel { } renderFinalReport(results) { - this.toggleExecutingIndicator(); + this.hideExecutingIndicator(); let summary = this.createElement('div', 'summary'); let percentage = Math.round((results.pass/results.count)*100); @@ -64,9 +65,12 @@ class Panel { return element; } - toggleExecutingIndicator() { - this.executing.style.display = - (this.executing.style.display === 'block') ? 'none' : 'block'; + displayExecutingIndicator() { + this.executing.style.display = 'block'; + } + + hideExecutingIndicator() { + this.executing.style.display = 'none'; } serialize() { } diff --git a/lib/terminal-command-executor.js b/lib/terminal-command-executor.js index 3662cd7..c778421 100644 --- a/lib/terminal-command-executor.js +++ b/lib/terminal-command-executor.js @@ -13,6 +13,8 @@ class TerminalCommandExecutor extends EventEmitter { } run(command, destinyFolder = null) { + this.cancelExecution(); + this.command = command; this.destinyFolder = destinyFolder; @@ -27,15 +29,17 @@ class TerminalCommandExecutor extends EventEmitter { ? `cd \"${this.destinyFolder}\" && ${this.command}\n` : `${this.command}\n`; - console.log('Launching command to terminal: #{terminalCommand}'); + console.log(`Launching command to terminal: ${terminalCommand}`); this.terminal.stdin.write(terminalCommand); this.terminal.stdin.write('exit\n'); } cancelExecution() { - if (this.terminal) + if (this.terminal) { this.terminal.kill('SIGKILL'); + this.terminal = null; + } } _stdOutDataReceived(newData) { @@ -47,7 +51,9 @@ class TerminalCommandExecutor extends EventEmitter { } _streamClosed(code) { - this.emit(this.dataFinishedEventName, code); + if (code === 1) { + this.emit(this.dataFinishedEventName, code); + } } } diff --git a/spec/fake-spawn.js b/spec/fake-spawn.js index 67b43f0..603bce8 100644 --- a/spec/fake-spawn.js +++ b/spec/fake-spawn.js @@ -27,8 +27,8 @@ class FakeSpawn { } emulateClose() { - this.mainCallBack(0); + this.mainCallBack(1); } } -module.exports = FakeSpawn +module.exports = FakeSpawn; diff --git a/spec/terminal-command-executor-spec.js b/spec/terminal-command-executor-spec.js index 1928843..fff5fa0 100644 --- a/spec/terminal-command-executor-spec.js +++ b/spec/terminal-command-executor-spec.js @@ -44,7 +44,7 @@ describe('TerminalCommandExecutor', () => { executor.run('command'); executor.on('dataFinished', (code) => exitCode = code); fake.emulateClose(); - expect(exitCode).toBe(0); + expect(exitCode).toBe(1); }); it('cancels the current execution', () => { @@ -52,4 +52,10 @@ describe('TerminalCommandExecutor', () => { executor.cancelExecution(); expect(fake.kill).toHaveBeenCalled(); }); + + it('cancels the current execution if launched several times', () => { + executor.run('command'); + executor.run('command'); + expect(fake.kill).toHaveBeenCalled(); + }); }); From 39559506050f4dd9e8980f6dd989720b7849e3c2 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sat, 12 Mar 2016 19:57:02 +0000 Subject: [PATCH 17/47] Toggling the plugin launches the test runner process --- keymaps/ava.cson | 1 - lib/main.js | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/keymaps/ava.cson b/keymaps/ava.cson index 6742a6a..ff01f26 100644 --- a/keymaps/ava.cson +++ b/keymaps/ava.cson @@ -1,3 +1,2 @@ 'atom-workspace': 'ctrl-alt-a': 'ava:toggle' - 'ctrl-alt-x': 'ava:run' diff --git a/lib/main.js b/lib/main.js index de7fd1c..a32d8ce 100644 --- a/lib/main.js +++ b/lib/main.js @@ -29,6 +29,7 @@ module.exports = { } else { this.atomPanel.show(); + this.panel.run(); } } }; From 2f5319d7a1ceea501ea6f3e5bf460eab68fea0a4 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sat, 12 Mar 2016 20:58:09 +0000 Subject: [PATCH 18/47] Addressing issues marked by xo --- lib/main.js | 53 +++++---- lib/panel.js | 150 ++++++++++++------------- lib/terminal-command-executor.js | 100 ++++++++--------- lib/test-runner-process.js | 114 +++++++++---------- package.json | 1 + spec/fake-spawn.js | 50 +++++---- spec/main-spec.js | 62 +++++----- spec/terminal-command-executor-spec.js | 110 +++++++++--------- spec/test-runner-process-spec.js | 114 +++++++++---------- 9 files changed, 377 insertions(+), 377 deletions(-) diff --git a/lib/main.js b/lib/main.js index a32d8ce..b628d4c 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,35 +1,34 @@ 'use babel'; const Panel = require('./panel'); -const { CompositeDisposable } = require('atom'); +const {CompositeDisposable} = require('atom'); module.exports = { - activate(state) { - this.panel = new Panel(state.testingForAvaViewState); - this.atomPanel = atom.workspace.addRightPanel({item: this.panel, visible: false}); - this.subscriptions = new CompositeDisposable(); + activate(state) { + this.panel = new Panel(state.testingForAvaViewState); + this.atomPanel = atom.workspace.addRightPanel({item: this.panel, visible: false}); + this.subscriptions = new CompositeDisposable(); - this.subscriptions.add( - atom.commands.add('atom-workspace', 'ava:toggle', () => this.toggle())); + this.subscriptions.add( + atom.commands.add('atom-workspace', 'ava:toggle', () => this.toggle())); - this.subscriptions.add( - atom.commands.add('atom-workspace', 'ava:run', () => this.panel.run())); - }, - deactivate() { - this.subscriptions.dispose(); - this.panel.destroy(); - }, - serialize() { - this.atomAva = this.panel.serialize(); - }, - toggle() { - if (this.atomPanel.isVisible()) { - this.panel.cancelExecution(); - this.atomPanel.hide(); - } - else { - this.atomPanel.show(); - this.panel.run(); - } - } + this.subscriptions.add( + atom.commands.add('atom-workspace', 'ava:run', () => this.panel.run())); + }, + deactivate() { + this.subscriptions.dispose(); + this.panel.destroy(); + }, + serialize() { + this.atomAva = this.panel.serialize(); + }, + toggle() { + if (this.atomPanel.isVisible()) { + this.panel.cancelExecution(); + this.atomPanel.hide(); + } else { + this.atomPanel.show(); + this.panel.run(); + } + } }; diff --git a/lib/panel.js b/lib/panel.js index 52f7947..fc2f340 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -3,81 +3,81 @@ const TestRunnerProcess = require('./test-runner-process'); class Panel { - constructor(serializedState, testRunnerProcess = new TestRunnerProcess) { - this.testRunnerProcess = testRunnerProcess; - - this.testRunnerProcess.on('assert', (result) => this.renderAssert(result)); - this.testRunnerProcess.on('complete', (results) => this.renderFinalReport(results)); - - this.renderBase(); - } - - renderBase() { - this.element = this.createElement('div', 'ava'); - let message = this.createElement('div', 'message'); - message.textContent = 'AVA test runner'; - this.element.appendChild(message); - - this.executing = this.createElement('div', 'executing'); - this.executing.textContent = 'Loading'; - this.executing.style.display = 'none'; - this.element.appendChild(this.executing); - - this.testsContainer = this.createElement('div', 'test-container'); - this.element.appendChild(this.testsContainer); - } - - run() { - this.displayExecutingIndicator(); - this.testsContainer.innerHTML = ''; - let editor = atom.workspace.getActiveTextEditor(); - let currentFileName = editor.buffer.file.path; - this.testRunnerProcess.run(currentFileName); - } - - cancelExecution() { - this.hideExecutingIndicator(); - this.testRunnerProcess.cancelExecution(); - } - - renderAssert(assert) { - let newTest = this.createElement('div', 'test'); - let status = (assert.ok) ? 'OK' : 'NO'; - newTest.textContent = `${status} - ${assert.name}`; - this.testsContainer.appendChild(newTest); - } - - renderFinalReport(results) { - this.hideExecutingIndicator(); - - let summary = this.createElement('div', 'summary'); - let percentage = Math.round((results.pass/results.count)*100); - summary.textContent = `${results.count} total - ${percentage}% passed`; - - this.testsContainer.appendChild(summary); - } - - createElement(elementType, cssClass = null) { - let element = document.createElement(elementType); - if (cssClass) { - element.classList.add(cssClass); - } - return element; - } - - displayExecutingIndicator() { - this.executing.style.display = 'block'; - } - - hideExecutingIndicator() { - this.executing.style.display = 'none'; - } - - serialize() { } - - destroy() { - this.element.remove(); - } + constructor(serializedState, testRunnerProcess = new TestRunnerProcess()) { + this.testRunnerProcess = testRunnerProcess; + + this.testRunnerProcess.on('assert', result => this.renderAssert(result)); + this.testRunnerProcess.on('complete', results => this.renderFinalReport(results)); + + this.renderBase(); + } + + renderBase() { + this.element = this.createElement('div', 'ava'); + const message = this.createElement('div', 'message'); + message.textContent = 'AVA test runner'; + this.element.appendChild(message); + + this.executing = this.createElement('div', 'executing'); + this.executing.textContent = 'Loading'; + this.executing.style.display = 'none'; + this.element.appendChild(this.executing); + + this.testsContainer = this.createElement('div', 'test-container'); + this.element.appendChild(this.testsContainer); + } + + run() { + this.displayExecutingIndicator(); + this.testsContainer.innerHTML = ''; + const editor = atom.workspace.getActiveTextEditor(); + const currentFileName = editor.buffer.file.path; + this.testRunnerProcess.run(currentFileName); + } + + cancelExecution() { + this.hideExecutingIndicator(); + this.testRunnerProcess.cancelExecution(); + } + + renderAssert(assert) { + const newTest = this.createElement('div', 'test'); + const status = (assert.ok) ? 'OK' : 'NO'; + newTest.textContent = `${status} - ${assert.name}`; + this.testsContainer.appendChild(newTest); + } + + renderFinalReport(results) { + this.hideExecutingIndicator(); + + const summary = this.createElement('div', 'summary'); + const percentage = Math.round((results.pass / results.count) * 100); + summary.textContent = `${results.count} total - ${percentage}% passed`; + + this.testsContainer.appendChild(summary); + } + + createElement(elementType, cssClass = null) { + const element = document.createElement(elementType); + if (cssClass) { + element.classList.add(cssClass); + } + return element; + } + + displayExecutingIndicator() { + this.executing.style.display = 'block'; + } + + hideExecutingIndicator() { + this.executing.style.display = 'none'; + } + + serialize() { } + + destroy() { + this.element.remove(); + } } module.exports = Panel; diff --git a/lib/terminal-command-executor.js b/lib/terminal-command-executor.js index c778421..274b28d 100644 --- a/lib/terminal-command-executor.js +++ b/lib/terminal-command-executor.js @@ -4,57 +4,55 @@ const EventEmitter = require('events'); const ChildProcess = require('child_process'); class TerminalCommandExecutor extends EventEmitter { - constructor() { - super(); - EventEmitter.call(this); - - this.dataReceivedEventName = 'dataReceived'; - this.dataFinishedEventName = 'dataFinished'; - } - - run(command, destinyFolder = null) { - this.cancelExecution(); - - this.command = command; - this.destinyFolder = destinyFolder; - - let spawn = ChildProcess.spawn; - - this.terminal = spawn('bash', ['-l']); - this.terminal.on('close', (statusCode) => this._streamClosed(statusCode)); - this.terminal.stdout.on('data', (data) => this._stdOutDataReceived(data)); - this.terminal.stderr.on('data', (data) => this._stdErrDataReceived(data)); - - let terminalCommand = (this.destinyFolder) - ? `cd \"${this.destinyFolder}\" && ${this.command}\n` - : `${this.command}\n`; - - console.log(`Launching command to terminal: ${terminalCommand}`); - - this.terminal.stdin.write(terminalCommand); - this.terminal.stdin.write('exit\n'); - } - - cancelExecution() { - if (this.terminal) { - this.terminal.kill('SIGKILL'); - this.terminal = null; - } - } - - _stdOutDataReceived(newData) { - this.emit(this.dataReceivedEventName, newData.toString()); - } - - _stdErrDataReceived(newData) { - this.emit(this.dataReceivedEventName, newData.toString()); - } - - _streamClosed(code) { - if (code === 1) { - this.emit(this.dataFinishedEventName, code); - } - } + constructor() { + super(); + EventEmitter.call(this); + + this.dataReceivedEventName = 'dataReceived'; + this.dataFinishedEventName = 'dataFinished'; + } + + run(command, destinyFolder = null) { + this.cancelExecution(); + + this.command = command; + this.destinyFolder = destinyFolder; + + const spawn = ChildProcess.spawn; + + this.terminal = spawn('bash', ['-l']); + this.terminal.on('close', statusCode => this._streamClosed(statusCode)); + this.terminal.stdout.on('data', data => this._stdOutDataReceived(data)); + this.terminal.stderr.on('data', data => this._stdErrDataReceived(data)); + + const terminalCommand = (this.destinyFolder) ? + `cd \"${this.destinyFolder}\" && ${this.command}\n` : + `${this.command}\n`; + + this.terminal.stdin.write(terminalCommand); + this.terminal.stdin.write('exit\n'); + } + + cancelExecution() { + if (this.terminal) { + this.terminal.kill('SIGKILL'); + this.terminal = null; + } + } + + _stdOutDataReceived(newData) { + this.emit(this.dataReceivedEventName, newData.toString()); + } + + _stdErrDataReceived(newData) { + this.emit(this.dataReceivedEventName, newData.toString()); + } + + _streamClosed(code) { + if (code === 1) { + this.emit(this.dataFinishedEventName, code); + } + } } module.exports = TerminalCommandExecutor; diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index e6b4488..cef0e9a 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -1,71 +1,67 @@ 'use babel'; -const Parser = require('tap-parser'); +const parser = require('tap-parser'); const EventEmitter = require('events'); const TerminalCommandExecutor = require('./terminal-command-executor'); class FilePathParser { - parse(filePath) { - //TODO: Fix parsing of folders and files - let folder = filePath.substring(0, filePath.lastIndexOf('/') + 1); - let file = filePath.split('/').pop(); - return { - folder: folder, - file: file - }; - } + parse(filePath) { + // TODO: Fix parsing of folders and files + const folder = filePath.substring(0, filePath.lastIndexOf('/') + 1); + const file = filePath.split('/').pop(); + return {folder, file}; + } } class TestRunnerProcess extends EventEmitter { - constructor( - executor = new TerminalCommandExecutor, - filePathParser = new FilePathParser) { - - super(); - - this.eventHandlers = {}; - this.filePathParser = filePathParser; - this.terminalCommandExecutor = executor; - this.terminalCommandExecutor.on('dataReceived', (data) => this._addAvaOutput(data)); - this.terminalCommandExecutor.on('dataFinished', () => this._endAvaOutput()); - - EventEmitter.call(this); - } - - run(filePath) { - this.parser = this._getParser(); - this._setHandlersOnParser(this.parser); - - let filePathParseResult = this.filePathParser.parse(filePath); - let command = `ava ${filePathParseResult.file} --tap`; - - this.terminalCommandExecutor.run(command, filePathParseResult.folder); - } - - cancelExecution() { - this.terminalCommandExecutor.cancelExecution(); - } - - _setHandlersOnParser(parser) { - parser.on('assert', (assert) => this.emit('assert', assert)); - parser.on('complete', (results) => this.emit('complete', results)); - } - - _addAvaOutput(data) { - this.parser.write(data); - } - - _endAvaOutput() { - this.parser.end(); - } - - _getParser() { - return Parser(); - } - - destroy() { - this.terminalCommandExecutor.destroy(); - } + constructor( + executor = new TerminalCommandExecutor(), + filePathParser = new FilePathParser()) { + super(); + + this.eventHandlers = {}; + this.filePathParser = filePathParser; + this.terminalCommandExecutor = executor; + this.terminalCommandExecutor.on('dataReceived', data => this._addAvaOutput(data)); + this.terminalCommandExecutor.on('dataFinished', () => this._endAvaOutput()); + + EventEmitter.call(this); + } + + run(filePath) { + this.parser = this._getParser(); + this._setHandlersOnParser(this.parser); + + const filePathParseResult = this.filePathParser.parse(filePath); + const command = `ava ${filePathParseResult.file} --tap`; + + this.terminalCommandExecutor.run(command, filePathParseResult.folder); + } + + cancelExecution() { + this.terminalCommandExecutor.cancelExecution(); + } + + _setHandlersOnParser(parser) { + parser.on('assert', assert => this.emit('assert', assert)); + parser.on('complete', results => this.emit('complete', results)); + } + + _addAvaOutput(data) { + this.parser.write(data); + } + + _endAvaOutput() { + this.parser.end(); + } + + _getParser() { + return parser(); + } + + destroy() { + this.terminalCommandExecutor.destroy(); + } } module.exports = TestRunnerProcess; diff --git a/package.json b/package.json index 4b5b613..5fd1674 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "xo": "*" }, "xo": { + "envs": ["browser", "node", "jasmine", "atomtest"], "esnext": true, "globals": [ "atom" diff --git a/spec/fake-spawn.js b/spec/fake-spawn.js index 603bce8..db674d1 100644 --- a/spec/fake-spawn.js +++ b/spec/fake-spawn.js @@ -1,34 +1,36 @@ 'use babel'; class FakeSpawn { - constructor() { - this.commandsReceived = []; - self = this; + constructor() { + this.commandsReceived = []; + const self = this; - this.stdout = { - write: (data) => self.stdOutCallBack(data), - on: (event, callback) => { - self.stdOutCallBack = callback; - } - }; - this.stderr = { - write: (data) => self.stdErrCallBack(data), - on: (event, callback) => self.stdErrCallBack = callback - }; - this.stdin = { - write: (command) => self.commandsReceived.push(command) - }; - } + this.stdout = { + write: data => self.stdOutCallBack(data), + on: (event, callback) => { + self.stdOutCallBack = callback; + } + }; + this.stderr = { + write: data => self.stdErrCallBack(data), + on: (event, callback) => { + self.stdErrCallBack = callback; + } + }; + this.stdin = { + write: command => self.commandsReceived.push(command) + }; + } - kill() { } + kill() { } - on(event, callback) { - this.mainCallBack = callback; - } + on(event, callback) { + this.mainCallBack = callback; + } - emulateClose() { - this.mainCallBack(1); - } + emulateClose() { + this.mainCallBack(1); + } } module.exports = FakeSpawn; diff --git a/spec/main-spec.js b/spec/main-spec.js index b4b87c9..090bacf 100644 --- a/spec/main-spec.js +++ b/spec/main-spec.js @@ -1,33 +1,37 @@ 'use babel'; describe('TestingForAva', () => { - let packageName = 'ava'; - let mainSelector = '.ava'; - let toggleCommand = 'ava:toggle'; - let workspaceElement = []; - let activationPromise = []; - - beforeEach(() => { - workspaceElement = atom.views.getView(atom.workspace); - activationPromise = atom.packages.activatePackage(packageName); - }); - - describe('when the ava:toggle event is triggered', () => { - it('hides and shows the view', () => { - jasmine.attachToDOM(workspaceElement); - - expect(workspaceElement.querySelector(mainSelector)).not.toExist(); - - atom.commands.dispatch(workspaceElement, toggleCommand); - - waitsForPromise(() => activationPromise); - - runs(() => { - let mainElement = workspaceElement.querySelector(mainSelector); - expect(mainElement).toBeVisible(); - atom.commands.dispatch(workspaceElement, toggleCommand); - expect(mainElement).not.toBeVisible(); - }); - }); - }); + const packageName = 'ava'; + const mainSelector = '.ava'; + const toggleCommand = 'ava:toggle'; + let workspaceElement = []; + let activationPromise = []; + + beforeEach(() => { + workspaceElement = atom.views.getView(atom.workspace); + activationPromise = atom.packages.activatePackage(packageName); + + const editor = {buffer: {file: {path: '/this/is/a/path/file.js'}}}; + + spyOn(atom.workspace, 'getActiveTextEditor').andReturn(editor); + }); + + describe('when the ava:toggle event is triggered', () => { + it('hides and shows the view', () => { + jasmine.attachToDOM(workspaceElement); + + expect(workspaceElement.querySelector(mainSelector)).not.toExist(); + + atom.commands.dispatch(workspaceElement, toggleCommand); + + waitsForPromise(() => activationPromise); + + runs(() => { + const mainElement = workspaceElement.querySelector(mainSelector); + expect(mainElement).toBeVisible(); + atom.commands.dispatch(workspaceElement, toggleCommand); + expect(mainElement).not.toBeVisible(); + }); + }); + }); }); diff --git a/spec/terminal-command-executor-spec.js b/spec/terminal-command-executor-spec.js index fff5fa0..bfb3c3d 100644 --- a/spec/terminal-command-executor-spec.js +++ b/spec/terminal-command-executor-spec.js @@ -5,57 +5,61 @@ const ChildProcess = require('child_process'); const FakeSpawn = require('./fake-spawn'); describe('TerminalCommandExecutor', () => { - let executor = {}; - let fake = {}; - let stdOutData = {}; - let exitCode = {}; - - beforeEach(() => { - stdOutData = ''; - exitCode = -1; - fake = new FakeSpawn(); - executor = new TerminalCommandExecutor(); - spyOn(ChildProcess, 'spawn').andReturn(fake); - spyOn(fake, 'kill'); - }); - - it('can be created', () => expect(executor).not.toBeNull()); - - it('writes the command and exits if not destination folder is provided', () => { - executor.run('command'); - expect(fake.commandsReceived[0]).toBe('command\n'); - expect(fake.commandsReceived[1]).toBe('exit\n'); - }); - - it('writes the folder, command and exits if folder is provided', () => { - executor.run('command', 'dir'); - expect(fake.commandsReceived[0]).toBe('cd "dir" && command\n'); - expect(fake.commandsReceived[1]).toBe('exit\n'); - }); - - it('calls the callback when new data appears in stdout', () => { - executor.run('command'); - executor.on('dataReceived', (data) => stdOutData = data); - fake.stdout.write('some data'); - expect(stdOutData).toBe('some data'); - }); - - it('calls the callback when the stream is closed', () => { - executor.run('command'); - executor.on('dataFinished', (code) => exitCode = code); - fake.emulateClose(); - expect(exitCode).toBe(1); - }); - - it('cancels the current execution', () => { - executor.run('command'); - executor.cancelExecution(); - expect(fake.kill).toHaveBeenCalled(); - }); - - it('cancels the current execution if launched several times', () => { - executor.run('command'); - executor.run('command'); - expect(fake.kill).toHaveBeenCalled(); - }); + let executor = {}; + let fake = {}; + let stdOutData = {}; + let exitCode = {}; + + beforeEach(() => { + stdOutData = ''; + exitCode = -1; + fake = new FakeSpawn(); + executor = new TerminalCommandExecutor(); + spyOn(ChildProcess, 'spawn').andReturn(fake); + spyOn(fake, 'kill'); + }); + + it('can be created', () => expect(executor).not.toBeNull()); + + it('writes the command and exits if not destination folder is provided', () => { + executor.run('command'); + expect(fake.commandsReceived[0]).toBe('command\n'); + expect(fake.commandsReceived[1]).toBe('exit\n'); + }); + + it('writes the folder, command and exits if folder is provided', () => { + executor.run('command', 'dir'); + expect(fake.commandsReceived[0]).toBe('cd "dir" && command\n'); + expect(fake.commandsReceived[1]).toBe('exit\n'); + }); + + it('calls the callback when new data appears in stdout', () => { + executor.run('command'); + executor.on('dataReceived', data => { + stdOutData = data; + }); + fake.stdout.write('some data'); + expect(stdOutData).toBe('some data'); + }); + + it('calls the callback when the stream is closed', () => { + executor.run('command'); + executor.on('dataFinished', code => { + exitCode = code; + }); + fake.emulateClose(); + expect(exitCode).toBe(1); + }); + + it('cancels the current execution', () => { + executor.run('command'); + executor.cancelExecution(); + expect(fake.kill).toHaveBeenCalled(); + }); + + it('cancels the current execution if launched several times', () => { + executor.run('command'); + executor.run('command'); + expect(fake.kill).toHaveBeenCalled(); + }); }); diff --git a/spec/test-runner-process-spec.js b/spec/test-runner-process-spec.js index d18a901..338ee1d 100644 --- a/spec/test-runner-process-spec.js +++ b/spec/test-runner-process-spec.js @@ -4,63 +4,59 @@ const TestRunnerProcess = require('../lib/test-runner-process'); const TerminalCommandExecutor = require('../lib/terminal-command-executor'); describe('TestRunnerProcess', () => { - let runner = {}; - let executor = {}; - let parser = {}; - - class TerminalCommandExecutorDouble extends TerminalCommandExecutor { - constructor() { - super(); - } - - emulateDataWrittenStdOut(data) { - this.emit(this.dataReceivedEventName, data); - } - - emulateDataFinished(statusCode) { - this.emit(this.dataFinishedEventName, statusCode); - } - } - - beforeEach(() => { - executor = new TerminalCommandExecutorDouble(); - - parser = { - on(eventName, callBack) {}, - write() {}, - end() {} - }; - - runner = new TestRunnerProcess(executor); - spyOn(runner, '_getParser').andReturn(parser); - }); - - it('can be created', () => expect(runner).not.toBeNull()); - - it('runs the executor with the appropriate parameters', () => { - spyOn(atom.project, 'getPaths').andReturn(['path']); - spyOn(executor, 'run'); - runner.run('/somefolder/filename'); - expect(executor.run).toHaveBeenCalledWith('ava filename --tap', '/somefolder/'); - }); - - it('redirects the output for the parser when is received', () => { - spyOn(parser, 'write'); - runner.run('/somefolder/filename'); - executor.emulateDataWrittenStdOut('newdata'); - expect(parser.write).toHaveBeenCalledWith('newdata'); - }); - - it('closes the parser stream when the output is over', () => { - spyOn(parser, 'end'); - runner.run('/somefolder/filename'); - executor.emulateDataFinished(0); - expect(parser.end).toHaveBeenCalled(); - }); - - it('cancels the current test execution', () => { - spyOn(executor, 'cancelExecution'); - runner.cancelExecution(); - expect(executor.cancelExecution).toHaveBeenCalled(); - }); + let runner = {}; + let executor = {}; + let parser = {}; + + class TerminalCommandExecutorDouble extends TerminalCommandExecutor { + emulateDataWrittenStdOut(data) { + this.emit(this.dataReceivedEventName, data); + } + + emulateDataFinished(statusCode) { + this.emit(this.dataFinishedEventName, statusCode); + } + } + + beforeEach(() => { + executor = new TerminalCommandExecutorDouble(); + + parser = { + on() {}, + write() {}, + end() {} + }; + + runner = new TestRunnerProcess(executor); + spyOn(runner, '_getParser').andReturn(parser); + }); + + it('can be created', () => expect(runner).not.toBeNull()); + + it('runs the executor with the appropriate parameters', () => { + spyOn(atom.project, 'getPaths').andReturn(['path']); + spyOn(executor, 'run'); + runner.run('/somefolder/filename'); + expect(executor.run).toHaveBeenCalledWith('ava filename --tap', '/somefolder/'); + }); + + it('redirects the output for the parser when is received', () => { + spyOn(parser, 'write'); + runner.run('/somefolder/filename'); + executor.emulateDataWrittenStdOut('newdata'); + expect(parser.write).toHaveBeenCalledWith('newdata'); + }); + + it('closes the parser stream when the output is over', () => { + spyOn(parser, 'end'); + runner.run('/somefolder/filename'); + executor.emulateDataFinished(0); + expect(parser.end).toHaveBeenCalled(); + }); + + it('cancels the current test execution', () => { + spyOn(executor, 'cancelExecution'); + runner.cancelExecution(); + expect(executor.cancelExecution).toHaveBeenCalled(); + }); }); From 86b5ed26a6febb7f72d81c981c5daa925c4d9ddb Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sun, 13 Mar 2016 19:26:55 +0000 Subject: [PATCH 19/47] Files use now /** @babel */ header --- lib/main.js | 2 +- lib/panel.js | 2 +- lib/terminal-command-executor.js | 2 +- lib/test-runner-process.js | 2 +- spec/fake-spawn.js | 2 +- spec/main-spec.js | 2 +- spec/terminal-command-executor-spec.js | 2 +- spec/test-runner-process-spec.js | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/main.js b/lib/main.js index b628d4c..c4f0e4a 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,4 +1,4 @@ -'use babel'; +/** @babel */ const Panel = require('./panel'); const {CompositeDisposable} = require('atom'); diff --git a/lib/panel.js b/lib/panel.js index fc2f340..87243af 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -1,4 +1,4 @@ -'use babel'; +/** @babel */ const TestRunnerProcess = require('./test-runner-process'); diff --git a/lib/terminal-command-executor.js b/lib/terminal-command-executor.js index 274b28d..32e0576 100644 --- a/lib/terminal-command-executor.js +++ b/lib/terminal-command-executor.js @@ -1,4 +1,4 @@ -'use babel'; +/** @babel */ const EventEmitter = require('events'); const ChildProcess = require('child_process'); diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index cef0e9a..479625a 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -1,4 +1,4 @@ -'use babel'; +/** @babel */ const parser = require('tap-parser'); const EventEmitter = require('events'); diff --git a/spec/fake-spawn.js b/spec/fake-spawn.js index db674d1..d70d79d 100644 --- a/spec/fake-spawn.js +++ b/spec/fake-spawn.js @@ -1,4 +1,4 @@ -'use babel'; +/** @babel */ class FakeSpawn { constructor() { diff --git a/spec/main-spec.js b/spec/main-spec.js index 090bacf..1cbe7b7 100644 --- a/spec/main-spec.js +++ b/spec/main-spec.js @@ -1,4 +1,4 @@ -'use babel'; +/** @babel */ describe('TestingForAva', () => { const packageName = 'ava'; diff --git a/spec/terminal-command-executor-spec.js b/spec/terminal-command-executor-spec.js index bfb3c3d..0fdaffe 100644 --- a/spec/terminal-command-executor-spec.js +++ b/spec/terminal-command-executor-spec.js @@ -1,4 +1,4 @@ -'use babel'; +/** @babel */ const TerminalCommandExecutor = require('../lib/terminal-command-executor'); const ChildProcess = require('child_process'); diff --git a/spec/test-runner-process-spec.js b/spec/test-runner-process-spec.js index 338ee1d..5c81023 100644 --- a/spec/test-runner-process-spec.js +++ b/spec/test-runner-process-spec.js @@ -1,4 +1,4 @@ -'use babel'; +/** @babel */ const TestRunnerProcess = require('../lib/test-runner-process'); const TerminalCommandExecutor = require('../lib/terminal-command-executor'); From f71d3ab657baf00647a8ff60b6c8aa2c08197d22 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sun, 13 Mar 2016 19:27:53 +0000 Subject: [PATCH 20/47] Items separated by lines in the environments for xo --- package.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 5fd1674..9a2f0c0 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,12 @@ "xo": "*" }, "xo": { - "envs": ["browser", "node", "jasmine", "atomtest"], + "envs": [ + "browser", + "node", + "jasmine", + "atomtest" + ], "esnext": true, "globals": [ "atom" From f35825a91addd376f3c48583b994f123fef8ee3f Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sun, 13 Mar 2016 19:33:38 +0000 Subject: [PATCH 21/47] Keymap uses now JSON --- keymaps/ava.cson | 2 -- keymaps/ava.json | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) delete mode 100644 keymaps/ava.cson create mode 100644 keymaps/ava.json diff --git a/keymaps/ava.cson b/keymaps/ava.cson deleted file mode 100644 index ff01f26..0000000 --- a/keymaps/ava.cson +++ /dev/null @@ -1,2 +0,0 @@ -'atom-workspace': - 'ctrl-alt-a': 'ava:toggle' diff --git a/keymaps/ava.json b/keymaps/ava.json new file mode 100644 index 0000000..2abe7b3 --- /dev/null +++ b/keymaps/ava.json @@ -0,0 +1,5 @@ +{ + "atom-workspace": { + "ctrl-alt-a": "ava:toggle" + } +} From f8675edd0eb643e6f7960d8a5d99ebf03dec3c97 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sun, 13 Mar 2016 19:47:51 +0000 Subject: [PATCH 22/47] Using new ES2015 import syntax --- lib/main.js | 4 ++-- lib/panel.js | 2 +- lib/terminal-command-executor.js | 4 ++-- lib/test-runner-process.js | 6 +++--- spec/terminal-command-executor-spec.js | 6 +++--- spec/test-runner-process-spec.js | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/main.js b/lib/main.js index c4f0e4a..beaefb1 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,7 +1,7 @@ /** @babel */ -const Panel = require('./panel'); -const {CompositeDisposable} = require('atom'); +import Panel from './panel.js'; +import {CompositeDisposable} from 'atom'; module.exports = { activate(state) { diff --git a/lib/panel.js b/lib/panel.js index 87243af..9370eae 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -1,6 +1,6 @@ /** @babel */ -const TestRunnerProcess = require('./test-runner-process'); +import TestRunnerProcess from './test-runner-process'; class Panel { constructor(serializedState, testRunnerProcess = new TestRunnerProcess()) { diff --git a/lib/terminal-command-executor.js b/lib/terminal-command-executor.js index 32e0576..af7f7a8 100644 --- a/lib/terminal-command-executor.js +++ b/lib/terminal-command-executor.js @@ -1,7 +1,7 @@ /** @babel */ -const EventEmitter = require('events'); -const ChildProcess = require('child_process'); +import EventEmitter from 'events'; +import ChildProcess from 'child_process'; class TerminalCommandExecutor extends EventEmitter { constructor() { diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index 479625a..49ffb41 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -1,8 +1,8 @@ /** @babel */ -const parser = require('tap-parser'); -const EventEmitter = require('events'); -const TerminalCommandExecutor = require('./terminal-command-executor'); +import parser from 'tap-parser'; +import EventEmitter from 'events'; +import TerminalCommandExecutor from './terminal-command-executor'; class FilePathParser { parse(filePath) { diff --git a/spec/terminal-command-executor-spec.js b/spec/terminal-command-executor-spec.js index 0fdaffe..182f6fd 100644 --- a/spec/terminal-command-executor-spec.js +++ b/spec/terminal-command-executor-spec.js @@ -1,8 +1,8 @@ /** @babel */ -const TerminalCommandExecutor = require('../lib/terminal-command-executor'); -const ChildProcess = require('child_process'); -const FakeSpawn = require('./fake-spawn'); +import TerminalCommandExecutor from '../lib/terminal-command-executor'; +import ChildProcess from 'child_process'; +import FakeSpawn from './fake-spawn'; describe('TerminalCommandExecutor', () => { let executor = {}; diff --git a/spec/test-runner-process-spec.js b/spec/test-runner-process-spec.js index 5c81023..8a470e7 100644 --- a/spec/test-runner-process-spec.js +++ b/spec/test-runner-process-spec.js @@ -1,7 +1,7 @@ /** @babel */ -const TestRunnerProcess = require('../lib/test-runner-process'); -const TerminalCommandExecutor = require('../lib/terminal-command-executor'); +import TestRunnerProcess from '../lib/test-runner-process'; +import TerminalCommandExecutor from '../lib/terminal-command-executor'; describe('TestRunnerProcess', () => { let runner = {}; From 33d8d6ead6f68fb29596585a51f86928fb88c859 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sun, 13 Mar 2016 19:52:46 +0000 Subject: [PATCH 23/47] Fixing double quotes in ava.less --- styles/ava.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/ava.less b/styles/ava.less index ad363d0..a961e22 100644 --- a/styles/ava.less +++ b/styles/ava.less @@ -1,4 +1,4 @@ -@import "ui-variables"; +@import 'ui-variables'; .ava { padding: 30px; From 90b97adb4437275b7b4f7ef1b516910cadd7840e Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sun, 13 Mar 2016 19:53:15 +0000 Subject: [PATCH 24/47] Changing from spaces to tabs --- styles/ava.less | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/styles/ava.less b/styles/ava.less index a961e22..af7bc45 100644 --- a/styles/ava.less +++ b/styles/ava.less @@ -1,11 +1,11 @@ @import 'ui-variables'; .ava { - padding: 30px; - font-size: 15px; + padding: 30px; + font-size: 15px; - .message { padding-bottom: 10px; } - .summary { font-size: 12px; padding-top: 10px; } - .test { font-size: 11px; font-weight: bold; } - .executing { font-size: 12px; font-weight: bold; padding-bottom: 10px;} + .message { padding-bottom: 10px; } + .summary { font-size: 12px; padding-top: 10px; } + .test { font-size: 11px; font-weight: bold; } + .executing { font-size: 12px; font-weight: bold; padding-bottom: 10px;} } From ec2f444753108d531257544d65c67b0aa05c695e Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Mon, 11 Apr 2016 21:43:23 +0100 Subject: [PATCH 25/47] Removing cancelling process --- lib/main.js | 1 - lib/panel.js | 5 ----- lib/terminal-command-executor.js | 11 +---------- lib/test-runner-process.js | 4 ---- spec/terminal-command-executor-spec.js | 12 ------------ spec/test-runner-process-spec.js | 6 ------ 6 files changed, 1 insertion(+), 38 deletions(-) diff --git a/lib/main.js b/lib/main.js index beaefb1..b21f73e 100644 --- a/lib/main.js +++ b/lib/main.js @@ -24,7 +24,6 @@ module.exports = { }, toggle() { if (this.atomPanel.isVisible()) { - this.panel.cancelExecution(); this.atomPanel.hide(); } else { this.atomPanel.show(); diff --git a/lib/panel.js b/lib/panel.js index 9370eae..329d878 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -35,11 +35,6 @@ class Panel { this.testRunnerProcess.run(currentFileName); } - cancelExecution() { - this.hideExecutingIndicator(); - this.testRunnerProcess.cancelExecution(); - } - renderAssert(assert) { const newTest = this.createElement('div', 'test'); const status = (assert.ok) ? 'OK' : 'NO'; diff --git a/lib/terminal-command-executor.js b/lib/terminal-command-executor.js index af7f7a8..c9dba3c 100644 --- a/lib/terminal-command-executor.js +++ b/lib/terminal-command-executor.js @@ -13,8 +13,6 @@ class TerminalCommandExecutor extends EventEmitter { } run(command, destinyFolder = null) { - this.cancelExecution(); - this.command = command; this.destinyFolder = destinyFolder; @@ -32,14 +30,7 @@ class TerminalCommandExecutor extends EventEmitter { this.terminal.stdin.write(terminalCommand); this.terminal.stdin.write('exit\n'); } - - cancelExecution() { - if (this.terminal) { - this.terminal.kill('SIGKILL'); - this.terminal = null; - } - } - + _stdOutDataReceived(newData) { this.emit(this.dataReceivedEventName, newData.toString()); } diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index 49ffb41..7e041b8 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -38,10 +38,6 @@ class TestRunnerProcess extends EventEmitter { this.terminalCommandExecutor.run(command, filePathParseResult.folder); } - cancelExecution() { - this.terminalCommandExecutor.cancelExecution(); - } - _setHandlersOnParser(parser) { parser.on('assert', assert => this.emit('assert', assert)); parser.on('complete', results => this.emit('complete', results)); diff --git a/spec/terminal-command-executor-spec.js b/spec/terminal-command-executor-spec.js index 182f6fd..989e330 100644 --- a/spec/terminal-command-executor-spec.js +++ b/spec/terminal-command-executor-spec.js @@ -50,16 +50,4 @@ describe('TerminalCommandExecutor', () => { fake.emulateClose(); expect(exitCode).toBe(1); }); - - it('cancels the current execution', () => { - executor.run('command'); - executor.cancelExecution(); - expect(fake.kill).toHaveBeenCalled(); - }); - - it('cancels the current execution if launched several times', () => { - executor.run('command'); - executor.run('command'); - expect(fake.kill).toHaveBeenCalled(); - }); }); diff --git a/spec/test-runner-process-spec.js b/spec/test-runner-process-spec.js index 8a470e7..b2a3d6e 100644 --- a/spec/test-runner-process-spec.js +++ b/spec/test-runner-process-spec.js @@ -53,10 +53,4 @@ describe('TestRunnerProcess', () => { executor.emulateDataFinished(0); expect(parser.end).toHaveBeenCalled(); }); - - it('cancels the current test execution', () => { - spyOn(executor, 'cancelExecution'); - runner.cancelExecution(); - expect(executor.cancelExecution).toHaveBeenCalled(); - }); }); From b4868b24ac3619ec16fbc038519d1625a025c64e Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Mon, 11 Apr 2016 21:46:57 +0100 Subject: [PATCH 26/47] Fixing trailing spaces error --- lib/terminal-command-executor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/terminal-command-executor.js b/lib/terminal-command-executor.js index c9dba3c..459b302 100644 --- a/lib/terminal-command-executor.js +++ b/lib/terminal-command-executor.js @@ -30,7 +30,7 @@ class TerminalCommandExecutor extends EventEmitter { this.terminal.stdin.write(terminalCommand); this.terminal.stdin.write('exit\n'); } - + _stdOutDataReceived(newData) { this.emit(this.dataReceivedEventName, newData.toString()); } From e6a33853fa10b05bba3984d3f8c8317f0a8a74f0 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Mon, 11 Apr 2016 22:01:11 +0100 Subject: [PATCH 27/47] Trying to fix errors due to outdated xo version --- lib/main.js | 2 +- lib/test-runner-process.js | 2 +- spec/terminal-command-executor-spec.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/main.js b/lib/main.js index b21f73e..d975556 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,7 +1,7 @@ /** @babel */ -import Panel from './panel.js'; import {CompositeDisposable} from 'atom'; +import Panel from './panel.js'; module.exports = { activate(state) { diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index 7e041b8..c722a56 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -1,7 +1,7 @@ /** @babel */ -import parser from 'tap-parser'; import EventEmitter from 'events'; +import parser from 'tap-parser'; import TerminalCommandExecutor from './terminal-command-executor'; class FilePathParser { diff --git a/spec/terminal-command-executor-spec.js b/spec/terminal-command-executor-spec.js index 989e330..213b975 100644 --- a/spec/terminal-command-executor-spec.js +++ b/spec/terminal-command-executor-spec.js @@ -1,8 +1,8 @@ /** @babel */ -import TerminalCommandExecutor from '../lib/terminal-command-executor'; import ChildProcess from 'child_process'; import FakeSpawn from './fake-spawn'; +import TerminalCommandExecutor from '../lib/terminal-command-executor'; describe('TerminalCommandExecutor', () => { let executor = {}; From b9a70696275e8af357eba448931a9d5f2502672e Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Mon, 11 Apr 2016 23:23:47 +0100 Subject: [PATCH 28/47] Initial implementation of current execution check before running tests --- lib/panel.js | 4 ++++ lib/test-runner-process.js | 12 ++++++++++++ spec/terminal-command-executor-spec.js | 2 +- spec/test-runner-process-spec.js | 16 ++++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/lib/panel.js b/lib/panel.js index 329d878..30f9d72 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -28,6 +28,10 @@ class Panel { } run() { + if (!this.testRunnerProcess.canRun()) { + return; + } + this.displayExecutingIndicator(); this.testsContainer.innerHTML = ''; const editor = atom.workspace.getActiveTextEditor(); diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index c722a56..95d0e10 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -28,7 +28,17 @@ class TestRunnerProcess extends EventEmitter { EventEmitter.call(this); } + canRun() { + return !this.isRunning; + } + run(filePath) { + if (!this.canRun()) { + return; + } + + this.isRunning = true; + this.parser = this._getParser(); this._setHandlersOnParser(this.parser); @@ -49,6 +59,7 @@ class TestRunnerProcess extends EventEmitter { _endAvaOutput() { this.parser.end(); + this.isRunning = false; } _getParser() { @@ -56,6 +67,7 @@ class TestRunnerProcess extends EventEmitter { } destroy() { + this.isRunning = false; this.terminalCommandExecutor.destroy(); } } diff --git a/spec/terminal-command-executor-spec.js b/spec/terminal-command-executor-spec.js index 213b975..fe5e5fb 100644 --- a/spec/terminal-command-executor-spec.js +++ b/spec/terminal-command-executor-spec.js @@ -1,8 +1,8 @@ /** @babel */ import ChildProcess from 'child_process'; -import FakeSpawn from './fake-spawn'; import TerminalCommandExecutor from '../lib/terminal-command-executor'; +import FakeSpawn from './fake-spawn'; describe('TerminalCommandExecutor', () => { let executor = {}; diff --git a/spec/test-runner-process-spec.js b/spec/test-runner-process-spec.js index b2a3d6e..4d3d724 100644 --- a/spec/test-runner-process-spec.js +++ b/spec/test-runner-process-spec.js @@ -53,4 +53,20 @@ describe('TestRunnerProcess', () => { executor.emulateDataFinished(0); expect(parser.end).toHaveBeenCalled(); }); + + it('prevents multiple executions', () => { + spyOn(executor, 'run'); + runner.run('/somefolder/filename'); + runner.run('/somefolder/filename'); + expect(executor.run.callCount).toBe(1); + }); + + it('informs about the state of the execution', () => { + spyOn(executor, 'run'); + expect(runner.canRun()).toBe(true); + runner.run('/somefolder/filename'); + expect(runner.canRun()).toBe(false); + executor.emulateDataFinished(0); + expect(runner.canRun()).toBe(true); + }); }); From e7fe05764aec8c34a9d3f415a304d1cdc406c3c2 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sat, 23 Apr 2016 10:39:05 +0100 Subject: [PATCH 29/47] First step towards new rendering infrastructure --- lib/panel.js | 58 +++---------------- lib/test-runner-process.js | 23 +++++++- lib/ui-renderer.js | 98 ++++++++++++++++++++++++++++++++ spec/test-runner-process-spec.js | 40 ++++++++++--- styles/ava.less | 61 +++++++++++++++++++- 5 files changed, 220 insertions(+), 60 deletions(-) create mode 100644 lib/ui-renderer.js diff --git a/lib/panel.js b/lib/panel.js index 30f9d72..d951e3a 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -1,30 +1,23 @@ /** @babel */ import TestRunnerProcess from './test-runner-process'; +import UiRenderer from './ui-renderer'; class Panel { constructor(serializedState, testRunnerProcess = new TestRunnerProcess()) { this.testRunnerProcess = testRunnerProcess; - this.testRunnerProcess.on('assert', result => this.renderAssert(result)); - this.testRunnerProcess.on('complete', results => this.renderFinalReport(results)); + this.testRunnerProcess.on('assert', result => this.renderer.renderAssert(result)); + this.testRunnerProcess.on('complete', results => this.renderer.renderFinalReport(results)); this.renderBase(); } renderBase() { - this.element = this.createElement('div', 'ava'); - const message = this.createElement('div', 'message'); - message.textContent = 'AVA test runner'; - this.element.appendChild(message); - - this.executing = this.createElement('div', 'executing'); - this.executing.textContent = 'Loading'; - this.executing.style.display = 'none'; - this.element.appendChild(this.executing); - - this.testsContainer = this.createElement('div', 'test-container'); - this.element.appendChild(this.testsContainer); + this.renderer = new UiRenderer(); + this.element = document.createElement('div'); + this.element.classList.add('ava'); + this.renderer.renderBase(this.element); } run() { @@ -32,46 +25,13 @@ class Panel { return; } - this.displayExecutingIndicator(); - this.testsContainer.innerHTML = ''; + this.renderer.displayExecutingIndicator(); + this.renderer.cleanTestsContainer(); const editor = atom.workspace.getActiveTextEditor(); const currentFileName = editor.buffer.file.path; this.testRunnerProcess.run(currentFileName); } - renderAssert(assert) { - const newTest = this.createElement('div', 'test'); - const status = (assert.ok) ? 'OK' : 'NO'; - newTest.textContent = `${status} - ${assert.name}`; - this.testsContainer.appendChild(newTest); - } - - renderFinalReport(results) { - this.hideExecutingIndicator(); - - const summary = this.createElement('div', 'summary'); - const percentage = Math.round((results.pass / results.count) * 100); - summary.textContent = `${results.count} total - ${percentage}% passed`; - - this.testsContainer.appendChild(summary); - } - - createElement(elementType, cssClass = null) { - const element = document.createElement(elementType); - if (cssClass) { - element.classList.add(cssClass); - } - return element; - } - - displayExecutingIndicator() { - this.executing.style.display = 'block'; - } - - hideExecutingIndicator() { - this.executing.style.display = 'none'; - } - serialize() { } destroy() { diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index 95d0e10..edd6692 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -15,8 +15,8 @@ class FilePathParser { class TestRunnerProcess extends EventEmitter { constructor( - executor = new TerminalCommandExecutor(), - filePathParser = new FilePathParser()) { + executor = new TerminalCommandExecutor(), + filePathParser = new FilePathParser()) { super(); this.eventHandlers = {}; @@ -38,6 +38,7 @@ class TestRunnerProcess extends EventEmitter { } this.isRunning = true; + this.currentExecution = {passed: 0, failed: 0}; this.parser = this._getParser(); this._setHandlersOnParser(this.parser); @@ -49,10 +50,26 @@ class TestRunnerProcess extends EventEmitter { } _setHandlersOnParser(parser) { - parser.on('assert', assert => this.emit('assert', assert)); + const instance = this; + parser.on('assert', assert => { + instance._updateCurrentExecution(assert); + const result = { + currentExecution: this.currentExecution, assert + }; + instance.emit('assert', result); + }); + parser.on('complete', results => this.emit('complete', results)); } + _updateCurrentExecution(assert) { + if (assert.ok) { + this.currentExecution.passed++; + } else { + this.currentExecution.failed++; + } + } + _addAvaOutput(data) { this.parser.write(data); } diff --git a/lib/ui-renderer.js b/lib/ui-renderer.js new file mode 100644 index 0000000..19d389e --- /dev/null +++ b/lib/ui-renderer.js @@ -0,0 +1,98 @@ +/** @babel */ + +class UiRenderer { + renderBase(container) { + this.element = container; + + const fileName = this._createContainer('file-header'); + fileName.textContent = 'filename.js'; + this.element.appendChild(fileName); + + const avaLogoContainer = this._createContainer('ava-logo-container'); + const avaLogo = document.createElement('img'); + avaLogo.src = 'https://raw.githubusercontent.com/sindresorhus/ava/master/media/logo.png'; + avaLogo.classList.add('ava-logo-image'); + avaLogoContainer.appendChild(avaLogo); + this.element.appendChild(avaLogoContainer); + + this.testsStatistics = this._createContainer('test-statistics'); + this._createTestStatisticsSection(this.testsStatistics, 'passed'); + this._createTestStatisticsSection(this.testsStatistics, 'failed'); + this.element.appendChild(this.testsStatistics); + + this.executing = this._createContainer('executing'); + this.executing.textContent = 'Loading'; + this.executing.style.display = 'none'; + this.element.appendChild(this.executing); + + this.testsContainer = this._createContainer('test-container'); + this.element.appendChild(this.testsContainer); + } + + renderAssert(assertResult) { + const assert = assertResult.assert; + + const newTest = this._createContainer('test'); + newTest.classList.add((assert.ok) ? 'ok' : 'ko'); + newTest.textContent = `${assert.name}`; + this.testsContainer.appendChild(newTest); + + // TODO: Change assert to something as AssertResult + // TODO: Rethink the concept + this._updateTestStatisticSection(assertResult); + } + + renderFinalReport(results) { + this.hideExecutingIndicator(); + + const summary = this._createContainer('div', 'summary'); + const percentage = Math.round((results.pass / results.count) * 100); + summary.textContent = `${results.count} total - ${percentage}% passed`; + + this.testsContainer.appendChild(summary); + } + + cleanTestsContainer() { + this.testsContainer.innerHTML = ''; + } + + // TODO: The class name should be extracted to a constant + // TODO: Maybe this generalization shouldn't be done a the container + // TODO: Should be a member, or have some class to deal with it. + _updateTestStatisticSection(assertResult) { + const passedContainer = document.getElementById('passed'); + const failedContainer = document.getElementById('failed'); + passedContainer.textContent = assertResult.currentExecution.passed; + failedContainer.textContent = assertResult.currentExecution.failed; + } + + _createTestStatisticsSection(parentContainer, containerName) { + const container = this._createContainer(`test-statistics-${containerName}-container`); + const number = this._createContainer('number'); + const text = this._createContainer('text'); + number.setAttribute('id', containerName); + number.textContent = '-'; + text.textContent = containerName.toUpperCase(); + container.appendChild(number); + container.appendChild(text); + parentContainer.appendChild(container); + } + + _createContainer(cssClass = null) { + const element = document.createElement('div'); + if (cssClass) { + element.classList.add(cssClass); + } + return element; + } + + displayExecutingIndicator() { + this.executing.style.display = 'block'; + } + + hideExecutingIndicator() { + this.executing.style.display = 'none'; + } +} + +module.exports = UiRenderer; diff --git a/spec/test-runner-process-spec.js b/spec/test-runner-process-spec.js index 4d3d724..7086a61 100644 --- a/spec/test-runner-process-spec.js +++ b/spec/test-runner-process-spec.js @@ -1,8 +1,23 @@ /** @babel */ +import EventEmitter from 'events'; import TestRunnerProcess from '../lib/test-runner-process'; import TerminalCommandExecutor from '../lib/terminal-command-executor'; +class FakeParser extends EventEmitter { + constructor() { + super(); + EventEmitter.call(this); + } + + emitAssert(assert) { + this.emit('assert', assert); + } + + write() { } + end() { } +} + describe('TestRunnerProcess', () => { let runner = {}; let executor = {}; @@ -19,14 +34,8 @@ describe('TestRunnerProcess', () => { } beforeEach(() => { + parser = new FakeParser(); executor = new TerminalCommandExecutorDouble(); - - parser = { - on() {}, - write() {}, - end() {} - }; - runner = new TestRunnerProcess(executor); spyOn(runner, '_getParser').andReturn(parser); }); @@ -69,4 +78,21 @@ describe('TestRunnerProcess', () => { executor.emulateDataFinished(0); expect(runner.canRun()).toBe(true); }); + + it('emits assertion with the correct format', () => { + const receivedAssertResults = []; + const okAssertResult = {ok: true}; + const notOkAssertResult = {ok: false}; + + runner.run('/somefolder/filename'); + runner.on('assert', result => receivedAssertResults.push(result)); + + parser.emitAssert(okAssertResult); + parser.emitAssert(notOkAssertResult); + + expect(receivedAssertResults[0].assert).toBe(okAssertResult); + expect(receivedAssertResults[0].currentExecution.passed).toBe(1); + expect(receivedAssertResults[1].assert).toBe(notOkAssertResult); + expect(receivedAssertResults[1].currentExecution.passed).toBe(1); + }); }); diff --git a/styles/ava.less b/styles/ava.less index af7bc45..190adaf 100644 --- a/styles/ava.less +++ b/styles/ava.less @@ -1,11 +1,70 @@ @import 'ui-variables'; +@horizontal-dividers-color: #DDD; + .ava { + width: 350px; padding: 30px; font-size: 15px; + .file-header { + border-top: 1px solid @horizontal-dividers-color; + border-bottom: 1px solid @horizontal-dividers-color; + padding: 5px; + } + + .ava-logo-container { + text-align: center; + .ava-logo-image { + width: 110px; + } + } + + .test-statistics { + display: table; + margin: 0 auto; + text-align: center; + padding-bottom: 25px; + + .number { + font-family: "Helvetica Neue", Helvetica; + font-weight: lighter; + font-size: 42px; + text-align: center; + } + .text { + font-size: 13px; + letter-spacing: -0.5px; + text-align: center; + margin-top: -10px; + } + + .test-statistics-passed-container { + float:left; + padding-right: 20px; + } + .test-statistics-failed-container { + float:left; + } + } + + .test-container { + border-top: 1px solid @horizontal-dividers-color; + padding-top: 25px; + + .test { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 12px; + font-weight: bold; + } + + .ok { color: #6D9669; } + .ko { color: #B03952; } + } + .message { padding-bottom: 10px; } .summary { font-size: 12px; padding-top: 10px; } - .test { font-size: 11px; font-weight: bold; } .executing { font-size: 12px; font-weight: bold; padding-bottom: 10px;} } From bc8a08baa68ed055d2a55fae0d3665089f3bdba5 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Wed, 27 Apr 2016 22:20:27 +0100 Subject: [PATCH 30/47] Refactoring the creation of the parser to a factory --- lib/parser-factory.js | 11 +++++++++++ lib/test-runner-process.js | 11 +++++------ spec/test-runner-process-spec.js | 8 ++++++-- 3 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 lib/parser-factory.js diff --git a/lib/parser-factory.js b/lib/parser-factory.js new file mode 100644 index 0000000..a687514 --- /dev/null +++ b/lib/parser-factory.js @@ -0,0 +1,11 @@ +/** @babel */ + +import parser from 'tap-parser'; + +class ParserFactory { + getParser() { + return parser(); + } +} + +module.exports = ParserFactory; diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index edd6692..b72a965 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -1,8 +1,8 @@ /** @babel */ import EventEmitter from 'events'; -import parser from 'tap-parser'; import TerminalCommandExecutor from './terminal-command-executor'; +import ParserFactory from './parser-factory' class FilePathParser { parse(filePath) { @@ -16,12 +16,15 @@ class FilePathParser { class TestRunnerProcess extends EventEmitter { constructor( executor = new TerminalCommandExecutor(), + parserFactory = new ParserFactory(), filePathParser = new FilePathParser()) { super(); this.eventHandlers = {}; this.filePathParser = filePathParser; this.terminalCommandExecutor = executor; + this.parserFactory = parserFactory; + this.terminalCommandExecutor.on('dataReceived', data => this._addAvaOutput(data)); this.terminalCommandExecutor.on('dataFinished', () => this._endAvaOutput()); @@ -40,7 +43,7 @@ class TestRunnerProcess extends EventEmitter { this.isRunning = true; this.currentExecution = {passed: 0, failed: 0}; - this.parser = this._getParser(); + this.parser = this.parserFactory.getParser(); this._setHandlersOnParser(this.parser); const filePathParseResult = this.filePathParser.parse(filePath); @@ -79,10 +82,6 @@ class TestRunnerProcess extends EventEmitter { this.isRunning = false; } - _getParser() { - return parser(); - } - destroy() { this.isRunning = false; this.terminalCommandExecutor.destroy(); diff --git a/spec/test-runner-process-spec.js b/spec/test-runner-process-spec.js index 7086a61..e726e54 100644 --- a/spec/test-runner-process-spec.js +++ b/spec/test-runner-process-spec.js @@ -3,6 +3,7 @@ import EventEmitter from 'events'; import TestRunnerProcess from '../lib/test-runner-process'; import TerminalCommandExecutor from '../lib/terminal-command-executor'; +import ParserFactory from '../lib/parser-factory'; class FakeParser extends EventEmitter { constructor() { @@ -22,6 +23,7 @@ describe('TestRunnerProcess', () => { let runner = {}; let executor = {}; let parser = {}; + let parserFactory = {}; class TerminalCommandExecutorDouble extends TerminalCommandExecutor { emulateDataWrittenStdOut(data) { @@ -36,8 +38,10 @@ describe('TestRunnerProcess', () => { beforeEach(() => { parser = new FakeParser(); executor = new TerminalCommandExecutorDouble(); - runner = new TestRunnerProcess(executor); - spyOn(runner, '_getParser').andReturn(parser); + parserFactory = new ParserFactory(); + spyOn(parserFactory, 'getParser').andReturn(parser); + + runner = new TestRunnerProcess(executor, parserFactory); }); it('can be created', () => expect(runner).not.toBeNull()); From f8135c145d4c5623e1d551cc19884762f56fdb58 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Wed, 27 Apr 2016 22:54:52 +0100 Subject: [PATCH 31/47] Initial extraction of test running logic from the panel --- lib/main.js | 18 ++++++++++++++++-- lib/panel.js | 29 ++++++++++++----------------- lib/test-runner-process.js | 2 +- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/lib/main.js b/lib/main.js index d975556..2167074 100644 --- a/lib/main.js +++ b/lib/main.js @@ -2,9 +2,14 @@ import {CompositeDisposable} from 'atom'; import Panel from './panel.js'; +import TestRunnerProcess from './test-runner-process'; module.exports = { activate(state) { + this.testRunnerProcess = new TestRunnerProcess(); + this.testRunnerProcess.on('assert', result => this.panel.renderAssert(result)); + this.testRunnerProcess.on('complete', results => this.panel.renderFinalReport(results)); + this.panel = new Panel(state.testingForAvaViewState); this.atomPanel = atom.workspace.addRightPanel({item: this.panel, visible: false}); this.subscriptions = new CompositeDisposable(); @@ -13,7 +18,16 @@ module.exports = { atom.commands.add('atom-workspace', 'ava:toggle', () => this.toggle())); this.subscriptions.add( - atom.commands.add('atom-workspace', 'ava:run', () => this.panel.run())); + atom.commands.add('atom-workspace', 'ava:run', () => this.run())); + }, + run() { + if (!this.testRunnerProcess.canRun()) { + return; + } + this.panel.renderStartProcess(); + const editor = atom.workspace.getActiveTextEditor(); + const currentFileName = editor.buffer.file.path; + this.testRunnerProcess.run(currentFileName); }, deactivate() { this.subscriptions.dispose(); @@ -27,7 +41,7 @@ module.exports = { this.atomPanel.hide(); } else { this.atomPanel.show(); - this.panel.run(); + this.run(); } } }; diff --git a/lib/panel.js b/lib/panel.js index d951e3a..edb04ce 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -1,35 +1,30 @@ /** @babel */ -import TestRunnerProcess from './test-runner-process'; import UiRenderer from './ui-renderer'; class Panel { - constructor(serializedState, testRunnerProcess = new TestRunnerProcess()) { - this.testRunnerProcess = testRunnerProcess; - - this.testRunnerProcess.on('assert', result => this.renderer.renderAssert(result)); - this.testRunnerProcess.on('complete', results => this.renderer.renderFinalReport(results)); - - this.renderBase(); + constructor() { + this._renderBase(); } - renderBase() { + _renderBase() { this.renderer = new UiRenderer(); this.element = document.createElement('div'); this.element.classList.add('ava'); this.renderer.renderBase(this.element); } - run() { - if (!this.testRunnerProcess.canRun()) { - return; - } - + renderStartProcess() { this.renderer.displayExecutingIndicator(); this.renderer.cleanTestsContainer(); - const editor = atom.workspace.getActiveTextEditor(); - const currentFileName = editor.buffer.file.path; - this.testRunnerProcess.run(currentFileName); + } + + renderAssert(result) { + this.renderer.renderAssert(result); + } + + renderFinalReport(results) { + this.renderer.renderFinalReport(results); } serialize() { } diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index b72a965..bd0565b 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -2,7 +2,7 @@ import EventEmitter from 'events'; import TerminalCommandExecutor from './terminal-command-executor'; -import ParserFactory from './parser-factory' +import ParserFactory from './parser-factory'; class FilePathParser { parse(filePath) { From dbe0a32dd6b3f4f92fea90894825c4d253d69af4 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Fri, 29 Apr 2016 23:18:40 +0100 Subject: [PATCH 32/47] UI improvements --- keymaps/ava.json | 3 +- lib/html-renderer-helper.js | 23 +++++++++ lib/main.js | 37 +++++++++----- lib/panel.js | 95 ++++++++++++++++++++++++++++++----- lib/ui-renderer.js | 98 ------------------------------------- package.json | 5 +- styles/ava.less | 18 ++++++- 7 files changed, 154 insertions(+), 125 deletions(-) create mode 100644 lib/html-renderer-helper.js delete mode 100644 lib/ui-renderer.js diff --git a/keymaps/ava.json b/keymaps/ava.json index 2abe7b3..9fcb2b8 100644 --- a/keymaps/ava.json +++ b/keymaps/ava.json @@ -1,5 +1,6 @@ { "atom-workspace": { - "ctrl-alt-a": "ava:toggle" + "ctrl-alt-r": "ava:run", + "ctrl-alt-a": "ava:toggle" } } diff --git a/lib/html-renderer-helper.js b/lib/html-renderer-helper.js new file mode 100644 index 0000000..14f3f35 --- /dev/null +++ b/lib/html-renderer-helper.js @@ -0,0 +1,23 @@ +/** @babel */ + +class HtmlRendererHelper { + createContainer(cssClass = null, textContent = null) { + const element = document.createElement('div'); + if (cssClass) { + element.classList.add(cssClass); + } + if (textContent) { + element.textContent = textContent; + } + return element; + } + + createImage(src, cssClass = null) { + const img = document.createElement('img'); + img.src = src; + img.classList.add(cssClass); + return img; + } +} + +module.exports = HtmlRendererHelper; diff --git a/lib/main.js b/lib/main.js index 2167074..ace50e8 100644 --- a/lib/main.js +++ b/lib/main.js @@ -6,12 +6,9 @@ import TestRunnerProcess from './test-runner-process'; module.exports = { activate(state) { - this.testRunnerProcess = new TestRunnerProcess(); - this.testRunnerProcess.on('assert', result => this.panel.renderAssert(result)); - this.testRunnerProcess.on('complete', results => this.panel.renderFinalReport(results)); + this.initRunnerProcess(); + this.initUI(state); - this.panel = new Panel(state.testingForAvaViewState); - this.atomPanel = atom.workspace.addRightPanel({item: this.panel, visible: false}); this.subscriptions = new CompositeDisposable(); this.subscriptions.add( @@ -20,7 +17,20 @@ module.exports = { this.subscriptions.add( atom.commands.add('atom-workspace', 'ava:run', () => this.run())); }, + initRunnerProcess() { + this.testRunnerProcess = new TestRunnerProcess(); + this.testRunnerProcess.on('assert', result => this.panel.renderAssert(result)); + this.testRunnerProcess.on('complete', results => this.panel.renderFinalReport(results)); + }, + initUI() { + this.panel = new Panel(this); + this.panel.renderBase(); + this.atomPanel = atom.workspace.addRightPanel({item: this.panel, visible: false}); + }, run() { + if (!this.atomPanel.isVisible()) { + this.toggle(); + } if (!this.testRunnerProcess.canRun()) { return; } @@ -29,13 +39,6 @@ module.exports = { const currentFileName = editor.buffer.file.path; this.testRunnerProcess.run(currentFileName); }, - deactivate() { - this.subscriptions.dispose(); - this.panel.destroy(); - }, - serialize() { - this.atomAva = this.panel.serialize(); - }, toggle() { if (this.atomPanel.isVisible()) { this.atomPanel.hide(); @@ -43,5 +46,15 @@ module.exports = { this.atomPanel.show(); this.run(); } + }, + closePanel() { + this.atomPanel.hide(); + }, + deactivate() { + this.subscriptions.dispose(); + this.panel.destroy(); + }, + serialize() { + this.atomAva = this.panel.serialize(); } }; diff --git a/lib/panel.js b/lib/panel.js index edb04ce..036d1ef 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -1,30 +1,101 @@ /** @babel */ -import UiRenderer from './ui-renderer'; +import HtmlRendererHelper from './html-renderer-helper'; class Panel { - constructor() { - this._renderBase(); + constructor(pluginInstance) { + this.htmlRendererHelper = new HtmlRendererHelper(); + this.pluginInstance = pluginInstance; + this.logoUrl = 'https://raw.githubusercontent.com/sindresorhus/ava/master/media/logo.png'; } - _renderBase() { - this.renderer = new UiRenderer(); + renderBase() { this.element = document.createElement('div'); this.element.classList.add('ava'); - this.renderer.renderBase(this.element); + + const closeIcon = this.htmlRendererHelper.createContainer('close-icon', 'x'); + this.element.appendChild(closeIcon); + + closeIcon.addEventListener('click', function (e) { + this.closePanel(e); + }.bind(this.pluginInstance), false); + + const fileName = this.htmlRendererHelper.createContainer('file-header', 'filename.js'); + this.element.appendChild(fileName); + + const avaLogoContainer = this.htmlRendererHelper.createContainer('ava-logo-container'); + const avaLogo = this.htmlRendererHelper.createImage(this.logoUrl, 'ava-logo-image'); + avaLogoContainer.appendChild(avaLogo); + this.element.appendChild(avaLogoContainer); + + this.testsStatistics = this.htmlRendererHelper.createContainer('test-statistics'); + this._createTestStatisticsSection(this.testsStatistics, 'passed'); + this._createTestStatisticsSection(this.testsStatistics, 'failed'); + this.element.appendChild(this.testsStatistics); + + this.executing = this.htmlRendererHelper.createContainer('executing', 'Loading'); + this.executing.style.display = 'none'; + this.element.appendChild(this.executing); + + this.testsContainer = this.htmlRendererHelper.createContainer('test-container'); + this.element.appendChild(this.testsContainer); + } + + renderAssert(assertResult) { + const assert = assertResult.assert; + + const newTest = this.htmlRendererHelper.createContainer('test'); + newTest.classList.add((assert.ok) ? 'ok' : 'ko'); + newTest.textContent = `${assert.name}`; + this.testsContainer.appendChild(newTest); + + this._updateTestStatisticSection(assertResult); + } + + renderFinalReport(results) { + this.hideExecutingIndicator(); + + const summary = this.htmlRendererHelper.createContainer('summary'); + const percentage = Math.round((results.pass / results.count) * 100); + summary.textContent = `${results.count} total - ${percentage}% passed`; + + this.testsContainer.appendChild(summary); + } + + cleanTestsContainer() { + this.testsContainer.innerHTML = ''; + } + + _updateTestStatisticSection(assertResult) { + const passedContainer = document.getElementById('passed'); + const failedContainer = document.getElementById('failed'); + passedContainer.textContent = assertResult.currentExecution.passed; + failedContainer.textContent = assertResult.currentExecution.failed; + } + + _createTestStatisticsSection(parentContainer, containerName) { + const container = this.htmlRendererHelper.createContainer(`test-statistics-${containerName}-container`); + const number = this.htmlRendererHelper.createContainer('number'); + const text = this.htmlRendererHelper.createContainer('text'); + number.setAttribute('id', containerName); + number.textContent = '-'; + text.textContent = containerName.toUpperCase(); + container.appendChild(number); + container.appendChild(text); + parentContainer.appendChild(container); } renderStartProcess() { - this.renderer.displayExecutingIndicator(); - this.renderer.cleanTestsContainer(); + this.displayExecutingIndicator(); + this.cleanTestsContainer(); } - renderAssert(result) { - this.renderer.renderAssert(result); + displayExecutingIndicator() { + this.executing.style.display = 'block'; } - renderFinalReport(results) { - this.renderer.renderFinalReport(results); + hideExecutingIndicator() { + this.executing.style.display = 'none'; } serialize() { } diff --git a/lib/ui-renderer.js b/lib/ui-renderer.js deleted file mode 100644 index 19d389e..0000000 --- a/lib/ui-renderer.js +++ /dev/null @@ -1,98 +0,0 @@ -/** @babel */ - -class UiRenderer { - renderBase(container) { - this.element = container; - - const fileName = this._createContainer('file-header'); - fileName.textContent = 'filename.js'; - this.element.appendChild(fileName); - - const avaLogoContainer = this._createContainer('ava-logo-container'); - const avaLogo = document.createElement('img'); - avaLogo.src = 'https://raw.githubusercontent.com/sindresorhus/ava/master/media/logo.png'; - avaLogo.classList.add('ava-logo-image'); - avaLogoContainer.appendChild(avaLogo); - this.element.appendChild(avaLogoContainer); - - this.testsStatistics = this._createContainer('test-statistics'); - this._createTestStatisticsSection(this.testsStatistics, 'passed'); - this._createTestStatisticsSection(this.testsStatistics, 'failed'); - this.element.appendChild(this.testsStatistics); - - this.executing = this._createContainer('executing'); - this.executing.textContent = 'Loading'; - this.executing.style.display = 'none'; - this.element.appendChild(this.executing); - - this.testsContainer = this._createContainer('test-container'); - this.element.appendChild(this.testsContainer); - } - - renderAssert(assertResult) { - const assert = assertResult.assert; - - const newTest = this._createContainer('test'); - newTest.classList.add((assert.ok) ? 'ok' : 'ko'); - newTest.textContent = `${assert.name}`; - this.testsContainer.appendChild(newTest); - - // TODO: Change assert to something as AssertResult - // TODO: Rethink the concept - this._updateTestStatisticSection(assertResult); - } - - renderFinalReport(results) { - this.hideExecutingIndicator(); - - const summary = this._createContainer('div', 'summary'); - const percentage = Math.round((results.pass / results.count) * 100); - summary.textContent = `${results.count} total - ${percentage}% passed`; - - this.testsContainer.appendChild(summary); - } - - cleanTestsContainer() { - this.testsContainer.innerHTML = ''; - } - - // TODO: The class name should be extracted to a constant - // TODO: Maybe this generalization shouldn't be done a the container - // TODO: Should be a member, or have some class to deal with it. - _updateTestStatisticSection(assertResult) { - const passedContainer = document.getElementById('passed'); - const failedContainer = document.getElementById('failed'); - passedContainer.textContent = assertResult.currentExecution.passed; - failedContainer.textContent = assertResult.currentExecution.failed; - } - - _createTestStatisticsSection(parentContainer, containerName) { - const container = this._createContainer(`test-statistics-${containerName}-container`); - const number = this._createContainer('number'); - const text = this._createContainer('text'); - number.setAttribute('id', containerName); - number.textContent = '-'; - text.textContent = containerName.toUpperCase(); - container.appendChild(number); - container.appendChild(text); - parentContainer.appendChild(container); - } - - _createContainer(cssClass = null) { - const element = document.createElement('div'); - if (cssClass) { - element.classList.add(cssClass); - } - return element; - } - - displayExecutingIndicator() { - this.executing.style.display = 'block'; - } - - hideExecutingIndicator() { - this.executing.style.display = 'none'; - } -} - -module.exports = UiRenderer; diff --git a/package.json b/package.json index 9a2f0c0..00ea8f5 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,10 @@ "test": "xo" }, "activationCommands": { - "atom-workspace": "ava:toggle" + "atom-workspace": [ + "ava:toggle", + "ava:run" + ] }, "keywords": [ "snippets", diff --git a/styles/ava.less b/styles/ava.less index 190adaf..df246e7 100644 --- a/styles/ava.less +++ b/styles/ava.less @@ -7,6 +7,14 @@ padding: 30px; font-size: 15px; + .close-icon { + text-align: right; + margin-bottom: 10px; + margin-top: -15px; + font-size: 18px; + cursor: pointer; + } + .file-header { border-top: 1px solid @horizontal-dividers-color; border-bottom: 1px solid @horizontal-dividers-color; @@ -65,6 +73,14 @@ } .message { padding-bottom: 10px; } - .summary { font-size: 12px; padding-top: 10px; } + + .summary { + text-align: center; + padding: 10px; + margin-top: 15px; + border-top: 1px solid @horizontal-dividers-color; + font-size: 12px; + } + .executing { font-size: 12px; font-weight: bold; padding-bottom: 10px;} } From 83c0d2cc00c949bfbeed6065c386f1627ff80f5b Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Fri, 29 Apr 2016 23:43:52 +0100 Subject: [PATCH 33/47] Injecting Panel dependency --- lib/panel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/panel.js b/lib/panel.js index 036d1ef..0408ea2 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -3,8 +3,8 @@ import HtmlRendererHelper from './html-renderer-helper'; class Panel { - constructor(pluginInstance) { - this.htmlRendererHelper = new HtmlRendererHelper(); + constructor(pluginInstance, htmlRendererHelper = new HtmlRendererHelper()) { + this.htmlRendererHelper = htmlRendererHelper; this.pluginInstance = pluginInstance; this.logoUrl = 'https://raw.githubusercontent.com/sindresorhus/ava/master/media/logo.png'; } From 267805e68c4bec56d8cf3f119abbc369f9e17a67 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sat, 30 Apr 2016 02:00:30 +0100 Subject: [PATCH 34/47] Update file-name header --- lib/main.js | 9 +++++++-- lib/panel.js | 36 +++++++++++++++++++----------------- lib/test-runner-process.js | 20 ++++---------------- styles/ava.less | 5 +++-- 4 files changed, 33 insertions(+), 37 deletions(-) diff --git a/lib/main.js b/lib/main.js index ace50e8..261bbb4 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,5 +1,6 @@ /** @babel */ +import path from 'path'; import {CompositeDisposable} from 'atom'; import Panel from './panel.js'; import TestRunnerProcess from './test-runner-process'; @@ -34,10 +35,14 @@ module.exports = { if (!this.testRunnerProcess.canRun()) { return; } - this.panel.renderStartProcess(); + const editor = atom.workspace.getActiveTextEditor(); const currentFileName = editor.buffer.file.path; - this.testRunnerProcess.run(currentFileName); + const folder = path.dirname(currentFileName); + const file = path.basename(currentFileName); + + this.panel.renderStartProcess(file); + this.testRunnerProcess.run(folder, file); }, toggle() { if (this.atomPanel.isVisible()) { diff --git a/lib/panel.js b/lib/panel.js index 0408ea2..837c9f9 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -16,11 +16,10 @@ class Panel { const closeIcon = this.htmlRendererHelper.createContainer('close-icon', 'x'); this.element.appendChild(closeIcon); - closeIcon.addEventListener('click', function (e) { - this.closePanel(e); - }.bind(this.pluginInstance), false); + closeIcon.addEventListener('click', e => this.pluginInstance.closePanel(e), false); - const fileName = this.htmlRendererHelper.createContainer('file-header', 'filename.js'); + const fileName = this.htmlRendererHelper.createContainer(); + fileName.setAttribute('id', 'file-header'); this.element.appendChild(fileName); const avaLogoContainer = this.htmlRendererHelper.createContainer('ava-logo-container'); @@ -66,6 +65,22 @@ class Panel { this.testsContainer.innerHTML = ''; } + renderStartProcess(fileName) { + const fileHeader = document.getElementById('file-header'); + fileHeader.textContent = fileName; + + this.displayExecutingIndicator(); + this.cleanTestsContainer(); + } + + displayExecutingIndicator() { + this.executing.style.display = 'block'; + } + + hideExecutingIndicator() { + this.executing.style.display = 'none'; + } + _updateTestStatisticSection(assertResult) { const passedContainer = document.getElementById('passed'); const failedContainer = document.getElementById('failed'); @@ -85,19 +100,6 @@ class Panel { parentContainer.appendChild(container); } - renderStartProcess() { - this.displayExecutingIndicator(); - this.cleanTestsContainer(); - } - - displayExecutingIndicator() { - this.executing.style.display = 'block'; - } - - hideExecutingIndicator() { - this.executing.style.display = 'none'; - } - serialize() { } destroy() { diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index bd0565b..f5ea15b 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -4,24 +4,13 @@ import EventEmitter from 'events'; import TerminalCommandExecutor from './terminal-command-executor'; import ParserFactory from './parser-factory'; -class FilePathParser { - parse(filePath) { - // TODO: Fix parsing of folders and files - const folder = filePath.substring(0, filePath.lastIndexOf('/') + 1); - const file = filePath.split('/').pop(); - return {folder, file}; - } -} - class TestRunnerProcess extends EventEmitter { constructor( executor = new TerminalCommandExecutor(), - parserFactory = new ParserFactory(), - filePathParser = new FilePathParser()) { + parserFactory = new ParserFactory()) { super(); this.eventHandlers = {}; - this.filePathParser = filePathParser; this.terminalCommandExecutor = executor; this.parserFactory = parserFactory; @@ -35,7 +24,7 @@ class TestRunnerProcess extends EventEmitter { return !this.isRunning; } - run(filePath) { + run(folder, file) { if (!this.canRun()) { return; } @@ -46,10 +35,9 @@ class TestRunnerProcess extends EventEmitter { this.parser = this.parserFactory.getParser(); this._setHandlersOnParser(this.parser); - const filePathParseResult = this.filePathParser.parse(filePath); - const command = `ava ${filePathParseResult.file} --tap`; + const command = `ava ${file} --tap`; - this.terminalCommandExecutor.run(command, filePathParseResult.folder); + this.terminalCommandExecutor.run(command, folder); } _setHandlersOnParser(parser) { diff --git a/styles/ava.less b/styles/ava.less index df246e7..739c75b 100644 --- a/styles/ava.less +++ b/styles/ava.less @@ -15,10 +15,12 @@ cursor: pointer; } - .file-header { + #file-header { border-top: 1px solid @horizontal-dividers-color; border-bottom: 1px solid @horizontal-dividers-color; padding: 5px; + font-size: 12px; + font-weight: bold; } .ava-logo-container { @@ -65,7 +67,6 @@ overflow: hidden; text-overflow: ellipsis; font-size: 12px; - font-weight: bold; } .ok { color: #6D9669; } From 27304ac8344b5cceb5450fba6fad4a9e23bab37a Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sat, 30 Apr 2016 02:08:18 +0100 Subject: [PATCH 35/47] Trying to fix xo errors that werent catch by my local version --- lib/panel.js | 2 +- lib/terminal-command-executor.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/panel.js b/lib/panel.js index 837c9f9..1cfaa11 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -44,7 +44,7 @@ class Panel { const assert = assertResult.assert; const newTest = this.htmlRendererHelper.createContainer('test'); - newTest.classList.add((assert.ok) ? 'ok' : 'ko'); + newTest.classList.add(assert.ok ? 'ok' : 'ko'); newTest.textContent = `${assert.name}`; this.testsContainer.appendChild(newTest); diff --git a/lib/terminal-command-executor.js b/lib/terminal-command-executor.js index 459b302..225342d 100644 --- a/lib/terminal-command-executor.js +++ b/lib/terminal-command-executor.js @@ -23,9 +23,9 @@ class TerminalCommandExecutor extends EventEmitter { this.terminal.stdout.on('data', data => this._stdOutDataReceived(data)); this.terminal.stderr.on('data', data => this._stdErrDataReceived(data)); - const terminalCommand = (this.destinyFolder) ? - `cd \"${this.destinyFolder}\" && ${this.command}\n` : - `${this.command}\n`; + const terminalCommand = this.destinyFolder ? + `cd \"${this.destinyFolder}\" && ${this.command}\n` : + `${this.command}\n`; this.terminal.stdin.write(terminalCommand); this.terminal.stdin.write('exit\n'); From 4059434006ee111f864a1aefefdd66fbd0482a5e Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Mon, 2 May 2016 14:57:43 +0100 Subject: [PATCH 36/47] Initial extraction of template for the panel view --- lib/panel.js | 49 +++++++++++------------------------------------- styles/ava.less | 4 ++-- views/panel.html | 17 +++++++++++++++++ 3 files changed, 30 insertions(+), 40 deletions(-) create mode 100644 views/panel.html diff --git a/lib/panel.js b/lib/panel.js index 1cfaa11..6e98186 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -1,43 +1,26 @@ /** @babel */ +/* global __dirname */ +import path from 'path'; +import fs from 'fs'; import HtmlRendererHelper from './html-renderer-helper'; class Panel { constructor(pluginInstance, htmlRendererHelper = new HtmlRendererHelper()) { this.htmlRendererHelper = htmlRendererHelper; this.pluginInstance = pluginInstance; - this.logoUrl = 'https://raw.githubusercontent.com/sindresorhus/ava/master/media/logo.png'; } renderBase() { this.element = document.createElement('div'); this.element.classList.add('ava'); - const closeIcon = this.htmlRendererHelper.createContainer('close-icon', 'x'); - this.element.appendChild(closeIcon); + const resolvedPath = path.resolve(__dirname, '../views/panel.html'); + this.element.innerHTML = fs.readFileSync(resolvedPath); + this.testsContainer = this.element.getElementsByClassName('tests-container')[0]; + const closeIcon = this.element.getElementsByClassName('close-icon')[0]; closeIcon.addEventListener('click', e => this.pluginInstance.closePanel(e), false); - - const fileName = this.htmlRendererHelper.createContainer(); - fileName.setAttribute('id', 'file-header'); - this.element.appendChild(fileName); - - const avaLogoContainer = this.htmlRendererHelper.createContainer('ava-logo-container'); - const avaLogo = this.htmlRendererHelper.createImage(this.logoUrl, 'ava-logo-image'); - avaLogoContainer.appendChild(avaLogo); - this.element.appendChild(avaLogoContainer); - - this.testsStatistics = this.htmlRendererHelper.createContainer('test-statistics'); - this._createTestStatisticsSection(this.testsStatistics, 'passed'); - this._createTestStatisticsSection(this.testsStatistics, 'failed'); - this.element.appendChild(this.testsStatistics); - - this.executing = this.htmlRendererHelper.createContainer('executing', 'Loading'); - this.executing.style.display = 'none'; - this.element.appendChild(this.executing); - - this.testsContainer = this.htmlRendererHelper.createContainer('test-container'); - this.element.appendChild(this.testsContainer); } renderAssert(assertResult) { @@ -74,11 +57,13 @@ class Panel { } displayExecutingIndicator() { - this.executing.style.display = 'block'; + const executing = document.getElementById('executing'); + executing.style.display = 'block'; } hideExecutingIndicator() { - this.executing.style.display = 'none'; + const executing = document.getElementById('executing'); + executing.style.display = 'none'; } _updateTestStatisticSection(assertResult) { @@ -88,18 +73,6 @@ class Panel { failedContainer.textContent = assertResult.currentExecution.failed; } - _createTestStatisticsSection(parentContainer, containerName) { - const container = this.htmlRendererHelper.createContainer(`test-statistics-${containerName}-container`); - const number = this.htmlRendererHelper.createContainer('number'); - const text = this.htmlRendererHelper.createContainer('text'); - number.setAttribute('id', containerName); - number.textContent = '-'; - text.textContent = containerName.toUpperCase(); - container.appendChild(number); - container.appendChild(text); - parentContainer.appendChild(container); - } - serialize() { } destroy() { diff --git a/styles/ava.less b/styles/ava.less index 739c75b..ec7115f 100644 --- a/styles/ava.less +++ b/styles/ava.less @@ -58,7 +58,7 @@ } } - .test-container { + .tests-container { border-top: 1px solid @horizontal-dividers-color; padding-top: 25px; @@ -83,5 +83,5 @@ font-size: 12px; } - .executing { font-size: 12px; font-weight: bold; padding-bottom: 10px;} + #executing { font-size: 12px; font-weight: bold; padding-bottom: 10px;} } diff --git a/views/panel.html b/views/panel.html new file mode 100644 index 0000000..ed947a1 --- /dev/null +++ b/views/panel.html @@ -0,0 +1,17 @@ +
x
+
+
+ +
+
+
+
-
+
PASSED
+
+
+
-
+
PASSED
+
+
+ +
From 87ce941a0eb26b23f17583586fafb6ac1119ad25 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Mon, 2 May 2016 15:09:29 +0100 Subject: [PATCH 37/47] Initial changes in the UI towards supporting multiple files --- styles/ava.less | 22 ++++++++++++---------- views/panel.html | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/styles/ava.less b/styles/ava.less index ec7115f..6a0d7e2 100644 --- a/styles/ava.less +++ b/styles/ava.less @@ -4,21 +4,19 @@ .ava { width: 350px; - padding: 30px; font-size: 15px; .close-icon { text-align: right; - margin-bottom: 10px; - margin-top: -15px; - font-size: 18px; + margin-right: 10px; + font-size: 16px; cursor: pointer; } #file-header { - border-top: 1px solid @horizontal-dividers-color; - border-bottom: 1px solid @horizontal-dividers-color; - padding: 5px; + background-color: #EEE; + color: #888; + padding: 10px; font-size: 12px; font-weight: bold; } @@ -59,8 +57,7 @@ } .tests-container { - border-top: 1px solid @horizontal-dividers-color; - padding-top: 25px; + padding: 10px 30px; .test { white-space: nowrap; @@ -83,5 +80,10 @@ font-size: 12px; } - #executing { font-size: 12px; font-weight: bold; padding-bottom: 10px;} + #executing { + font-size: 12px; + font-weight: bold; + padding-bottom: 10px; + text-align:center; + } } diff --git a/views/panel.html b/views/panel.html index ed947a1..6822549 100644 --- a/views/panel.html +++ b/views/panel.html @@ -1,5 +1,4 @@
x
-
@@ -14,4 +13,5 @@ +
From 786465a29be950404a224e1c2891a443b5505107 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Mon, 2 May 2016 15:44:40 +0100 Subject: [PATCH 38/47] New loading indicator --- lib/panel.js | 5 +++-- styles/ava.less | 14 ++++++-------- styles/loading-indicator.less | 32 ++++++++++++++++++++++++++++++++ views/panel.html | 8 +++++++- 4 files changed, 48 insertions(+), 11 deletions(-) create mode 100644 styles/loading-indicator.less diff --git a/lib/panel.js b/lib/panel.js index 6e98186..14eebb8 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -9,6 +9,7 @@ class Panel { constructor(pluginInstance, htmlRendererHelper = new HtmlRendererHelper()) { this.htmlRendererHelper = htmlRendererHelper; this.pluginInstance = pluginInstance; + this.loadingSelector = 'sk-three-bounce'; } renderBase() { @@ -57,12 +58,12 @@ class Panel { } displayExecutingIndicator() { - const executing = document.getElementById('executing'); + const executing = document.getElementById(this.loadingSelector); executing.style.display = 'block'; } hideExecutingIndicator() { - const executing = document.getElementById('executing'); + const executing = document.getElementById(this.loadingSelector); executing.style.display = 'none'; } diff --git a/styles/ava.less b/styles/ava.less index 6a0d7e2..d9662d7 100644 --- a/styles/ava.less +++ b/styles/ava.less @@ -1,4 +1,5 @@ @import 'ui-variables'; +@import "loading-indicator.less"; @horizontal-dividers-color: #DDD; @@ -16,9 +17,11 @@ #file-header { background-color: #EEE; color: #888; - padding: 10px; + padding: 7px; font-size: 12px; font-weight: bold; + border-top: 1px solid #E9E9E9; + border-bottom: 1px solid #E9E9E9; } .ava-logo-container { @@ -32,7 +35,7 @@ display: table; margin: 0 auto; text-align: center; - padding-bottom: 25px; + padding-bottom: 5px; .number { font-family: "Helvetica Neue", Helvetica; @@ -80,10 +83,5 @@ font-size: 12px; } - #executing { - font-size: 12px; - font-weight: bold; - padding-bottom: 10px; - text-align:center; - } + #executing { height: 35px; } } diff --git a/styles/loading-indicator.less b/styles/loading-indicator.less new file mode 100644 index 0000000..16e8809 --- /dev/null +++ b/styles/loading-indicator.less @@ -0,0 +1,32 @@ +#sk-three-bounce { + margin: 0px auto; + width: 80px; + text-align: center; } + #sk-three-bounce .sk-child { + width: 6px; + height: 6px; + background-color: #555; + border-radius: 100%; + display: inline-block; + -webkit-animation: sk-three-bounce 1.4s ease-in-out 0s infinite both; + animation: sk-three-bounce 1.4s ease-in-out 0s infinite both; } + #sk-three-bounce .sk-bounce1 { + -webkit-animation-delay: -0.32s; + animation-delay: -0.32s; } + #sk-three-bounce .sk-bounce2 { + -webkit-animation-delay: -0.16s; + animation-delay: -0.16s; } +@-webkit-keyframes sk-three-bounce { + 0%, 80%, 100% { + -webkit-transform: scale(0); + transform: scale(0); } + 40% { + -webkit-transform: scale(1); + transform: scale(1); } } +@keyframes sk-three-bounce { + 0%, 80%, 100% { + -webkit-transform: scale(0); + transform: scale(0); } + 40% { + -webkit-transform: scale(1); + transform: scale(1); } } diff --git a/views/panel.html b/views/panel.html index 6822549..846146b 100644 --- a/views/panel.html +++ b/views/panel.html @@ -12,6 +12,12 @@
PASSED
- +
+
+
+
+
+
+
From a35c73d4bd1e00875fa047000db23886b8f3d6e9 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Mon, 2 May 2016 16:20:55 +0100 Subject: [PATCH 39/47] Removing the dependency on the status code --- lib/terminal-command-executor.js | 4 +--- styles/ava.less | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/terminal-command-executor.js b/lib/terminal-command-executor.js index 225342d..28ac644 100644 --- a/lib/terminal-command-executor.js +++ b/lib/terminal-command-executor.js @@ -40,9 +40,7 @@ class TerminalCommandExecutor extends EventEmitter { } _streamClosed(code) { - if (code === 1) { - this.emit(this.dataFinishedEventName, code); - } + this.emit(this.dataFinishedEventName, code); } } diff --git a/styles/ava.less b/styles/ava.less index d9662d7..964e859 100644 --- a/styles/ava.less +++ b/styles/ava.less @@ -6,6 +6,7 @@ .ava { width: 350px; font-size: 15px; + overflow: scroll; .close-icon { text-align: right; From dc5cc762578f24328e4d1265933305f416c85cc6 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Mon, 2 May 2016 17:15:26 +0100 Subject: [PATCH 40/47] Adapting tests to new test runner api --- lib/panel.js | 8 ++++++-- spec/test-runner-process-spec.js | 14 +++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/panel.js b/lib/panel.js index 14eebb8..14b8bcd 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -59,12 +59,16 @@ class Panel { displayExecutingIndicator() { const executing = document.getElementById(this.loadingSelector); - executing.style.display = 'block'; + if (executing) { + executing.style.display = 'block'; + } } hideExecutingIndicator() { const executing = document.getElementById(this.loadingSelector); - executing.style.display = 'none'; + if (executing) { + executing.style.display = 'none'; + } } _updateTestStatisticSection(assertResult) { diff --git a/spec/test-runner-process-spec.js b/spec/test-runner-process-spec.js index e726e54..e1aee30 100644 --- a/spec/test-runner-process-spec.js +++ b/spec/test-runner-process-spec.js @@ -49,35 +49,35 @@ describe('TestRunnerProcess', () => { it('runs the executor with the appropriate parameters', () => { spyOn(atom.project, 'getPaths').andReturn(['path']); spyOn(executor, 'run'); - runner.run('/somefolder/filename'); + runner.run('/somefolder/', 'filename'); expect(executor.run).toHaveBeenCalledWith('ava filename --tap', '/somefolder/'); }); it('redirects the output for the parser when is received', () => { spyOn(parser, 'write'); - runner.run('/somefolder/filename'); + runner.run('/somefolder/', 'filename'); executor.emulateDataWrittenStdOut('newdata'); expect(parser.write).toHaveBeenCalledWith('newdata'); }); it('closes the parser stream when the output is over', () => { spyOn(parser, 'end'); - runner.run('/somefolder/filename'); + runner.run('/somefolder/', 'filename'); executor.emulateDataFinished(0); expect(parser.end).toHaveBeenCalled(); }); it('prevents multiple executions', () => { spyOn(executor, 'run'); - runner.run('/somefolder/filename'); - runner.run('/somefolder/filename'); + runner.run('/somefolder/', 'filename'); + runner.run('/somefolder/', 'filename'); expect(executor.run.callCount).toBe(1); }); it('informs about the state of the execution', () => { spyOn(executor, 'run'); expect(runner.canRun()).toBe(true); - runner.run('/somefolder/filename'); + runner.run('/somefolder/', 'filename'); expect(runner.canRun()).toBe(false); executor.emulateDataFinished(0); expect(runner.canRun()).toBe(true); @@ -88,7 +88,7 @@ describe('TestRunnerProcess', () => { const okAssertResult = {ok: true}; const notOkAssertResult = {ok: false}; - runner.run('/somefolder/filename'); + runner.run('/somefolder/', 'filename'); runner.on('assert', result => receivedAssertResults.push(result)); parser.emitAssert(okAssertResult); From 4458a8ac016ef65c84c9db194d1557675fcd7a5b Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Mon, 2 May 2016 17:38:57 +0100 Subject: [PATCH 41/47] Solved error in the template: both columns had passed as text --- views/panel.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/panel.html b/views/panel.html index 846146b..4516c0a 100644 --- a/views/panel.html +++ b/views/panel.html @@ -9,7 +9,7 @@
-
-
PASSED
+
FAILED
From b2d0b8b33656dbad80fcc6ae0c9db6cad106dbb7 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Wed, 4 May 2016 23:27:16 +0100 Subject: [PATCH 42/47] Chaging the close icon --- styles/ava.less | 1 + views/panel.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/styles/ava.less b/styles/ava.less index 964e859..313d18d 100644 --- a/styles/ava.less +++ b/styles/ava.less @@ -11,6 +11,7 @@ .close-icon { text-align: right; margin-right: 10px; + margin-top: 10px; font-size: 16px; cursor: pointer; } diff --git a/views/panel.html b/views/panel.html index 4516c0a..8b19a3f 100644 --- a/views/panel.html +++ b/views/panel.html @@ -1,4 +1,4 @@ -
x
+
From 6d6449a2b93b69d81ba3bea8cda839ce1aa80d53 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Thu, 5 May 2016 20:51:01 +0100 Subject: [PATCH 43/47] TODO and SKIP tests are not taken as failed/passed --- lib/panel.js | 9 ++++++++- lib/test-runner-process.js | 6 ++++-- spec/test-runner-process-spec.js | 26 ++++++++++++++++++++++++++ styles/ava.less | 2 ++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/lib/panel.js b/lib/panel.js index 14b8bcd..e505255 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -28,13 +28,20 @@ class Panel { const assert = assertResult.assert; const newTest = this.htmlRendererHelper.createContainer('test'); - newTest.classList.add(assert.ok ? 'ok' : 'ko'); + newTest.classList.add(this._getCssClassForAssert(assert)); newTest.textContent = `${assert.name}`; this.testsContainer.appendChild(newTest); this._updateTestStatisticSection(assertResult); } + _getCssClassForAssert(assert) { + if (assert.ok) { + return (assert.skip) ? 'skipped' : 'ok'; + } + return (assert.todo) ? 'todo' : 'ko'; + } + renderFinalReport(results) { this.hideExecutingIndicator(); diff --git a/lib/test-runner-process.js b/lib/test-runner-process.js index f5ea15b..1d9a10c 100644 --- a/lib/test-runner-process.js +++ b/lib/test-runner-process.js @@ -55,8 +55,10 @@ class TestRunnerProcess extends EventEmitter { _updateCurrentExecution(assert) { if (assert.ok) { - this.currentExecution.passed++; - } else { + if (!assert.skip) { + this.currentExecution.passed++; + } + } else if (!assert.todo) { this.currentExecution.failed++; } } diff --git a/spec/test-runner-process-spec.js b/spec/test-runner-process-spec.js index e1aee30..748dcfa 100644 --- a/spec/test-runner-process-spec.js +++ b/spec/test-runner-process-spec.js @@ -99,4 +99,30 @@ describe('TestRunnerProcess', () => { expect(receivedAssertResults[1].assert).toBe(notOkAssertResult); expect(receivedAssertResults[1].currentExecution.passed).toBe(1); }); + + it('does not count skipped tests as success', () => { + const receivedAssertResults = []; + const assertResult = {ok: true, skip: true}; + runner.run('/somefolder/', 'filename'); + runner.on('assert', result => receivedAssertResults.push(result)); + + parser.emitAssert(assertResult); + + expect(receivedAssertResults[0].assert).toBe(assertResult); + expect(receivedAssertResults[0].currentExecution.passed).toBe(0); + expect(receivedAssertResults[0].currentExecution.failed).toBe(0); + }); + + it('does not count todo tests as failed', () => { + const receivedAssertResults = []; + const assertResult = {ok: false, todo: true}; + runner.run('/somefolder/', 'filename'); + runner.on('assert', result => receivedAssertResults.push(result)); + + parser.emitAssert(assertResult); + + expect(receivedAssertResults[0].assert).toBe(assertResult); + expect(receivedAssertResults[0].currentExecution.passed).toBe(0); + expect(receivedAssertResults[0].currentExecution.failed).toBe(0); + }); }); diff --git a/styles/ava.less b/styles/ava.less index 313d18d..9ea67f1 100644 --- a/styles/ava.less +++ b/styles/ava.less @@ -73,6 +73,8 @@ .ok { color: #6D9669; } .ko { color: #B03952; } + .skipped { color: #CCC; } + .todo { color: #CCC; } } .message { padding-bottom: 10px; } From 7d3aa5edecbd77b99fec7bcaa549ce63dcd59d02 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Thu, 5 May 2016 21:24:45 +0100 Subject: [PATCH 44/47] Fixes in the styles towards supporting better themes --- styles/ava.less | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/styles/ava.less b/styles/ava.less index 9ea67f1..1aed8b5 100644 --- a/styles/ava.less +++ b/styles/ava.less @@ -17,13 +17,15 @@ } #file-header { - background-color: #EEE; + background-color: @tab-bar-background-color; color: #888; padding: 7px; font-size: 12px; font-weight: bold; - border-top: 1px solid #E9E9E9; - border-bottom: 1px solid #E9E9E9; + border-top: 1px solid @tab-bar-border-color; + border-bottom: 1px solid @tab-bar-border-color; + color: @text-color; + opacity: 0.5; } .ava-logo-container { @@ -73,8 +75,8 @@ .ok { color: #6D9669; } .ko { color: #B03952; } - .skipped { color: #CCC; } - .todo { color: #CCC; } + .skipped { color: @text-color-subtle; } + .todo { color: @text-color-subtle; } } .message { padding-bottom: 10px; } From 099a5ff2562448f544ca2c15c577a71768725ac0 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Thu, 5 May 2016 22:16:31 +0100 Subject: [PATCH 45/47] Preventing crashes when the user does not have an window with active editor selected --- lib/main.js | 5 ++++- styles/loading-indicator.less | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/main.js b/lib/main.js index 261bbb4..682aa29 100644 --- a/lib/main.js +++ b/lib/main.js @@ -28,11 +28,14 @@ module.exports = { this.panel.renderBase(); this.atomPanel = atom.workspace.addRightPanel({item: this.panel, visible: false}); }, + canRun() { + return (atom.workspace.getActiveTextEditor() && this.testRunnerProcess.canRun()); + }, run() { if (!this.atomPanel.isVisible()) { this.toggle(); } - if (!this.testRunnerProcess.canRun()) { + if (!this.canRun()) { return; } diff --git a/styles/loading-indicator.less b/styles/loading-indicator.less index 16e8809..d4ebf40 100644 --- a/styles/loading-indicator.less +++ b/styles/loading-indicator.less @@ -1,7 +1,9 @@ #sk-three-bounce { margin: 0px auto; width: 80px; - text-align: center; } + text-align: center; + display:none; +} #sk-three-bounce .sk-child { width: 6px; height: 6px; From 2bd121b15235baad52511d88ec9a9960eb385a34 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Thu, 5 May 2016 22:51:42 +0100 Subject: [PATCH 46/47] Fixed some discrepancy in the summary when the suite has skip/todo tests --- lib/panel.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/panel.js b/lib/panel.js index e505255..64d553a 100644 --- a/lib/panel.js +++ b/lib/panel.js @@ -46,7 +46,8 @@ class Panel { this.hideExecutingIndicator(); const summary = this.htmlRendererHelper.createContainer('summary'); - const percentage = Math.round((results.pass / results.count) * 100); + const passed = results.pass - (results.skip ? results.skip : 0); + const percentage = Math.round((passed / results.count) * 100); summary.textContent = `${results.count} total - ${percentage}% passed`; this.testsContainer.appendChild(summary); From 71b5372b445ceeee3fea88e6303d444399b92c30 Mon Sep 17 00:00:00 2001 From: Jacob84 Date: Sun, 8 May 2016 21:50:16 +0100 Subject: [PATCH 47/47] AVA logo served from local asset --- media/logo.png | Bin 0 -> 6936 bytes views/panel.html | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 media/logo.png diff --git a/media/logo.png b/media/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a74de1d7a7b5470c9150cf3601ac81dbd1468584 GIT binary patch literal 6936 zcmbtZ^nX|u~^{2 z`%v$VG2C-|^?e_@y|Bno-`Zf$8odAea4$9&yT)Q_vn5?0W)?dR`jyj|P!deA)SQ-K&+D?kq2k^t+p zeEtBtKos%4VxPN*(2p?1@4n~gHcsUX{-uS3wR5J8d#DlO{psF6Hf>`G@z>Pj25eNe zz0rDM+3bTY<)}J{bfFJ|*nW0}N^LCqKx_B&hz5M$UO;y=Q}68V7s*2R^%UQqT9z2F zy(gO~|J=i``gcN}>t%=~@1JV>s0|Q4H-A~y5ZRoNXz&desb_kbO`J5}5;*8KzSh?mzTFTM-d4{^i^)vjsTCFa zo)Z2tuL(uL|20sv_mQ1M4o!YLr=BQd2qs<){R|>WJlc zlwoSb?E`jv=9SGOoM{d1kN5&UrCEnfy=!}oDYIvYzL3CktjUt!QpsCT01Q>Ilk8;| z@Uu$GVfNe39ZS8<3fqK(tAbXuU^uC~-Mj6%CZXO?ziRMkb(g9q9rI6%KN(b&#Lj<1G{v>cFJ6xSc@nYgGKc@?cDQENFoiR{pYegNSZfR2;`^=8nz)`u8H-7 z@{_TqgOe`{?%AzHnr)a-embB4_a>Kq1Wt3UIHr8uZlf)H+)6LX&Aj0p7%XRk}C=nu2d9tgvN!PL_4B6@if07M36kQwvR z@--s~@i&P>C?PVo{#8YcM;Rk7+mXy6luLL)Bh0?{GoC1(+~)gTRZ2Z6qqEOx$qKNH}k6lV&= zKZb7o>+?u8dZsaYe%Gmxn5MZw776s8q~SR_cUk!Kr z?W)}P#{WEgQi6cC_c;;XsLRS$BI$Zs=yn-ne#<{X!AlZJcWD%xo`pZ}P*;NoY%h?BoA#*Cod_$A$B5my+3Yx^9{`1dy@cZ%=PNiJdSsKz0)Fl=7)1kFpmGXE-hdeHMsA z8>y1xLgnBJ$k2)P%yOQX=?s8J!mIn^{kBKnK%Il!q5K6=Sg5MaPq1NaC1KT z&SlpzL;K5Ln$p-lH0f6~OU~b2gq4H>ejUo(w6gHK92npOllbdvJ4TaZ9D?r#l3NaK z_+xw3_c_wkxu>NL*CF@jIOr5P6tXfu9l9_nT41vm2r`kR01yD@0|JGO%7^D%9hw`y z;;j+RG|5W_T^h*+Z{YnWG~hsMr#hF$`~H91T=1<#70eakG=?{R74sUiiqZgqfou%4 zf460P>RWmAuX))6)Q!w8!llr14DdSZ z#IjQC?j#E+%A+#MvGbBWk~riq9sY8hu<=&Z?t0G_VF#%)@vV%+*BR(1Jh#!?(bO68 zr%YtJl=(ZzS@~%w=BnN0u-mn+k|KfGRBAY;EOIesH}8#tGt1mPH~#;aanc)Yw zQ>kBZ=+D9qH{z{{@;AaZ>A;^Qn4g=ySKwykf+aG57XEQOh*S6@mqHD>#SGwb+G_6K zMCYEFE^)}Ju75yUWh{8dZoO4W%HK&Ck1THL;<>O=k;D0Yly&h4dUx8wFzekCJ(_gxHP7WH5a)z&Q$VD~L!Og?DKurTlaOI6ra5 zhv4WVgIeOR5B;~4Y?2lKRUToZ;e3wEsRM!IOTb|-Tf)ts1fjzwesR92b?r5R{d{;e zvS25DP=wKL<3SMg3ZY=`H_?OAK7534C>VFDLQxrO==Agi36*6uy6LmWNFm(cS{;zu z$d9NXbsd*(O-%edAC4m8j$Y(kWc^hSXdV>oG`1!Jq{5}toj9wwGdh=m7V!p_Jmw;S zj$S&TbfkXw_1d`@8-cYK2MEKhTXf4=do!V6p+CYCv&WzEvdTM)z5bL|%30r~F(Rnh ze3ox~!rR!&;8<%(^XuzcNjoX7W@TXje807o+ccw25q_v8x2pd$b*-cTfsgIzeYcrz zT2SvSRa?!@k%e5w-IQ4(^h1HRRsUROt47KAx}Seq>ik%w&B(B)>B(dK`2CtfTVf-f zi~cWTsSNAO zPOzcc#E`I63AK6VMF*m#-3)0OlJsoR7C#y^lIH%9*U z*!uBI`r7;mtAZ=sbIvA4S_!M@m|tIQK5vGYKi*(Wu?CId_#xJC4^+ZCO0@J6%Z%@d zCR(c4%xHWM)0%npyre-I8(qWEv02^>%frRy*oug+H^9ULuZ zwKT>=)>jbueyLj6)>PT1oq>~8j*?l)9F>&hNg{fFZav;Kit8Pgdc_kR2Cjhx`paI0 zZqmPdfvxpR$M(z%omPxZpJk|qae+M1!qgwIP>pdtHaOOI#VS4ObrWX@`5X3cT_>%k zJc48!*OdhGR-(Ze_h=?rBNhgRpq+O&qW9`eAO8#(v;9Z0W$05=z4H=aVnT*i(~x!; zUKblJKl_@xZKJM^7i=&OCr$_{InHdUQrmeKwzpNB$(ND3WLvBrWitN7sD@Q;S~%=C`qXQ=>8UM7r9G?V|3DLMAtPFqlAjOKY%F@6)^ zf?{?)*>pLr?>77SM+`P&hV^oNhR<5AFDFLpI*YFP38~(sTJJW8el6`)O3gRhx_sxt zD2v79_nNtpJ|&fq+53ybhpJXlc7!kO3?f?&+<;TxeA{BSrK|}m?q9o-zNDx;(I(9i z&heOJ{TTt{6ng7;_%Or41@vbmn9lGFu)!8>1oy`txP6F3pgeP|lp znu|^}Egu(ApRC|=5wzvYzIH&2!IpGCWfx!JEp_;zJsbUg`aUd8yRQEC`|^y#wbQ;r z<1UdShdY(3{^{~{3k6|gHw~NhZwM*Kfji}?-pl2O?h6kk?dPq``PpMBUP;?wu-eZP zF!87Tr?yN7YN0#!BTxlx_ao|Gnmx;1rP_&=i!tOQiEyen(?C`g^oHyA3X@%>@(IOZ zUZ)1Jn3XKLwqBkPO3L#-q*M`FxTI6Lg?_dlZ8ZvLCi`u^8x&(={g%m4a%8cX#-HN6 z>#YjCYa7PKFd)p;ry+1$cx~m&T&uqFByd}%r}1Z%-Atz!xOYAOi937G7Ddu%Kb`C4 z3m)}UOqJG@?}t6LXTy7ES3qxw$9!o|)V)sBdLf_n@}vx*#yMeR1;cJ2t921f7hKaW z#W8Ml0#=_B6V@={-8+xD6b`i6*q7l)etEb?>luD>d?;OH=HO&M70CMJZagTAi+!6e zNSXcBT96K91?!d`Syn^^gJ*aqU_VJWkCLf3KbP`g%a9FRc_)pF)sbyS%9fkNMKsdgH`}X;@XuD@3eU3BHh1xX_?>*uCHI zBE+1Hp^J0QpBmSg+qMUzs;};(pcT>rVhD)-Z@{64G*1-@Mlj5 zY9zHoa?02ziARONLVUZScPUgC<5=q^%e`?#edf>#Kco?`h$1}6I+Ei);)V9dbMjYT zDm$l!IWfx~@NT8|3B{fl-$Z_}KUgVxW#B;}Pptu3ejIYyxv{7>Kjh)qgS(-+{jHpw zY7BU_GH-C_yz@o3-`ZD9Dcdem|X*ApGLO9@V+xpxZBUHh+W2m7SVkUcvE54FK1+cO*a z5%ZKKl|=_TX<$Tgqqn7HpFkHjU($mn35?1$UO~mb1@%~e6yCx-S|MKP_pp>DEP1Cp z)a8&9SmMcR-ut;);!QWZ$n1uucE=>)WlLkv$J#sp4p2Cy2bNXjH;VJXA3ykmf50Dz zjvsITfq+e3p|@8Q$N6=TlDp{-?UYfUs1I~*gLiTRujFLILoy|~wL5?dcr5c0(c|T> z>*J$JY*N3r>-Z_e;`Xcd@~LnO>F=7nSDLzOZ+CT?WtoFw z%tvK}74IAGMO)9XotnX_e@(a+=Bj`nsc>OjBWF(uGomje8*$k|4Sk!A_qbMKySMOl@lLy_ z>H9+}R0@daC3TA+5z1o+wNU9`=5=Mm=Vavq9PGGPLh&qYsb%OAR(khc4S+blM)Isg z9nE}|LsM&d3DeCS3s>eW64z)>KSz9Ut9m-~iJ4vZLbY7UeOHT(El5+z={gj60X1ef zJB|aq539`;TjMudz<6H)>y(<7YNqd%pB*qqS12KmL& zz0q%xF6eQ|K7ABSeqs>DQ()QuZrdTrBmlJn-_$V2KC;j^55<`WpLKn@F1V2&dD64t zXHm}pMgb*=m#!6JSAL9Bw&i**Im>LkaeF12Th&u;fmnh_(UWJVA@u3>Q{6S5V2N|~ zh!aO^Q8INSQ?B-q{T|n++EL_R0?kFZyVSn=P5uKNP)&-XAY`SGV*$%-&-hQWf*W33 z(U6Vy_L8xH8DkSF=`B3_+zny9TKC6_*@6UFr*B{l+F z5dZFr^-+jBj{|*@H9arnU@#?fL;E-n7xSj(&G>^-8(D24B3OqTzA5HnL#wOhrK5&0{&g4&(x*!nsd^KT!7dOdOEkbBjfE6Q}h3PP2+JH@pg(<*ag zKWg{#dNXPap&>W z3}72_i&OwvcW@>pZxQXA63|~{FY4iNvDRR$CIf#}gU5j7Z;H|8j1cL6RUW(pAf%lwYDPaor)2X zQYPObi!=YRjrOIaAOZk57-RnRt;0WUqyC@1viz^f_#pmx;SLyOj|?^H)f`^@ EAIOf~&j0`b literal 0 HcmV?d00001 diff --git a/views/panel.html b/views/panel.html index 8b19a3f..7bf8042 100644 --- a/views/panel.html +++ b/views/panel.html @@ -1,6 +1,6 @@
- +