├── .gitignore ├── .travis.yml ├── ISSUE_TEMPLATE.md ├── lib ├── utility.coffee ├── command.coffee ├── ruby-test-view.coffee ├── test-runner.coffee ├── ruby-test.coffee └── source-info.coffee ├── spec ├── utility-spec.coffee ├── ruby-test-spec.coffee ├── test-runner-spec.coffee ├── command-spec.coffee ├── ruby-test-view-spec.coffee └── source-info-spec.coffee ├── menus └── ruby-test.cson ├── keymaps └── ruby-test.cson ├── styles └── ruby-test.less ├── LICENSE.md ├── package.json ├── doc ├── switching-settings-between-projects.md └── running_against_vm.md ├── README.md └── CHANGELOG.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | 3 | notifications: 4 | email: 5 | on_success: never 6 | on_failure: change 7 | 8 | script: 'curl -s https://raw.githubusercontent.com/atom/ci/master/build-package.sh | sh' 9 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Note:** before you submit an issue, you should be aware that the maintainer 2 | of this project (github.com/moxley) is looking for a replacement maintainer, and he is unlikely 3 | to respond to your issue. With that said, don't let that stop you from 4 | creating an issue. If you are interested in maintaining this project, please 5 | contact Moxley at `@moxicon` on Twitter. 6 | -------------------------------------------------------------------------------- /lib/utility.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | class Utility 3 | saveFile: -> 4 | @editor().save() if @filePath() 5 | 6 | activePath: -> 7 | @filePath() or @treeViewSelectedPath() 8 | 9 | filePath: -> 10 | @editor() and 11 | @editor().buffer and 12 | @editor().buffer.file and 13 | @editor().buffer.file.path 14 | 15 | treeViewSelectedPath: -> 16 | @treeView().mainModule.treeView.selectedPath if @treeView() 17 | 18 | editor: -> 19 | atom.workspace.getActiveTextEditor() 20 | 21 | treeView: -> 22 | atom.packages.getActivePackage('tree-view') 23 | -------------------------------------------------------------------------------- /lib/command.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | # Calculates test command, based on test framework and test scope 3 | class Command 4 | @testCommand: (scope, testFramework) -> 5 | if scope == "single" 6 | @testSingleCommand(testFramework) 7 | else if scope == "file" 8 | @testFileCommand(testFramework) 9 | else if scope == "all" 10 | @testAllCommand(testFramework) 11 | else 12 | throw "Unknown scope: #{scope}" 13 | 14 | @testFileCommand: (testFramework) -> 15 | atom.config.get("ruby-test.#{testFramework}FileCommand") 16 | 17 | @testAllCommand: (testFramework) -> 18 | atom.config.get("ruby-test.#{testFramework}AllCommand") 19 | 20 | @testSingleCommand: (testFramework) -> 21 | atom.config.get("ruby-test.#{testFramework}SingleCommand") 22 | -------------------------------------------------------------------------------- /spec/utility-spec.coffee: -------------------------------------------------------------------------------- 1 | Utility = require '../lib/utility' 2 | 3 | describe "Utility", -> 4 | describe "::saveFile", -> 5 | makeEditor = (opts) -> 6 | editor = { 7 | save: null 8 | buffer: {file: {path: opts.path}} 9 | } 10 | spyOn(editor, 'save') 11 | spyOn(atom.workspace, 'getActiveTextEditor').andReturn(editor) 12 | editor 13 | 14 | it "calls save() on the active editor file", -> 15 | editor = makeEditor(path: 'foo/bar.rb') 16 | 17 | util = new Utility 18 | util.saveFile() 19 | 20 | expect(editor.save).toHaveBeenCalled() 21 | 22 | it "does not call save() when there is no file path", -> 23 | editor = makeEditor(path: null) 24 | 25 | util = new Utility 26 | util.saveFile() 27 | 28 | expect(editor.save).not.toHaveBeenCalled() 29 | -------------------------------------------------------------------------------- /spec/ruby-test-spec.coffee: -------------------------------------------------------------------------------- 1 | RubyTest = require '../lib/ruby-test' 2 | RubyTestView = require '../lib/ruby-test-view' 3 | 4 | # Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs. 5 | # 6 | # To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit` 7 | # or `fdescribe`). Remove the `f` to unfocus the block. 8 | 9 | describe "RubyTest", -> 10 | workspaceElement = null 11 | 12 | beforeEach -> 13 | workspaceElement = atom.views.getView(atom.workspace) 14 | 15 | describe "when the ruby-test:test-file event is triggered", -> 16 | it "displays terminus", -> 17 | # This is an activation event, triggering it will cause the package to be 18 | # activated. 19 | atom.commands.dispatch workspaceElement, 'ruby-test:test-file' 20 | 21 | runs -> 22 | atom.packages.activatePackage('terminus').then -> 23 | expect(RubyTestView.prototype.initialize).toHaveBeenCalled() 24 | -------------------------------------------------------------------------------- /menus/ruby-test.cson: -------------------------------------------------------------------------------- 1 | # See https://atom.io/docs/latest/creating-a-package#menus for more details 2 | 'context-menu': 3 | '.overlayer': [ 4 | { 'label': 'Run ruby-test for file', 'command': 'ruby-test:test-file' } 5 | { 'label': 'Run ruby-test at line', 'command': 'ruby-test:test-single' } 6 | { 'label': 'Run ruby-test for all tests', 'command': 'ruby-test:test-all' } 7 | { 'label': 'Run ruby-test for previous tests', 'command': 'ruby-test:test-previous' } 8 | ] 9 | 10 | 'menu': [ 11 | { 12 | 'label': 'Packages' 13 | 'submenu': [ 14 | 'label': 'Ruby Test' 15 | 'submenu': [ 16 | { 'label': 'Run test file', 'command': 'ruby-test:test-file' } 17 | { 'label': 'Run single test', 'command': 'ruby-test:test-single' } 18 | { 'label': 'Run all tests', 'command': 'ruby-test:test-all' } 19 | { 'label': 'Run previous tests', 'command': 'ruby-test:test-previous' } 20 | ] 21 | ] 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /keymaps/ruby-test.cson: -------------------------------------------------------------------------------- 1 | # Keybindings require three things to be fully defined: A selector that is 2 | # matched against the focused element, the keystroke and the command to 3 | # execute. 4 | # 5 | # Below is a basic keybinding which registers on all platforms by applying to 6 | # the root workspace element. 7 | 8 | # For more detailed documentation see 9 | # https://atom.io/docs/latest/advanced/keymaps 10 | '.platform-darwin atom-workspace': 11 | 'cmd-ctrl-x': 'ruby-test:toggle' 12 | 'cmd-ctrl-y': 'ruby-test:test-all' 13 | 'cmd-ctrl-t': 'ruby-test:test-file' 14 | 'cmd-ctrl-r': 'ruby-test:test-single' 15 | 'cmd-ctrl-e': 'ruby-test:test-previous' 16 | 'cmd-ctrl-c': 'ruby-test:cancel' 17 | 18 | '.platform-linux atom-workspace': 19 | 'ctrl-shift-c': 'ruby-test:cancel' 20 | 'ctrl-shift-e': 'ruby-test:test-previous' 21 | 'ctrl-shift-r': 'ruby-test:test-single' 22 | 'ctrl-shift-t': 'ruby-test:test-file' 23 | 'ctrl-shift-x': 'ruby-test:toggle' 24 | 'ctrl-shift-y': 'ruby-test:test-all' 25 | -------------------------------------------------------------------------------- /styles/ruby-test.less: -------------------------------------------------------------------------------- 1 | // The ui-variables file is provided by base themes provided by Atom. 2 | // 3 | // See https://github.com/atom/atom-dark-ui/blob/master/stylesheets/ui-variables.less 4 | // for a full listing of what's available. 5 | @import "ui-variables"; 6 | 7 | .ruby-test { 8 | left: 0; 9 | right: 0; 10 | margin: 0; 11 | width: auto; 12 | } 13 | 14 | .ruby-test-resize-handle { 15 | position: absolute; 16 | top: 0; 17 | bottom: 0; 18 | height: 10px; 19 | width: 100%; 20 | cursor: row-resize; 21 | z-index: 3; 22 | } 23 | 24 | .ruby-test .panel-body { 25 | height: 200px; 26 | overflow-y: scroll; 27 | font-family: monospace; 28 | } 29 | 30 | .ruby-test-spinner { 31 | margin: auto; 32 | margin-top: 20px; 33 | background-image: url(images/octocat-spinner-128.gif); 34 | background-repeat: no-repeat; 35 | background-size: 64px; 36 | background-position: top center; 37 | padding-top: 70px; 38 | text-align: center; 39 | } 40 | 41 | .ruby-test .heading-close { 42 | cursor: pointer; 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Moxley Stratton 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ruby-test", 3 | "author": "Moxley Stratton", 4 | "homepage": "https://github.com/moxley/atom-ruby-test", 5 | "bugs": "https://github.com/moxley/atom-ruby-test/issues", 6 | "main": "./lib/ruby-test", 7 | "version": "1.0.2", 8 | "description": "Run Ruby tests, Rspec examples, and Cucumber features from Atom", 9 | "activationCommands": { 10 | "atom-workspace": [ 11 | "ruby-test:toggle", 12 | "ruby-test:test-file", 13 | "ruby-test:test-single", 14 | "ruby-test:test-previous", 15 | "ruby-test:test-all" 16 | ] 17 | }, 18 | "repository": "https://github.com/moxley/atom-ruby-test", 19 | "license": "MIT", 20 | "engines": { 21 | "atom": ">=1.2.0 <2.0.0" 22 | }, 23 | "dependencies": { 24 | "ansi-to-html": "^0.2", 25 | "atom-space-pen-views": "^2.0.3", 26 | "atom-package-deps": "^4.3" 27 | }, 28 | "package-deps": [ 29 | "terminus" 30 | ], 31 | "keywords": [ 32 | "ruby", 33 | "test", 34 | "rspec", 35 | "spec", 36 | "cucumber" 37 | ], 38 | "consumedServices": { 39 | "terminusTerminal": { 40 | "versions": { 41 | "1.1.x": "consumeRunInTerminal" 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /doc/switching-settings-between-projects.md: -------------------------------------------------------------------------------- 1 | ## How To Switch Settings Between Projects 2 | 3 | Use this technique to switch to a different project and have 4 | your ruby-test settings change with it. 5 | 6 | 1. Install the `project-manager` Atom package. This gives you project-specific 7 | settings for any Atom package, not just ruby-test. 8 | 2. Open the project you want different ruby-test settings for. 9 | 3. Execute the "Project Manager: Edit Project" (singular) Atom command 10 | (Press `⇧⌘P` Mac to access the commands). 11 | 4. Fill out the fields for your project. 12 | 5. Execute the "Project Manager: Edit Projects" (plural) command. This will 13 | open a CSON file containing project settings of all your projects. 14 | 6. Find your project, and add this bit of code to it: 15 | ``` 16 | settings: 17 | "ruby-test": 18 | specFramework: "rspec" 19 | minitestFileCommand: "bundle exec m {relative_path}" 20 | minitestSingleCommand: "bundle exec m {relative_path}:{line_number}" 21 | minitestAllCommand: "bundle exec m spec/*" 22 | ``` 23 | 7. Change the code above to the values needed for your project. 24 | 8. Now, next time you want to open your project, execute the 25 | "Project Manager: List Projects" command (`^⌘P` Mac), and enter your 26 | project's name. When it opens, it'll have the correct ruby-test settings. 27 | -------------------------------------------------------------------------------- /doc/running_against_vm.md: -------------------------------------------------------------------------------- 1 | ### Using ruby-test with a Ruby project running on Vagrant or other VM 2 | 3 | **Warning:** This is kind of an insecure setup. 4 | 5 | On the VM, create a server that accepts and runs shell commands. For 6 | example, create this command (e.g., `~/bin/command_server`): 7 | 8 | ```ruby 9 | #!/usr/bin/env ruby 10 | 11 | require 'socket' 12 | require 'open3' 13 | 14 | port = ARGV[0] || 2000 15 | puts "Starting server on port #{port}" 16 | server = TCPServer.new(port.to_i) # Server bind to port 2000 17 | loop do 18 | client = server.accept # Wait for a client to connect 19 | cmd = client.gets 20 | client.puts "Running command: #{cmd}" 21 | 22 | Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr| 23 | stdin.close 24 | while !stderr.eof? || !stdout.eof? 25 | client.print stderr.gets 26 | client.print stdout.gets 27 | end 28 | end 29 | end 30 | ``` 31 | 32 | Start the server: `~/bin/command_server` from your project's directory in the Vagrant VM. 33 | 34 | In the ruby-test settings, wrap each test command in an `echo ''`, and append `| nc VM_ADDR 2000`. Replace `VM_ADDR` with the IP address or hostname of the 35 | VM. `nc` (netcat) will forward the test commands to the server, which will 36 | then execute the commands. 37 | 38 | For example: 39 | 40 | rspecFileCommand: 41 | 42 | ``` 43 | echo 'bundle exec spring rspec {relative_path}' | nc 10.11.12.13 2000 44 | ``` 45 | -------------------------------------------------------------------------------- /lib/ruby-test-view.coffee: -------------------------------------------------------------------------------- 1 | {$,View} = require 'atom-space-pen-views' 2 | TestRunner = require './test-runner' 3 | Utility = require './utility' 4 | SourceInfo = require './source-info' 5 | Convert = require 'ansi-to-html' 6 | 7 | module.exports = 8 | class RubyTestView extends View 9 | @content: -> 10 | @div class: "ruby-test inset-panel panel-bottom native-key-bindings", tabindex: -1, => 11 | 12 | initialize: (serializeState, terminal) -> 13 | @terminal = terminal; 14 | sourceInfo = new SourceInfo() 15 | atom.commands.add "atom-workspace", "ruby-test:toggle", => @toggle() 16 | atom.commands.add "atom-workspace", "ruby-test:test-file", => @testFile() 17 | atom.commands.add "atom-workspace", "ruby-test:test-single", => @testSingle() 18 | atom.commands.add "atom-workspace", "ruby-test:test-previous", => @testPrevious() 19 | atom.commands.add "atom-workspace", "ruby-test:test-all", => @testAll() 20 | atom.commands.add "atom-workspace", "ruby-test:cancel", => @cancelTest() 21 | 22 | # Returns an object that can be retrieved when package is activated 23 | serialize: -> 24 | 25 | # Tear down any state and detach 26 | destroy: -> 27 | @output = '' 28 | @detach() 29 | 30 | closePanel: -> 31 | if @hasParent() 32 | @detach() 33 | 34 | currentEditor: -> 35 | atom.views.getView(atom.workspace.getActiveTextEditor()) 36 | 37 | toggle: -> 38 | atom.commands.dispatch(@currentEditor(), 'terminus:toggle') 39 | 40 | testFile: -> 41 | @runTest(testScope: "file") 42 | 43 | testSingle: -> 44 | @runTest(testScope: "single") 45 | 46 | testAll: -> 47 | @runTest(testScope: "all") 48 | 49 | testPrevious: -> 50 | return unless @runner 51 | @saveFile() 52 | @runner.run() 53 | 54 | runTest: (params) -> 55 | @saveFile() 56 | @runner = new TestRunner(params, @terminal) 57 | @runner.run() 58 | 59 | onTestRunEnd: => 60 | null 61 | 62 | showPanel: -> 63 | unless @hasParent() 64 | atom.workspace.addBottomPanel(item: @) 65 | @spinner = @find('.ruby-test-spinner') 66 | 67 | cancelTest: -> 68 | atom.commands.dispatch(@currentEditor(), 'terminus:close') 69 | 70 | saveFile: -> 71 | util = new Utility 72 | util.saveFile() 73 | -------------------------------------------------------------------------------- /spec/test-runner-spec.coffee: -------------------------------------------------------------------------------- 1 | TestRunner = require '../lib/test-runner' 2 | SourceInfo = require '../lib/source-info' 3 | Command = require '../lib/command' 4 | 5 | describe "TestRunner", -> 6 | 7 | beforeEach -> 8 | @mockRun = jasmine.createSpy('run') 9 | @mockTerminalInput = jasmine.createSpy('input') 10 | @mockOpen = jasmine.createSpy('open') 11 | @mockGetTerminalViews = [ { input: @mockTerminalInput, open: @mockOpen } ] 12 | @mockTerminal = { 13 | run: @mockRun, getTerminalViews: => @mockGetTerminalViews 14 | } 15 | @testRunnerParams = {} 16 | 17 | spyOn(SourceInfo.prototype, 'activeFile').andReturn('fooTestFile') 18 | spyOn(SourceInfo.prototype, 'currentLine').andReturn(100) 19 | spyOn(SourceInfo.prototype, 'minitestRegExp').andReturn('test foo') 20 | spyOn(Command, 'testFileCommand').andReturn('fooTestCommand {relative_path}') 21 | spyOn(Command, 'testSingleCommand').andReturn('fooTestCommand {relative_path}:{line_number}') 22 | 23 | describe "::run", -> 24 | it "constructs a single-test command when testScope is 'single'", -> 25 | @testRunnerParams.testScope = "single" 26 | runner = new TestRunner(@testRunnerParams, @mockTerminal) 27 | runner.run() 28 | expect(@mockRun).toHaveBeenCalledWith(["fooTestCommand fooTestFile:100"]) 29 | expect(@mockOpen).not.toHaveBeenCalled() 30 | expect(@mockTerminalInput).not.toHaveBeenCalled() 31 | 32 | it "constructs a single-minitest command when testScope is 'single'", -> 33 | Command.testSingleCommand.andReturn('fooTestCommand {relative_path} -n \"/{regex}/\"') 34 | @testRunnerParams.testScope = "single" 35 | runner = new TestRunner(@testRunnerParams, @mockTerminal) 36 | runner.run() 37 | expect(@mockRun).toHaveBeenCalledWith(["fooTestCommand fooTestFile -n \"/test foo/\""]) 38 | 39 | it "reuses the previous terminal used for testing", -> 40 | @testRunnerParams.testScope = "single" 41 | runner = new TestRunner(@testRunnerParams, @mockTerminal) 42 | runner.run() 43 | runner = new TestRunner(@testRunnerParams, @mockTerminal) 44 | runner.run() 45 | expect(@mockOpen).toHaveBeenCalled() 46 | expect(@mockTerminalInput).toHaveBeenCalledWith("fooTestCommand fooTestFile:100\n") 47 | -------------------------------------------------------------------------------- /lib/test-runner.coffee: -------------------------------------------------------------------------------- 1 | SourceInfo = require './source-info' 2 | Command = require './command' 3 | Utility = require './utility' 4 | 5 | module.exports = 6 | class TestRunner 7 | constructor: (params, terminal) -> 8 | @initialize(params, terminal) 9 | 10 | initialize: (params, terminal) -> 11 | @terminal = terminal 12 | @params = params 13 | @sourceInfo = new SourceInfo() 14 | @utility = new Utility 15 | 16 | run: -> 17 | if atom.packages.isPackageDisabled('terminus') 18 | alert("Terminus package is disabled. It must be enabled to run tests.") 19 | return 20 | 21 | @returnFocusToEditorAfterTerminalRun(); 22 | terminals = @terminal.getTerminalViews() 23 | if (@terminal.lastTerminalForTestIdx != null && terminals[@terminal.lastTerminalForTestIdx]) 24 | lastTerminalForTest = terminals[@terminal.lastTerminalForTestIdx] 25 | lastTerminalForTest.open() 26 | lastTerminalForTest.input(@command() + "\n") 27 | else 28 | @terminal.run([@command()]) 29 | @terminal.lastTerminalForTestIdx = @terminal.getTerminalViews().length - 1 30 | 31 | returnFocusToEditorAfterTerminalRun: => 32 | editor = @utility.editor() 33 | 34 | # If no files are open (we're running the entire suite) then 35 | # there is no cursor to return to 36 | return unless editor? 37 | 38 | cursorPos = editor.getCursorBufferPosition() 39 | 40 | # It appears that the terminal does not have create a panel until the 41 | # animation is complete. We need to wait for it to finish before we can 42 | # blur it to return focus 43 | setTimeout -> 44 | editor.setCursorBufferPosition(cursorPos, {autoscroll: true}) 45 | panels = atom.workspace.getBottomPanels(); 46 | for panel in panels 47 | if panel.getItem().hasClass?('terminus') 48 | panel.getItem().blur() 49 | 50 | editor.getElement().focus() 51 | , 700 52 | 53 | 54 | command: => 55 | framework = @sourceInfo.testFramework() 56 | cmd = Command.testCommand(@params.testScope, framework) 57 | cmd.replace('{relative_path}', @sourceInfo.activeFile()). 58 | replace('{line_number}', @sourceInfo.currentLine()). 59 | replace('{regex}', @sourceInfo.minitestRegExp()) 60 | -------------------------------------------------------------------------------- /spec/command-spec.coffee: -------------------------------------------------------------------------------- 1 | Command = require '../lib/command' 2 | 3 | # Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs. 4 | # 5 | # To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit` 6 | # or `fdescribe`). Remove the `f` to unfocus the block. 7 | 8 | describe "Command", -> 9 | describe "testSingleCommand", -> 10 | it "returns the ruby-test.rspecSingleCommand config value for the rspec framework", -> 11 | atom.config.set("ruby-test.rspecSingleCommand", "rspec command") 12 | actualValue = Command.testSingleCommand("rspec") 13 | expect(actualValue).toBe("rspec command") 14 | 15 | it "returns undefined when given an unrecognized framework", -> 16 | actualValue = Command.testSingleCommand("unknown") 17 | expect(actualValue).toBe(undefined) 18 | 19 | describe "testFileCommand", -> 20 | it "returns the ruby-test.rspecFileCommand config value for the rspec framework", -> 21 | atom.config.set("ruby-test.rspecFileCommand", "rspec command") 22 | actualValue = Command.testFileCommand("rspec") 23 | expect(actualValue).toBe("rspec command") 24 | 25 | it "returns the ruby-test.minitestFileCommand config value for the minitest framework", -> 26 | atom.config.set("ruby-test.minitestFileCommand", "minitest command") 27 | actualValue = Command.testFileCommand("minitest") 28 | expect(actualValue).toBe("minitest command") 29 | 30 | it "returns the ruby-test.cucumberFileCommand config value for the cucumber framework", -> 31 | atom.config.set("ruby-test.cucumberFileCommand", "cucumber command") 32 | actualValue = Command.testFileCommand("cucumber") 33 | expect(actualValue).toBe("cucumber command") 34 | 35 | it "returns undefined when given an unrecognized framework", -> 36 | actualValue = Command.testFileCommand("unknown") 37 | expect(actualValue).toBe(undefined) 38 | 39 | describe "testAllCommand", -> 40 | it "returns the ruby-test.rspecAllCommand config value for the rspec framework", -> 41 | atom.config.set("ruby-test.rspecAllCommand", "rspec command") 42 | actualValue = Command.testAllCommand("rspec") 43 | expect(actualValue).toBe("rspec command") 44 | 45 | it "returns undefined when given an unrecognized framework", -> 46 | actualValue = Command.testAllCommand("unknown") 47 | expect(actualValue).toBe(undefined) 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ruby-test package 2 | 3 | [![Build Status](https://api.travis-ci.org/moxley/atom-ruby-test.svg?branch=master)](https://travis-ci.org/moxley/atom-ruby-test) 4 | 5 | Run Ruby tests, RSpec examples, and Cucumber features from Atom, 6 | quickly and easily. 7 | 8 | --- 9 | 10 | ## Announcement 11 | 12 | This project is no longer maintained. I'm looking for someone to take over this project as a maintainer. I haven't used ruby-test for over a year. Contact me through Twitter direct message: @moxicon. 13 | 14 | --- 15 | 16 | ![Running tests is quick and easy](http://cl.ly/image/300n2g101z0y/ruby-test6.gif) 17 | 18 | ## Install 19 | 20 | In Atom's Settings, go to Packages, search for "Ruby Test". 21 | Click the `Install` button. 22 | 23 | ## Configure 24 | 25 | IMPORTANT: Before configuring ruby-test, toggle to the test panel to activate 26 | the package: `cmd-ctrl-x`. 27 | 28 | This relies on the 29 | [platformio-ide-terminal](https://github.com/platformio/platformio-atom-ide-terminal) 30 | package to run commands and has its own settings. Make sure, in its settings, the 31 | Working Directory is set to "Project". This is the package's default, but might be different if you already had the package installed. 32 | 33 | ![Ruby Test Settings view](http://cl.ly/image/1l3H0g1C1J3g/ruby-test-settings.png) 34 | 35 | ## Run 36 | 37 | Open the test file you want to run, then issue one of the following: 38 | 39 | * `cmd-ctrl-y` - Run all the test files 40 | * `cmd-ctrl-t` - Run the current test file 41 | * `cmd-ctrl-r` - Run the test at the current line in test file 42 | * `cmd-ctrl-e` - Re-run the previous test (doesn't need to have the test file active) 43 | 44 | ## Features 45 | 46 | * Run all the tests in the project 47 | * Run the tests for current test file 48 | * Run single test at cursor 49 | * Run previous test 50 | * Configure the shell commands that run the tests 51 | * Supports Ruby managers, like `rvm` and `rbenv` 52 | * Supports bash, z-shell, fish 53 | * Supports Test::Unit, RSpec, Minitest, and Cucumber 54 | 55 | ## Helpful Tips 56 | 57 | * Use ruby-test in conjunction with [project-manager](https://atom.io/packages/project-manager) 58 | to maintain different ruby-test settings per project. 59 | * [Run ruby-test on a project that runs in a VM or Vagrant](https://github.com/moxley/atom-ruby-test/blob/master/doc/running_against_vm.md) 60 | 61 | ## Contributing 62 | 63 | Any of the following are appreciated: 64 | 65 | * [Submit a Pull Request](https://github.com/moxley/atom-ruby-test/pulls) 66 | * [Submit an Issue](https://github.com/moxley/atom-ruby-test/issues) 67 | -------------------------------------------------------------------------------- /lib/ruby-test.coffee: -------------------------------------------------------------------------------- 1 | RubyTestView = require './ruby-test-view' 2 | 3 | module.exports = 4 | config: 5 | minitestAllCommand: 6 | title: "Minitest command: Run all tests" 7 | type: 'string' 8 | default: "ruby -I test test" 9 | minitestFileCommand: 10 | title: "Minitest command: Run test file" 11 | type: 'string' 12 | default: "ruby -I test {relative_path}" 13 | minitestSingleCommand: 14 | title: "Minitest command: Run current test" 15 | type: 'string' 16 | default: "ruby -I test {relative_path} -n \"/{regex}/\"" 17 | testAllCommand: 18 | title: "Ruby Test command: Run all tests" 19 | type: 'string' 20 | default: "ruby -I test test" 21 | testFileCommand: 22 | title: "Ruby Test command: Run test in file" 23 | type: 'string' 24 | default: "ruby -I test {relative_path}" 25 | testSingleCommand: 26 | title: "Ruby Test command: Run test at line number" 27 | type: 'string' 28 | default: "ruby -I test {relative_path}:{line_number}" 29 | rspecAllCommand: 30 | title: "RSpec command: run all specs" 31 | type: 'string', 32 | default: "rspec --tty spec" 33 | rspecFileCommand: 34 | title: "RSpec command: run spec file" 35 | type: 'string', 36 | default: "rspec --tty {relative_path}" 37 | rspecSingleCommand: 38 | title: "RSpec command: run spec at current line" 39 | type: 'string', 40 | default: "rspec --tty {relative_path}:{line_number}" 41 | cucumberAllCommand: 42 | title: "Cucumber command: Run all features" 43 | type: 'string', 44 | default: "cucumber --color features" 45 | cucumberFileCommand: 46 | title: "Cucumber command: Run features file" 47 | type: 'string', 48 | default: "cucumber --color {relative_path}" 49 | cucumberSingleCommand: 50 | title: "Cucumber command: Run features at current line" 51 | type: 'string', 52 | default: "cucumber --color {relative_path}:{line_number}" 53 | specFramework: 54 | type: 'string' 55 | default: '' 56 | enum: ['', 'rspec', 'minitest'] 57 | description: 'RSpec and Minitest spec files look very similar to each other, and ruby-test often can\'t tell them apart. Choose your preferred *_spec.rb framework.' 58 | testFramework: 59 | type: 'string' 60 | default: '' 61 | enum: ['', 'minitest', 'test'] 62 | description: 'Minitest test files and Test::Unit files look very similar to each other, and ruby-test often can\'t tell them apart. Choose your preferred *_test.rb framework.' 63 | 64 | rubyTestView: null 65 | 66 | activate: (@state) -> 67 | require('atom-package-deps').install('ruby-test') 68 | 69 | consumeRunInTerminal: (runInTerminalProvider) -> 70 | @rubyTestView = new RubyTestView(@state.rubyTestViewState, runInTerminalProvider) 71 | 72 | deactivate: -> 73 | @rubyTestView.destroy() 74 | 75 | serialize: -> 76 | rubyTestViewState: @rubyTestView.serialize() if @rubyTestView 77 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## Master 4 | 5 | * Use the terminus package to run commands 6 | 7 | ## Version 1.0.2 8 | 9 | * #101 Add ruby include path default test command 10 | 11 | ## Version 1.0.1 12 | 13 | * #99 Return focus after terminal loads 14 | 15 | ## Version 1.0.0 16 | 17 | * Use the platformio-ide-terminal package to run commands ([pr96](https://github.com/moxley/atom-ruby-test/pull/96)) 18 | 19 | ## Version 0.9.17 20 | 21 | * Fix broken test file path calculation for multi-path projects 22 | * Fix Minitest regular expression calculation for single test executions. 23 | 24 | ## Version 0.9.16 25 | 26 | * Fish support (#31, #72, #73) 27 | 28 | ## Version 0.9.15 29 | 30 | * Correctly determine framework when no test file is open (#70) 31 | 32 | ## Version 0.9.14 33 | 34 | * Correctly detect Cucumber feature file when project has .rspec file (#59) 35 | 36 | ## Version 0.9.13 37 | 38 | * Configurable spec framework and test framework (#57) 39 | 40 | ## Version 0.9.12 41 | 42 | * Ability to copy from output pane (#6) 43 | 44 | ## Version 0.9.11 45 | 46 | * Add test runner debug info to console 47 | 48 | ## Version 0.9.10 49 | 50 | * Fix rspec detection (#55) 51 | 52 | ## Version 0.9.9 53 | 54 | * Fix rspec detection (#54) 55 | 56 | ## Version 0.9.8 57 | 58 | * Minitest - run single test 59 | 60 | ## Version 0.9.7 61 | 62 | * Fix cursor function deprecation (#48) 63 | 64 | ## Version 0.9.6 65 | 66 | * Fix string replacement error (#44) 67 | * Handle special shell characters in cwd (#30) 68 | 69 | ## Version 0.9.5 70 | 71 | * Upgrade to Atom API 1.0 (#39) 72 | * Fix save-all without open file (#33) 73 | * Fix atom engine semver (#42) 74 | 75 | ## Version 0.9.4 76 | 77 | * Upgrade from deprecated style API (#36) 78 | * Fix failed spec (#35) 79 | 80 | ## Version 0.9.3 81 | 82 | * Cucumber color output 83 | 84 | ## Version 0.9.2 85 | 86 | * Linux key bindings 87 | 88 | ## Version 0.9.1 89 | 90 | * Fix switching shells 91 | * Fix shell startup invocation 92 | 93 | ## Version 0.9.0 94 | 95 | * Configurable shell. Supports bash, zsh. 96 | * Cancel running test with cmd+ctrl+c 97 | * Save current file before running test 98 | 99 | ## Version 0.8.0 100 | 101 | * Fix query for current row 102 | 103 | ## Version 0.7.0 104 | 105 | * Unbuffered, real-time output 106 | 107 | ## Version 0.6.0 108 | 109 | * ANSI color support 110 | 111 | ## Version 0.5.0 112 | 113 | * Run all tests 114 | * Auto scroll to bottom of results 115 | * Fix always show spinner when tests start 116 | * Fix double-click panel resize 117 | 118 | ## Version 0.4.1 119 | 120 | * Fix output area for resize 121 | * Fix scrolling of output area 122 | 123 | ## Version 0.4.0 124 | 125 | * Show spinner while waiting for tests to show something 126 | * Resizable panel 127 | 128 | ## Version 0.3.0 129 | 130 | * Restore lost changes from version 0.2.0 131 | 132 | ## Version 0.2.0 133 | 134 | * Fix missing settings fields 135 | * Fix default Cucumber command 136 | * README screenshots: primary GIF animation, Settings view 137 | * README sections: Install, Configure, Run, Features, Contributing 138 | 139 | ## Version 0.1.0 140 | 141 | * Initial release 142 | * Run tests in file 143 | * Run test at last 144 | * Run previous test 145 | * Toggle test panel 146 | * Support for TestUnit, Rspec, Cucumber 147 | * Support for Ruby managers (rvm, rbenv) 148 | * Configurable shell commands 149 | -------------------------------------------------------------------------------- /spec/ruby-test-view-spec.coffee: -------------------------------------------------------------------------------- 1 | {$$} = require 'atom-space-pen-views' 2 | RubyTestView = require '../lib/ruby-test-view' 3 | TestRunner = require '../lib/test-runner' 4 | 5 | describe "RubyTestView", -> 6 | activeEditor = null 7 | testRunnerInitializeParams = null 8 | view = null 9 | fileOpened = false 10 | terminalMock = null 11 | 12 | beforeEach -> 13 | mockGetTerminalViews = [ { terminal: closeBtn: { click: -> } } ] 14 | @terminalMock = { 15 | run: (commands) -> 16 | getTerminalViews: () => 17 | mockGetTerminalViews 18 | } 19 | view = new RubyTestView({}, @terminalMock) 20 | 21 | spyOnTestRunnerInitialize = -> 22 | spyOn(activeEditor, 'save') 23 | spyOn(TestRunner.prototype, 'initialize').andCallFake (params) -> 24 | testRunnerInitializeParams = params 25 | spyOn(TestRunner.prototype, 'run').andReturn(null) 26 | 27 | validateTestRunnerInitialize = -> 28 | expect(testRunnerInitializeParams).toBeDefined() 29 | expect(testRunnerInitializeParams).not.toBe(null) 30 | expect(testRunnerInitializeParams.write).toEqual(view.write) 31 | 32 | spyOnTestRunnerRun = -> 33 | if activeEditor? 34 | spyOn(activeEditor, 'save') 35 | spyOn(TestRunner.prototype, 'initialize').andCallThrough() 36 | spyOn(TestRunner.prototype, 'run').andCallThrough() 37 | spyOn(TestRunner.prototype, 'command').andReturn 'fooTestCommand' 38 | 39 | validateTestRunnerRun = -> 40 | expect(TestRunner.prototype.initialize).toHaveBeenCalled() 41 | expect(TestRunner.prototype.run).toHaveBeenCalledWith() 42 | expect(activeEditor.save).toHaveBeenCalled() 43 | 44 | setUpActiveEditor = -> 45 | atom.workspace.open('/tmp/text.txt').then (editor) -> 46 | activeEditor = editor 47 | fileOpened = true 48 | waitsFor -> fileOpened is true 49 | 50 | describe "with open editor", -> 51 | beforeEach -> 52 | fileOpened = false 53 | testRunnerInitializeParams = null 54 | activeEditor = null 55 | setUpActiveEditor() 56 | 57 | describe "::testAll", -> 58 | it "instantiates TestRunner with specific arguments", -> 59 | spyOnTestRunnerInitialize() 60 | 61 | view.testAll() 62 | 63 | validateTestRunnerInitialize() 64 | expect(testRunnerInitializeParams.testScope).toEqual('all') 65 | 66 | it "instantiates TestRunner and calls ::run on it", -> 67 | spyOnTestRunnerRun() 68 | 69 | view.testAll() 70 | 71 | validateTestRunnerRun() 72 | 73 | describe "::testFile", -> 74 | it "instantiates TestRunner with specific arguments", -> 75 | spyOnTestRunnerInitialize() 76 | 77 | view.testFile() 78 | 79 | validateTestRunnerInitialize() 80 | expect(testRunnerInitializeParams.testScope).toEqual('file') 81 | 82 | it "calls ::run on the TestRunner instance", -> 83 | spyOnTestRunnerRun() 84 | view.testFile() 85 | 86 | validateTestRunnerRun() 87 | 88 | describe "::testSingle", -> 89 | it "instantiates TestRunner with specific arguments", -> 90 | spyOnTestRunnerInitialize() 91 | 92 | view.testSingle() 93 | 94 | validateTestRunnerInitialize() 95 | expect(testRunnerInitializeParams.testScope).toEqual('single') 96 | 97 | it "instantiates TestRunner and calls ::run on it", -> 98 | spyOnTestRunnerRun() 99 | 100 | view.testSingle() 101 | 102 | validateTestRunnerRun() 103 | 104 | describe "::testPrevious", -> 105 | it "intantiates TestRunner and calls ::run on it with specific arguments", -> 106 | spyOn(activeEditor, 'save') 107 | 108 | previousRunner = new TestRunner({ scope: 'file' }, @terminalMock) 109 | previousRunner.command = -> "foo" 110 | view.runner = previousRunner 111 | view.testPrevious() 112 | 113 | expect(view.runner).toBe(previousRunner) 114 | expect(activeEditor.save).toHaveBeenCalled() 115 | 116 | describe "without open editor", -> 117 | beforeEach -> 118 | fileOpened = false 119 | testRunnerInitializeParams = null 120 | 121 | # Reproduce https://github.com/moxley/atom-ruby-test/issues/33: 122 | # "Uncaught TypeError: Cannot read property 'save' of undefined" 123 | describe "::testAll", -> 124 | it "instantiates TestRunner and calls ::run on it", -> 125 | spyOnTestRunnerRun() 126 | 127 | view.testAll() 128 | 129 | expect(TestRunner.prototype.initialize).toHaveBeenCalled() 130 | expect(TestRunner.prototype.run).toHaveBeenCalledWith() 131 | -------------------------------------------------------------------------------- /lib/source-info.coffee: -------------------------------------------------------------------------------- 1 | fs = require('fs') 2 | Utility = require './utility' 3 | 4 | module.exports = 5 | # Provides information about the source code being tested 6 | class SourceInfo 7 | frameworkLookup: 8 | test: 'test' 9 | spec: 'rspec' 10 | rspec: 'rspec' 11 | feature: 'cucumber' 12 | minitest: 'minitest' 13 | 14 | regExpForTestStyle: 15 | unit: /def\s(.*?)$/ 16 | spec: /(?:"|')(.*?)(?:"|')/ 17 | 18 | projectPath: -> 19 | defaultPath = atom.project.getPaths()[0] 20 | if @filePath() 21 | for path in atom.project.getPaths() 22 | if @filePath().indexOf(path) == 0 23 | return path 24 | return defaultPath 25 | else 26 | defaultPath 27 | 28 | activeFile: -> 29 | @_activeFile ||= (fp = @filePath()) and atom.project.relativize(fp) 30 | 31 | currentLine: -> 32 | @_currentLine ||= unless @_currentLine 33 | editor = atom.workspace.getActiveTextEditor() 34 | cursor = editor and editor.getLastCursor() 35 | if cursor 36 | cursor.getBufferRow() + 1 37 | else 38 | null 39 | 40 | minitestRegExp: -> 41 | return @_minitestRegExp if @_minitestRegExp != undefined 42 | file = @fileAnalysis() 43 | @_minitestRegExp = @extractMinitestRegExp(file.testHeaderLine, file.testStyle) 44 | 45 | extractMinitestRegExp: (testHeaderLine, testStyle)-> 46 | regExp = @regExpForTestStyle[testStyle] 47 | match = testHeaderLine? and testHeaderLine.match(regExp) or null 48 | if match 49 | match[1] 50 | else 51 | "" 52 | 53 | fileFramework: -> 54 | @fileAnalysis() unless @_fileAnalysis 55 | @_fileAnalysis.framework 56 | 57 | testStyle: -> 58 | @fileAnalysis() unless @_fileAnalysis 59 | @_fileAnalysis.testStyle 60 | 61 | fileAnalysis: -> 62 | return @_fileAnalysis if @_fileAnalysis != undefined 63 | 64 | @_fileAnalysis = 65 | testHeaderLine: null 66 | testStyle: null 67 | framework: null 68 | 69 | editor = atom.workspace.getActiveTextEditor() 70 | i = @currentLine() - 1 71 | specRegExp = new RegExp(/\b(?:should|test|it)\s+['"](.*)['"]\s+do\b/) 72 | rspecRequireRegExp = new RegExp(/^require.*(rails|spec)_helper/) 73 | rspecAssertionRegExp = new RegExp(/^\s*expect\(/) 74 | minitestClassRegExp = new RegExp(/class\s(.*)<(\s?|\s+)Minitest::Test/) 75 | minitestMethodRegExp = new RegExp(/^(\s+)def\s(.*)$/) 76 | while i >= 0 77 | sourceLine = editor.lineTextForBufferRow(i) 78 | 79 | if not @_fileAnalysis.testHeaderLine 80 | # check if it is rspec or minitest spec 81 | if res = sourceLine.match(specRegExp) 82 | @_minitestRegExp = res[1] 83 | @_fileAnalysis.testStyle = 'spec' 84 | @_fileAnalysis.testHeaderLine = sourceLine 85 | 86 | # check if it is minitest unit 87 | else if minitestMethodRegExp.test(sourceLine) 88 | @_fileAnalysis.testStyle = 'unit' 89 | @_fileAnalysis.testHeaderLine = sourceLine 90 | 91 | # if it is spec and has require spec_helper which means it is rspec spec 92 | if rspecRequireRegExp.test(sourceLine) 93 | @_fileAnalysis.testStyle = 'spec' 94 | @_fileAnalysis.framework = 'rspec' 95 | break 96 | 97 | else if rspecAssertionRegExp.test(sourceLine) 98 | @_fileAnalysis.testStyle = 'spec' 99 | @_fileAnalysis.framework = 'rspec' 100 | break 101 | 102 | # if it is unit test and inherit from Minitest::Unit 103 | else if @_fileAnalysis.testStyle == 'unit' && minitestClassRegExp.test(sourceLine) 104 | @_fileAnalysis.framework = 'minitest' 105 | return @_fileAnalysis 106 | 107 | i-- 108 | 109 | if @_fileAnalysis.framework != 'rspec' and @_fileAnalysis.testStyle == 'spec' 110 | @_fileAnalysis.framework = 'minitest' 111 | 112 | @_fileAnalysis 113 | 114 | testFramework: -> 115 | @_testFramework ||= unless @_testFramework 116 | ((t = @fileType()) and @frameworkLookup[t]) or 117 | (fs.existsSync(@projectPath() + '/.rspec') and 'rspec') or 118 | @projectType() 119 | 120 | fileType: -> 121 | @_fileType ||= if @_fileType == undefined 122 | 123 | if not @activeFile() 124 | null 125 | else if matches = @activeFile().match(/_(test|spec)\.rb$/) 126 | if matches[1] == 'test' and atom.config.get("ruby-test.testFramework") 127 | atom.config.get("ruby-test.testFramework") 128 | else if matches[1] == 'spec' and atom.config.get("ruby-test.specFramework") 129 | atom.config.get("ruby-test.specFramework") 130 | else if @fileFramework() == 'minitest' or (not @fileFramework() and matches[1] == 'test' and @testStyle() == 'spec') 131 | 'minitest' 132 | else if matches[1] == 'spec' 133 | 'rspec' 134 | else 135 | 'test' 136 | else if matches = @activeFile().match(/\.(feature)$/) 137 | matches[1] 138 | 139 | projectType: -> 140 | if fs.existsSync(@projectPath() + '/test') 141 | atom.config.get("ruby-test.testFramework") || 'test' 142 | else if fs.existsSync(@projectPath() + '/spec') 143 | atom.config.get("ruby-test.specFramework") || 'rspec' 144 | else if fs.existsSync(@projectPath() + '/features') 145 | 'cucumber' 146 | else 147 | null 148 | 149 | filePath: -> 150 | util = new Utility 151 | util.activePath() 152 | -------------------------------------------------------------------------------- /spec/source-info-spec.coffee: -------------------------------------------------------------------------------- 1 | SourceInfo = require '../lib/source-info' 2 | fs = require('fs') 3 | 4 | describe "SourceInfo", -> 5 | editor = null 6 | sourceInfo = null 7 | 8 | withSetup = (opts) -> 9 | atom.project.getPaths = -> 10 | ["/projects/project_1", "/projects/project_2"] 11 | atom.project.relativize = (filePath) -> 12 | for folderPath in @getPaths() 13 | index = filePath.indexOf(folderPath) 14 | if index >= 0 15 | newPath = filePath.slice index + folderPath.length, filePath.length 16 | newPath = newPath.slice(1, newPath.length) if newPath[0] == '/' 17 | return newPath 18 | 19 | editor = {buffer: {file: {path: "/projects/project_2/test/foo_test.rb"}}} 20 | cursor = getBufferRow: -> 99 21 | editor.getLastCursor = -> cursor 22 | editor.lineTextForBufferRow = (line) -> "" 23 | spyOn(atom.workspace, 'getActiveTextEditor').andReturn(editor) 24 | sourceInfo = new SourceInfo() 25 | 26 | if 'testFile' of opts 27 | editor.buffer.file.path = opts.testFile 28 | 29 | if 'testTreeviewPath' of opts 30 | delete editor.buffer.file.path 31 | treeViewPackage = { mainModule: { treeView: { selectedPath: opts.testTreeviewPath }} } 32 | spyOn(atom.packages, 'getActivePackage').andReturn(treeViewPackage) 33 | 34 | if 'projectPaths' of opts 35 | atom.project.getPaths = -> opts.projectPaths 36 | 37 | if 'currentLine' of opts 38 | cursor.getBufferRow = -> opts.currentLine - 1 39 | 40 | if 'fileContent' of opts 41 | lines = opts.fileContent.split("\n") 42 | editor.lineTextForBufferRow = (row) -> 43 | lines[row] 44 | 45 | if 'config' of opts 46 | for key, value of opts.config 47 | atom.config.set(key, value) 48 | 49 | if 'mockPaths' of opts 50 | spyOn(fs, 'existsSync').andCallFake (path) -> 51 | path in opts.mockPaths 52 | 53 | beforeEach -> 54 | editor = null 55 | sourceInfo = null 56 | 57 | describe "::projectPath", -> 58 | describe "with no testFile", -> 59 | it "is atom.project.getPaths()[0]", -> 60 | withSetup 61 | projectPaths: ['/projects/project_1', '/projects/project_2'] 62 | testFile: null 63 | expect(sourceInfo.projectPath()).toBe("/projects/project_1") 64 | describe "with a testFile", -> 65 | it "is the path within atom.project.getPaths() that is an ancestor of the testFile path", -> 66 | withSetup 67 | projectPaths: ['/projects/project_1', '/projects/project_2'] 68 | testFile: '/projects/project_2/foo/bar_test.rb' 69 | expect(sourceInfo.projectPath()).toBe("/projects/project_2") 70 | 71 | # Detect framework, by inspecting a combination of current file name, 72 | # project subdirectory names, current file content, and configuration value 73 | describe "::testFramework", -> 74 | describe "RSpec detection", -> 75 | it "detects RSpec based on configuration value set to 'rspec'", -> 76 | withSetup 77 | config: "ruby-test.specFramework": "rspec" 78 | projectPaths: ['/home/user/project_1'] 79 | testFile: '/home/user/project_1/bar/foo_spec.rb' 80 | currentLine: 1 81 | fileContent: '' 82 | 83 | expect(sourceInfo.testFramework()).toBe("rspec") 84 | 85 | it "selects RSpec for spec file if spec_helper is required", -> 86 | withSetup 87 | config: "ruby-test.specFramework": "" 88 | projectPaths: ['/home/user/project_1'] 89 | testFile: '/home/user/project_1/bar/foo_spec.rb' 90 | currentLine: 5 91 | fileContent: 92 | """ 93 | require 'spec_helper' 94 | 95 | describe "something" do 96 | it "test something" do 97 | expect('foo').to eq 'foo' 98 | end 99 | end 100 | """ 101 | expect(sourceInfo.testFramework()).toBe("rspec") 102 | 103 | it "selects RSpec for spec file if spec_helper is required with require_relative", -> 104 | withSetup 105 | config: "ruby-test.specFramework": "" 106 | projectPaths: ['/home/user/project_1'] 107 | testFile: '/home/user/project_1/bar/foo_spec.rb' 108 | currentLine: 2 109 | fileContent: 110 | """ 111 | require_relative '../spec_helper' 112 | 113 | """ 114 | expect(sourceInfo.testFramework()).toBe("rspec") 115 | 116 | it "selects RSpec for spec file if expect() is called", -> 117 | withSetup 118 | config: "ruby-test.specFramework": "" 119 | projectPaths: ['/home/user/project_1'] 120 | testFile: '/home/user/project_1/bar/foo_spec.rb' 121 | currentLine: 5 122 | fileContent: 123 | """ 124 | describe "something" do 125 | it "test something" do 126 | expect('foo').to eq 'foo' 127 | end 128 | end 129 | """ 130 | expect(sourceInfo.testFramework()).toBe("rspec") 131 | 132 | describe "Minitest detection", -> 133 | it "is Minitest if filename matches _test.rb, and file contains specs", -> 134 | withSetup 135 | config: "ruby-test.specFramework": "" 136 | projectPaths: ['/home/user/project_1'] 137 | testFile: '/home/user/project_1/bar/foo_test.rb' 138 | currentLine: 3 139 | fileContent: 140 | """ 141 | describe "something" do 142 | it "test something" do 143 | 1.must_equal 1 144 | end 145 | end 146 | """ 147 | expect(sourceInfo.testFramework()).toBe("minitest") 148 | 149 | it "detects Minitest based on configuration value set to 'minitest'", -> 150 | withSetup 151 | config: "ruby-test.specFramework": "minitest" 152 | projectPaths: ['/home/user/project_1'] 153 | testFile: '/home/user/project_1/bar/foo_spec.rb' 154 | currentLine: 1 155 | fileContent: '' 156 | 157 | expect(sourceInfo.testFramework()).toBe("minitest") 158 | 159 | it "is Minitest for a _test.rb file that contains Minitest::Test", -> 160 | withSetup 161 | projectPaths: ['/home/user/project_1'] 162 | testFile: '/home/user/project_1/bar/foo_test.rb' 163 | currentLine: 3 164 | fileContent: 165 | """ 166 | class sometest < Minitest::Test 167 | def something 168 | assert_equal 1, 1 169 | end 170 | end 171 | """ 172 | expect(sourceInfo.testFramework()).toBe("minitest") 173 | 174 | it "when no test file is open, detects Minitest based on configuration value set to 'minitest'", -> 175 | withSetup 176 | config: "ruby-test.specFramework": "minitest" 177 | projectPaths: ['/home/user/project_1'] 178 | testFile: null 179 | currentLine: null 180 | mockPaths: ['/home/user/project_1/spec'] 181 | fileContent: '' 182 | 183 | expect(sourceInfo.testFramework()).toBe("minitest") 184 | 185 | describe "Test::Unit detection", -> 186 | it "assumes Test::Unit when the filename ends with _test.rb, has a method definition, and doesn't have a reference to Minitest", -> 187 | withSetup 188 | projectPaths: ['/home/user/project_1'] 189 | testFile: '/home/user/project_1/bar/foo_test.rb' 190 | currentLine: 3 191 | fileContent: 192 | """ 193 | class sometest < Whatever::Unit 194 | def something 195 | assert_equal 1, 1 196 | end 197 | end 198 | """ 199 | expect(sourceInfo.testFramework()).toBe("test") 200 | 201 | describe "Cucumber detection", -> 202 | it "correctly detects Cucumber file", -> 203 | withSetup 204 | projectPaths: ['/home/user/project_1'] 205 | testFile: '/home/user/project_1/foo/foo.feature' 206 | currentLine: 1 207 | mockPaths: [ 208 | '/home/user/project_1/spec', 209 | '/home/user/project_1/.rspec' 210 | ], 211 | fileContent: 212 | """ 213 | """ 214 | expect(sourceInfo.testFramework()).toBe("cucumber") 215 | 216 | # For when no test file is active 217 | # Detect project type, based on presence of a directory name matching a test framework 218 | describe "::projectType", -> 219 | it "correctly detects a test directory", -> 220 | withSetup 221 | projectPaths: ['/home/user/project_1'] 222 | testFile: null 223 | mockPaths: ['/home/user/project_1/test'] 224 | 225 | expect(sourceInfo.projectType()).toBe("test") 226 | 227 | it "correctly detecs a spec directory", -> 228 | withSetup 229 | projectPaths: ['/home/user/project_1'] 230 | testFile: null 231 | mockPaths: ['/home/user/project_1/spec'] 232 | 233 | expect(sourceInfo.projectType()).toBe("rspec") 234 | 235 | it "correctly detects a cucumber directory", -> 236 | withSetup 237 | projectPaths: ['/home/user/project_1'] 238 | testFile: null 239 | mockPaths: ['/home/user/project_1/features'] 240 | 241 | expect(sourceInfo.projectType()).toBe("cucumber") 242 | 243 | describe "::activeFile", -> 244 | it "is the project-relative path for the current file path", -> 245 | withSetup 246 | projectPaths: ['/projects/project_1', '/projects/project_2'] 247 | testFile: '/projects/project_2/bar/foo_test.rb' 248 | expect(sourceInfo.activeFile()).toBe("bar/foo_test.rb") 249 | 250 | it "if no active editor, it is the selected path in treeview", -> 251 | withSetup 252 | projectPaths: ['/projects/project_1', '/projects/project_2'] 253 | testTreeviewPath: '/projects/project_2/foo/bar' 254 | expect(sourceInfo.activeFile()).toBe("foo/bar") 255 | 256 | 257 | describe "::currentLine", -> 258 | it "is the cursor getBufferRow() plus 1", -> 259 | withSetup 260 | currentLine: 100 261 | expect(sourceInfo.currentLine()).toBe(100) 262 | 263 | describe "::minitestRegExp", -> 264 | it "correctly returns the matching regex for spec", -> 265 | withSetup 266 | projectPaths: ['/projects/project_1'] 267 | testFile: '/projects/project_1/bar/foo_test.rb' 268 | currentLine: 6 269 | fileContent: 270 | """ 271 | require 'minitest/spec' 272 | require 'minitest/autorun' 273 | 274 | describe "Addition" do 275 | it "adds" do 276 | (1 + 1).must_equal 2 277 | end 278 | end 279 | """ 280 | expect(sourceInfo.minitestRegExp()).toBe("adds") 281 | 282 | describe "::minitestRegExp", -> 283 | it "correctly returns the matching regex for spec", -> 284 | sourceInfo = new SourceInfo() 285 | expect(sourceInfo.extractMinitestRegExp(" it \"test something\" do", "spec")).toBe("test something") 286 | 287 | it "correctly returns the matching regex for minitest unit", -> 288 | sourceInfo = new SourceInfo() 289 | expect(sourceInfo.extractMinitestRegExp(" def test_something", "unit")).toBe("test_something") 290 | 291 | it "should return empty string if no match", -> 292 | sourceInfo = new SourceInfo() 293 | expect(sourceInfo.extractMinitestRegExp("test something", "spec")).toBe("") 294 | --------------------------------------------------------------------------------