loris 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. data/.gitignore +2 -0
  2. data/README.rdoc +9 -0
  3. data/Rakefile +41 -0
  4. data/TODO +21 -0
  5. data/VERSION +1 -0
  6. data/bin/loris +15 -0
  7. data/cucumber.yml +1 -0
  8. data/examples/self_test/jsl.conf +1 -0
  9. data/examples/self_test/spec/spec.rhino.js +6 -0
  10. data/features/javascript_lint.feature +34 -0
  11. data/features/run.feature +36 -0
  12. data/features/step_definitons/all.rb +69 -0
  13. data/features/support/env.rb +145 -0
  14. data/lib/always_continuer.rb +7 -0
  15. data/lib/browser_finder.rb +23 -0
  16. data/lib/file_actioner.rb +16 -0
  17. data/lib/file_finder.rb +31 -0
  18. data/lib/filters/ends_with_filter.rb +17 -0
  19. data/lib/filters/extension_filter.rb +20 -0
  20. data/lib/filters/file_filter.rb +14 -0
  21. data/lib/filters/modified_filter.rb +21 -0
  22. data/lib/icons/error.png +0 -0
  23. data/lib/icons/failure.png +0 -0
  24. data/lib/icons/info.png +0 -0
  25. data/lib/icons/success.png +0 -0
  26. data/lib/icons/warning.png +0 -0
  27. data/lib/js-test-driver/JsTestDriver-1.1.jar +0 -0
  28. data/lib/js-test-driver/plugins/coverage-1.1.jar +0 -0
  29. data/lib/loris.rb +170 -0
  30. data/lib/outputs/growl_output.rb +26 -0
  31. data/lib/outputs/output_collection.rb +23 -0
  32. data/lib/outputs/shell_output.rb +17 -0
  33. data/lib/outputs/unix_console_clearing_output.rb +13 -0
  34. data/lib/outputs/windows_console_clearing_output.rb +13 -0
  35. data/lib/pinger.rb +23 -0
  36. data/lib/poller.rb +16 -0
  37. data/lib/sleep_waiter.rb +11 -0
  38. data/lib/task_manager.rb +45 -0
  39. data/lib/tasks/command_line_task.rb +28 -0
  40. data/lib/tasks/javascript_lint/javascript_lint_parser.rb +45 -0
  41. data/lib/tasks/javascript_lint/javascript_lint_runner.rb +25 -0
  42. data/lib/tasks/js_test_driver/js_test_driver_config.rb +22 -0
  43. data/lib/tasks/js_test_driver/js_test_driver_parser.rb +28 -0
  44. data/lib/tasks/js_test_driver/js_test_driver_runner.rb +28 -0
  45. data/lib/tasks/js_test_driver/js_test_driver_server.rb +38 -0
  46. data/lib/tasks/jspec/jspec_parser.rb +30 -0
  47. data/lib/tasks/jspec/jspec_runner.rb +25 -0
  48. data/lib/tasks/list_task.rb +34 -0
  49. data/lib/tasks/rspec/rspec_parser.rb +25 -0
  50. data/lib/tasks/rspec/rspec_runner.rb +25 -0
  51. data/lib/unix_process.rb +7 -0
  52. data/lib/windows_process.rb +12 -0
  53. data/loris.gemspec +133 -0
  54. data/loris.tmproj +232 -0
  55. data/spec/file_actioner_spec.rb +41 -0
  56. data/spec/file_finder_spec.rb +73 -0
  57. data/spec/filters/ends_with_filter_spec.rb +26 -0
  58. data/spec/filters/file_filter_spec.rb +28 -0
  59. data/spec/filters/modified_filter_spec.rb +47 -0
  60. data/spec/growl_output_spec.rb +33 -0
  61. data/spec/list_task_spec.rb +58 -0
  62. data/spec/poller_spec.rb +28 -0
  63. data/spec/shell_output_spec.rb +25 -0
  64. data/spec/task_manager_spec.rb +64 -0
  65. data/spec/tasks/javascript_lint/javascript_lint_runner_spec.rb +90 -0
  66. data/spec/tasks/js_test_driver/js_test_driver_runner_spec.rb +92 -0
  67. data/spec/tasks/jspec/jspec_parser_spec.rb +28 -0
  68. data/spec/tasks/jspec/jspec_runner_spec.rb +78 -0
  69. metadata +174 -0
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .gitignore
2
+ .DS_Store
data/README.rdoc ADDED
@@ -0,0 +1,9 @@
1
+ == Autotest for Javascript ==
2
+
3
+ Run your javascript unit tests any time a file in your project changes.
4
+
5
+ Runs Javascript Lint (if jsl.conf is found)
6
+ Runs JSpec (if spec/spec.rhino.js is found)
7
+ Runs JS Test Driver (if jsTestDriver.conf is found)
8
+
9
+ Also runs RSpec (if any file ending in _spec.rb is found in the spec directory)
data/Rakefile ADDED
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'spec/rake/spectask'
4
+ require 'cucumber'
5
+ require 'cucumber/rake/task'
6
+
7
+ begin
8
+ require 'jeweler'
9
+ Jeweler::Tasks.new do |gem|
10
+ gem.name = "loris"
11
+ gem.summary = 'Automatically run javascript unit tests'
12
+ gem.description = 'Automatically run javascript unit tests'
13
+ gem.email = "loris@monket.net"
14
+ gem.homepage = "http://github.com/karl/loris"
15
+ gem.authors = ["Karl O'Keeffe"]
16
+
17
+ gem.add_dependency('visionmedia-bind', [">= 0.2.6"])
18
+ gem.add_dependency('karl-growl', [">= 1.0.3"])
19
+ gem.add_dependency('extensions', [">= 0.6.0"])
20
+ gem.add_dependency('win32-process', [">= 0.6.1"])
21
+
22
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
23
+ end
24
+ rescue LoadError
25
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
26
+ end
27
+
28
+
29
+ task :default => :spec
30
+
31
+ Spec::Rake::SpecTask.new(:spec) do |t|
32
+ t.spec_files = Dir.glob('spec/**/*_spec.rb')
33
+ # t.rcov = true
34
+ end
35
+
36
+ Cucumber::Rake::Task.new(:features) do |t|
37
+ t.cucumber_opts = "features --format pretty"
38
+ end
39
+
40
+
41
+
data/TODO ADDED
@@ -0,0 +1,21 @@
1
+ * Work out what is going on with fork in windows (running second copy of Loris?)
2
+
3
+ * Remove JsTestDriver.jar from lib dir
4
+ * Tidy Windows related if statements
5
+ * Tidy JSL filename removing
6
+
7
+ * javascript lint not installed? (package if not already installed?)
8
+ * JSpec not installed? (can require as gem)
9
+
10
+ * Detect file rename
11
+ * Detect file deletion/directory rename
12
+
13
+ * Factories to create tasks
14
+
15
+ * Deal with filesystem exceptions
16
+ * jspec task (just modified files?)
17
+ * javascript lint task (just modified files?)
18
+ * separate thread (create new and kill old)
19
+ * listen for interrupt
20
+ * cucumber task
21
+ * Tidy growl windows code
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.12
data/bin/loris ADDED
@@ -0,0 +1,15 @@
1
+ #!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
2
+ $:.unshift(File.dirname(__FILE__ + '.rb') + '/../lib') unless $:.include?(File.dirname(__FILE__ + '.rb') + '/../lib')
3
+
4
+ require 'loris'
5
+ begin
6
+ # The dup is to keep ARGV intact, so that tools like ruby-debug can respawn.
7
+ failure = Loris::CLI::Main.execute(ARGV.dup)
8
+ Kernel.exit(failure ? 1 : 0)
9
+ rescue SystemExit => e
10
+ Kernel.exit(e.status)
11
+ rescue Exception => e
12
+ STDERR.puts("#{e.message} (#{e.class})")
13
+ STDERR.puts(e.backtrace.join("\n"))
14
+ Kernel.exit 1
15
+ end
data/cucumber.yml ADDED
@@ -0,0 +1 @@
1
+ default: features
@@ -0,0 +1 @@
1
+ +process tmp/*.js
@@ -0,0 +1,6 @@
1
+
2
+ load('/Library/Ruby/Gems/1.8/gems/visionmedia-jspec-2.2.1/lib/jspec.js')
3
+
4
+ JSpec
5
+ .run({ formatter : JSpec.formatters.Terminal })
6
+ .report()
@@ -0,0 +1,34 @@
1
+ Feature Javascript Lint
2
+
3
+ Scenario: Javascript Lint is run
4
+ Given I run loris --debug
5
+ When I create a file named "example.js" with:
6
+ """
7
+ var foo = function() {
8
+ x = 'moo'
9
+ }
10
+ """
11
+ And I wait until loris has finished processing changes
12
+ Then the Loris output should contain:
13
+ """
14
+ Javascript Lint
15
+ warning
16
+ """
17
+ And I should not see any errors
18
+
19
+ # Scenario: Javascript Lint is run when Loris starts
20
+ # When I create a file named "example.js" with:
21
+ # """
22
+ # var foo = function() {
23
+ # x = 'moo'
24
+ # }
25
+ # """
26
+ # Given I run loris --debug
27
+ # And I wait until loris has finished processing changes
28
+ # Then the Loris output should contain:
29
+ # """
30
+ # Javascript Lint
31
+ # warning
32
+ # """
33
+ # And I should not see any errors
34
+ #
@@ -0,0 +1,36 @@
1
+ Feature: Run Loris
2
+
3
+ Scenario: Creating a file triggers loris
4
+ Given I run loris --debug
5
+ When I create a file named "created.txt"
6
+ And I wait until loris has finished processing changes
7
+ Then I should see "created.txt" in the Loris output
8
+ And I should not see any errors
9
+
10
+ Scenario: Modifying a file triggers loris
11
+ Given I create a file named "modified.txt"
12
+ When I run loris --debug
13
+ And I wait until loris has finished processing changes
14
+ And I start recording the Loris output
15
+ And I modify the "modified.txt" file
16
+ And I wait until loris has finished processing changes
17
+ Then I should see "modified.txt" in the recorded output
18
+ And I should not see any errors
19
+
20
+ Scenario: Modified only triggered once
21
+ Given I create a file named "modified.txt"
22
+ When I run loris --debug
23
+ And I wait until loris has finished processing changes
24
+ And I start recording the Loris output
25
+ And I modify the "modified.txt" file
26
+ And I wait until loris has finished processing changes
27
+ And I wait until loris has finished processing changes
28
+ Then I should only see "modified.txt" once in the recorded output
29
+ And I should not see any errors
30
+
31
+ Scenario: Directories not included
32
+ Given I run loris --debug
33
+ When I create a directory named "dir"
34
+ And I wait until loris has finished processing changes
35
+ Then I should NOT see "dir" in the Loris output
36
+ And I should not see any errors
@@ -0,0 +1,69 @@
1
+ require 'spec/expectations'
2
+
3
+ When /^I run loris(.*)$/ do |loris_opts|
4
+ run_in_background "#{Loris::RUBY_BINARY} #{Loris::BINARY} #{loris_opts}"
5
+ end
6
+
7
+ Given /^(?:I create )a file named "([^\"]*)"$/ do |file_name|
8
+ @current_dir = working_dir
9
+ create_file(file_name, '')
10
+ end
11
+
12
+ Given /^(?:I create )a directory named "([^\"]*)"$/ do |dir_name|
13
+ @current_dir = working_dir
14
+ create_dir(dir_name)
15
+ end
16
+
17
+ Given /^(?:I create )a file named "([^\"]*)" with:$/ do |file_name, file_content|
18
+ @current_dir = working_dir
19
+ create_file(file_name, file_content)
20
+ end
21
+
22
+ When /^I modify the "([^\"]*)" file$/ do |file_name|
23
+ @current_dir = working_dir
24
+ touch_file(file_name)
25
+ end
26
+
27
+ When /^I wait until loris has finished processing changes$/ do
28
+ len = get_background_output.length
29
+ new_output = ""
30
+ while not new_output =~ /\[Poll complete\]/
31
+ new_output = get_background_output[len..-1]
32
+ sleep 0.5
33
+ end
34
+ end
35
+
36
+ When /^I start recording the Loris output$/ do
37
+ @pre_recorded_length = get_background_output.length
38
+ end
39
+
40
+ Then /^I should see "([^\"]*)" in the recorded output$/ do |text|
41
+ recorded = get_background_output[@pre_recorded_length..-1]
42
+ recorded.should include text
43
+ end
44
+
45
+ Then /^I should only see "([^\"]*)" once in the recorded output$/ do |text|
46
+ recorded = get_background_output[@pre_recorded_length..-1]
47
+ recorded.should include text
48
+ recorded.scan(text).length.should equal 1
49
+ end
50
+
51
+ Then /^I should see "([^\"]*)" in the Loris output$/ do |text|
52
+ get_background_output.should include text
53
+ end
54
+
55
+ Then /^the Loris output should contain:$/ do |text|
56
+ get_background_output.should include text
57
+ end
58
+
59
+ Then /^the Loris output should NOT contain:$/ do |text|
60
+ get_background_output.should_not include text
61
+ end
62
+
63
+ Then /^I should NOT see "([^\"]*)" in the Loris output$/ do |text|
64
+ get_background_output.should_not include text
65
+ end
66
+
67
+ Then /^I should not see any errors$/ do
68
+ get_background_error.strip.should == ""
69
+ end
@@ -0,0 +1,145 @@
1
+ require 'rubygems'
2
+ require 'tempfile'
3
+ require 'spec/expectations'
4
+ require 'fileutils'
5
+ require 'forwardable'
6
+
7
+ require 'lib/loris.rb'
8
+
9
+ class CucumberWorld
10
+ extend Forwardable
11
+ def_delegators CucumberWorld, :examples_dir, :self_test_dir, :working_dir, :cucumber_lib_dir
12
+
13
+ def self.examples_dir(subdir=nil)
14
+ @examples_dir ||= File.expand_path(File.join(File.dirname(__FILE__), '../../examples'))
15
+ subdir ? File.join(@examples_dir, subdir) : @examples_dir
16
+ end
17
+
18
+ def self.self_test_dir
19
+ @self_test_dir ||= examples_dir('self_test')
20
+ end
21
+
22
+ def self.working_dir
23
+ @working_dir ||= examples_dir('self_test/tmp')
24
+ end
25
+
26
+ def cucumber_lib_dir
27
+ @cucumber_lib_dir ||= File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
28
+ end
29
+
30
+ def initialize
31
+ @current_dir = self_test_dir
32
+ end
33
+
34
+ private
35
+ attr_reader :last_exit_status, :last_stderr
36
+
37
+ # The last standard out, with the duration line taken out (unpredictable)
38
+ def last_stdout
39
+ strip_duration(@last_stdout)
40
+ end
41
+
42
+ def strip_duration(s)
43
+ s.gsub(/^\d+m\d+\.\d+s\n/m, "")
44
+ end
45
+
46
+ def replace_duration(s, replacement)
47
+ s.gsub(/\d+m\d+\.\d+s/m, replacement)
48
+ end
49
+
50
+ def create_file(file_name, file_content)
51
+ file_content.gsub!("CUCUMBER_LIB", "'#{cucumber_lib_dir}'") # Some files, such as Rakefiles need to use the lib dir
52
+ in_current_dir do
53
+ File.open(file_name, 'w') { |f| f << file_content }
54
+ end
55
+ end
56
+
57
+ def create_dir(dir_name)
58
+ in_current_dir do
59
+ # create any subdirectories if needed
60
+ FileUtils.mkdir(dir_name)
61
+ end
62
+ end
63
+
64
+ def touch_file(file_name)
65
+ in_current_dir do
66
+ FileUtils.touch(file_name)
67
+ end
68
+ end
69
+
70
+ def background_jobs
71
+ @background_jobs ||= []
72
+ end
73
+
74
+ def in_current_dir(&block)
75
+ Dir.chdir(@current_dir, &block)
76
+ end
77
+
78
+ def run(command)
79
+ stderr_file = Tempfile.new('loris-stderr')
80
+ stderr_file.close
81
+
82
+ stdout_file = Tempfile.new('loris-stdout')
83
+ stdout_file.close
84
+
85
+ in_current_dir do
86
+ `#{command} 1> #{stdout_file.path} 2> #{stderr_file.path}`
87
+ @last_exit_status = $?.exitstatus
88
+ end
89
+ @last_stdout = IO.read(stdout_file.path)
90
+ @last_stderr = IO.read(stderr_file.path)
91
+ end
92
+
93
+ def run_in_background(command)
94
+ @stderr_file = Tempfile.new('loris-stderr')
95
+ @stderr_file.close
96
+
97
+ @stdout_file = Tempfile.new('loris-stdout')
98
+ @stdout_file.close
99
+
100
+ pid = fork
101
+ in_current_dir do
102
+ if pid
103
+ background_jobs << pid
104
+ else
105
+ cmd = "#{command} 1> #{@stdout_file.path} 2> #{@stderr_file.path}"
106
+ exec cmd
107
+ end
108
+ end
109
+ end
110
+
111
+ def terminate_background_jobs
112
+ if @background_jobs
113
+ @background_jobs.each do |pid|
114
+ Process.kill(Signal.list['TERM'], pid)
115
+ end
116
+ end
117
+ end
118
+
119
+ def get_background_output
120
+ return IO.read(@stdout_file.path) rescue return ""
121
+ end
122
+
123
+ def get_background_error
124
+ return IO.read(@stderr_file.path) rescue return ""
125
+ end
126
+
127
+ end
128
+
129
+ World do
130
+ CucumberWorld.new
131
+ end
132
+
133
+ Before do
134
+ FileUtils.rm_rf CucumberWorld.working_dir
135
+ FileUtils.mkdir CucumberWorld.working_dir
136
+ end
137
+
138
+ After do
139
+ err = get_background_error
140
+ if (err != "")
141
+ puts 'Background process errors:'
142
+ puts err
143
+ end
144
+ terminate_background_jobs
145
+ end
@@ -0,0 +1,7 @@
1
+ class AlwaysContinuer
2
+
3
+ def continue?
4
+ return true
5
+ end
6
+
7
+ end
@@ -0,0 +1,23 @@
1
+ require 'win32/registry'
2
+
3
+ class BrowserFinder
4
+
5
+ def initialize
6
+ @ie = '"C:/Program Files/Internet Explorer/iexplore.exe" "%1"'
7
+ end
8
+
9
+ def getDefault
10
+ browser = getBrowserFromRegistry()
11
+ return browser != '' ? browser : @ie
12
+ end
13
+
14
+ def getBrowserFromRegistry
15
+
16
+ Win32::Registry::HKEY_CLASSES_ROOT.open('http\shell\open\command') do |reg|
17
+ reg_typ, reg_val = reg.read('')
18
+ return reg_val
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,16 @@
1
+ class FileActioner
2
+
3
+ def initialize(file_finder, task_manager)
4
+ @file_finder = file_finder
5
+ @task_manager = task_manager
6
+ end
7
+
8
+ def run
9
+ files = @file_finder.find
10
+
11
+ if (files[:filtered] != [])
12
+ @task_manager.run(files)
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,31 @@
1
+ class FileFinder
2
+
3
+ def initialize(finder, dir)
4
+ @finder = finder
5
+ @dir = dir
6
+ @filters = []
7
+ end
8
+
9
+ def add_filter(filter)
10
+ @filters << filter
11
+ end
12
+
13
+ def find
14
+ all_files = []
15
+ filtered_files = []
16
+
17
+ @finder.find(@dir) do |path|
18
+ all_files << path
19
+
20
+ keep = @filters.inject(true) { |k, filter| k && filter.filter(path) }
21
+ filtered_files << path if keep
22
+ end
23
+
24
+ @filters.each do |filter|
25
+ filter.complete
26
+ end
27
+
28
+ return { :all => all_files, :filtered => filtered_files }
29
+ end
30
+
31
+ end
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'extensions/string'
3
+
4
+ class EndsWithFilter
5
+
6
+ def initialize(text)
7
+ @text = text.downcase
8
+ end
9
+
10
+ def filter(file_name)
11
+ return file_name.downcase.ends_with? @text
12
+ end
13
+
14
+ def complete
15
+ end
16
+
17
+ end
@@ -0,0 +1,20 @@
1
+ class ExtensionFilter
2
+
3
+ def initialize(file_class, extension)
4
+ @file_class = file_class
5
+ @extension = extension.downcase
6
+ end
7
+
8
+ def filter(file_name)
9
+ return file_type(file_name) == @extension
10
+ end
11
+
12
+ # Return the part of the file name string after the last '.'
13
+ def file_type(file_name)
14
+ @file_class.extname(file_name).gsub( /^\./, '' ).downcase
15
+ end
16
+
17
+ def complete
18
+ end
19
+
20
+ end
@@ -0,0 +1,14 @@
1
+ class FileFilter
2
+
3
+ def initialize(file_class)
4
+ @file_class = file_class
5
+ end
6
+
7
+ def filter(path)
8
+ return @file_class.file?(path)
9
+ end
10
+
11
+ def complete
12
+ end
13
+
14
+ end
@@ -0,0 +1,21 @@
1
+ class ModifiedFilter
2
+
3
+ def initialize(file_class, last_modified = nil)
4
+ @file_class = file_class
5
+ @last_modified = last_modified
6
+ @modifieds = []
7
+ end
8
+
9
+ def filter(path)
10
+ modified = @file_class.mtime(path)
11
+ @modifieds << modified
12
+
13
+ return @last_modified.nil? || modified > @last_modified
14
+ end
15
+
16
+ def complete
17
+ @last_modified = @modifieds.max
18
+ @modifieds = []
19
+ end
20
+
21
+ end
Binary file
Binary file
Binary file
Binary file
Binary file