-
Notifications
You must be signed in to change notification settings - Fork 20
Run and show test results in the editor #8
Changes from 4 commits
eb14239
4d02424
ca43e68
f58fde7
d1ac430
7986bbd
7d8bec9
b872b85
77e54ee
596f5e5
4fabeec
ddc1f8e
af98d9a
469e2bf
054827a
c9ea899
3955950
2f5319d
86b5ed2
f71d3ab
f35825a
f8675ed
33d8d6e
90b97ad
ec2f444
b4868b2
e6a3385
b9a7069
e7fe057
bc8a08b
f8135c1
dbe0a32
83c0d2c
267805e
27304ac
4059434
87ce941
786465a
a35c73d
dc5cc76
4458a8a
b2d0b8b
6d6449a
7d3aa5e
099a5ff
2bd121b
71b5372
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
'atom-workspace': | ||
'ctrl-alt-a': 'ava:toggle' | ||
'ctrl-alt-x': 'ava:run' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there really any point in having two separate shortcuts? I would think it's enough with just Ctrl+Alt+A which toggles and runs tests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no big reason for having two today. I used two because they allowed me to easily test in isolation the two steps. I'll compact them in one. If in the future we have a parsing of the test file (something that I'd like to discuss after having a nice stable first version) phase we'll need two. No need to worry about this now. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
{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() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
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" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be better to use the AVA programmatic API. Example: https://github.com/pine613/fly-ava/blob/master/index.js There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Of course! Will do!. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sindresorhus, sorry if I'm missing something here. When trying to use the programmatic API providing an absolute path for running a test I'm getting 'No tests found in ../x/y.js, make sure to import "ava" at the top of your test file'. Same result if I use the CLI in a folder different from the one that hosts the file, as:
I have been able to trace the code till test-worker.js but being honest, I don't know why it's failing. Any idea?. Maybe I'm doing something wrong... Thank you! :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AVA 0.13.0 changed the API regarding how files are added. Ensure you pass them to the
I can reproduce too. Would you mind opening an issue on AVA?
As a dirty workaround, for now you could probably do this sindresorhus/atom-linter-xo@5d85618. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No worries, I will open the issue!. I thought that it was more likely to be something wrong with my environment. I saw the change in the API and was providing the files to the run() method. I'll take a look at the workaround. Thanks :). |
||
|
||
@terminalCommandExecutor.run(command, folder) | ||
|
||
addAvaOutput: (data) -> | ||
@parser.write(data) | ||
|
||
endAvaOutput: -> | ||
@parser.end() | ||
|
||
getParser: -> | ||
Parser() |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
"name": "ava", | ||
"version": "0.2.0", | ||
"description": "Snippets for AVA", | ||
"main": "./lib/main", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just rename There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not working for me... not sure if I'm missing something. |
||
"license": "MIT", | ||
"repository": "sindresorhus/atom-ava", | ||
"private": true, | ||
|
@@ -28,13 +29,20 @@ | |
"scripts": { | ||
"test": "xo" | ||
}, | ||
"activationCommands": { | ||
"atom-workspace": "ava:toggle" | ||
}, | ||
"keywords": [ | ||
"snippets", | ||
"test", | ||
"runner", | ||
"ava", | ||
"mocha" | ||
], | ||
"dependencies": { | ||
"event-kit": "^1.1.0", | ||
"tap-parser": ">=v1.2.2" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
}, | ||
"devDependencies": { | ||
"xo": "*" | ||
}, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
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() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
@import "ui-variables"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. single-quotes |
||
|
||
.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;} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use tab indentation |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use a JSON file here instead?