guard-jest 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rubocop.yml +13 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +70 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/guard-jest.gemspec +38 -0
- data/lib/generators/guard_jest/install_generator.rb +16 -0
- data/lib/generators/guard_jest/templates/Guardfile +9 -0
- data/lib/guard/jest.rb +113 -0
- data/lib/guard/jest/formatter.rb +103 -0
- data/lib/guard/jest/run_request.rb +41 -0
- data/lib/guard/jest/runner.rb +78 -0
- data/lib/guard/jest/server.rb +132 -0
- data/lib/guard/jest/version.rb +7 -0
- metadata +148 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 12086254600f743764afab326f8cd7d944139982
|
4
|
+
data.tar.gz: 754a8c45c368998d810518e74dd4968f9baa0733
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f8d72ca3d8057279249bfafe8709925b4f058ea484180c72a46764ea3e2b52f0e2f178c83ada98a12f3df29d6ea619951b11b2f5f2c0decd27779a0ae9567036
|
7
|
+
data.tar.gz: 02c4369e5df3c54de3088e940638baa79fdd81163b87b095d43a7fefd301198d0901446d0f3e030443d31c88c53776fc6fdf72308bd3e5ad305956e2333cb38c
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Nathan Stitt
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
[](https://travis-ci.org/nathanstitt/guard-jest)
|
2
|
+
|
3
|
+
# Guard::Jest automatically tests your Jest specs when files are modified.
|
4
|
+
|
5
|
+
It runs the Jest server in watch mode, so that it can run specs more efficiently when instructed.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
Add Guard::Jest to your `Gemfile`:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
group :development, :test do
|
15
|
+
gem 'guard-jest'
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
Add the default Guard::Jest template to your `Guardfile` by running:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
$ guard init jest
|
23
|
+
```
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
Please read the [Guard usage documentation](https://github.com/guard/guard#readme).
|
27
|
+
|
28
|
+
## Guardfile
|
29
|
+
|
30
|
+
Guard::Jest can be adapted to all kind of projects. Please read the
|
31
|
+
[Guard documentation](https://github.com/guard/guard#readme) for more information about the Guardfile DSL.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
guard 'jest' do
|
35
|
+
watch(%r{spec/javascripts/spec\.(js\.coffee|js|coffee)$}) { "spec/javascripts" }
|
36
|
+
watch(%r{spec/javascripts/.+_spec\.(js\.coffee|js|coffee)$})
|
37
|
+
watch(%r{app/assets/javascripts/(.+?)\.(js\.coffee|js|coffee)$}) { |m| "spec/javascripts/#{m[1]}_spec.#{m[2]}" }
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
## Options
|
42
|
+
|
43
|
+
There are many options that can customize Guard::Jest to your needs. Options are simply supplied as hash when
|
44
|
+
defining the Guard in your `Guardfile`:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
guard 'jest', jest_cmd: './node_modules/jest-cli/bin/jest.js' do
|
48
|
+
...
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
### Server options
|
53
|
+
|
54
|
+
The server options configures the server environment that is needed to run Guard::Jest:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
directory: <cwd> # Directory that should be used for running Jest
|
58
|
+
config_file: nil # Path to a [Jest configuration file](https://facebook.github.io/jest/docs/configuration.html)
|
59
|
+
|
60
|
+
jest_cmd: jest # Command to execute in order to start the Jest server
|
61
|
+
```
|
62
|
+
|
63
|
+
## Contributing
|
64
|
+
|
65
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/nathanstitt/guard-jest.
|
66
|
+
|
67
|
+
|
68
|
+
## License
|
69
|
+
|
70
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "guard/jest"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/guard-jest.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'guard/jest/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "guard-jest"
|
8
|
+
spec.version = Guard::Jest::VERSION
|
9
|
+
spec.authors = ["Nathan Stitt"]
|
10
|
+
spec.email = ["nathan@stitt.org"]
|
11
|
+
|
12
|
+
spec.summary = 'Guard plugin for auto-running Jest specs'
|
13
|
+
spec.description = 'Guard plugin for testing Javascript using the Jest test runner'
|
14
|
+
spec.homepage = "https://github.com/nathanstitt/guard-jest"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
if spec.respond_to?(:metadata)
|
18
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
19
|
+
else
|
20
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
21
|
+
"public gem pushes."
|
22
|
+
end
|
23
|
+
|
24
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
25
|
+
f.match(%r{^(test|spec|features)/})
|
26
|
+
end
|
27
|
+
spec.bindir = "exe"
|
28
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ["lib"]
|
30
|
+
|
31
|
+
spec.add_dependency 'guard-compat', '~> 1.2'
|
32
|
+
spec.add_dependency 'concurrent-ruby', '~> 1.0.4'
|
33
|
+
|
34
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
35
|
+
spec.add_development_dependency 'bundler', '~> 1.13'
|
36
|
+
spec.add_development_dependency 'rspec', '~> 3.5'
|
37
|
+
spec.add_development_dependency 'guard-rspec', '~> 4.7'
|
38
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module GuardJest
|
4
|
+
class InstallGenerator < Rails::Generators::Base
|
5
|
+
desc 'Install a sample Guardfile for running Jest specs via GuardJest'
|
6
|
+
|
7
|
+
def self.source_root
|
8
|
+
@source_root ||= File.join(File.dirname(__FILE__), 'templates')
|
9
|
+
end
|
10
|
+
|
11
|
+
# Generator Code. Remember this is just suped-up Thor so methods are executed in order
|
12
|
+
def install
|
13
|
+
template 'Guardfile'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard :jest do
|
5
|
+
watch(%r{^spec/javascripts/.*(?:_s|S)pec\.(coffee|js)$})
|
6
|
+
watch(%r{app/assets/javascripts/(.+?)\.(js\.coffee|js|coffee)(?:\.\w+)*$}) do |m|
|
7
|
+
"spec/javascripts/#{m[1]}_spec.#{m[2]}"
|
8
|
+
end
|
9
|
+
end
|
data/lib/guard/jest.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'guard/compat/plugin'
|
3
|
+
|
4
|
+
require_relative 'jest/version'
|
5
|
+
require_relative 'jest/formatter'
|
6
|
+
require_relative 'jest/run_request'
|
7
|
+
require_relative 'jest/runner'
|
8
|
+
require_relative 'jest/server'
|
9
|
+
|
10
|
+
module Guard
|
11
|
+
# The Jest guard that gets notifications about the following
|
12
|
+
# Guard events: `start`, `stop`, `reload`, `run_all` and `run_on_modifications`.
|
13
|
+
class Jest < Plugin
|
14
|
+
class << self
|
15
|
+
attr_writer :logger
|
16
|
+
def logger
|
17
|
+
@logger ||= ( l = Logger.new(STDOUT); l.level = Logger::INFO; l )
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
DEFAULT_OPTIONS = {
|
22
|
+
jest_cmd: 'jest'
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
attr_reader :runner, :server
|
26
|
+
|
27
|
+
# Initialize Guard::Jest
|
28
|
+
#
|
29
|
+
# @param [Hash] options the options for the Guard
|
30
|
+
# @option options [String] :config_file the location of a Jest configuration file
|
31
|
+
# @option options [String] :jest_cmd path to jest application that should be executed
|
32
|
+
#
|
33
|
+
def initialize(options = {})
|
34
|
+
options = DEFAULT_OPTIONS.merge(options)
|
35
|
+
options[:server] = @server = options[:server] || Server.new(options)
|
36
|
+
@runner = options[:runner] || Runner.new(options)
|
37
|
+
super(options)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Called once when Guard starts. Please override initialize method to init stuff.
|
41
|
+
#
|
42
|
+
# @raise [:task_has_failed] when start has failed
|
43
|
+
# @return [Object] the task result
|
44
|
+
#
|
45
|
+
def start
|
46
|
+
throw :task_has_failed unless server.start
|
47
|
+
run_all if options[:all_on_start]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Called when `stop|quit|exit|s|q|e + enter` is pressed (when Guard quits).
|
51
|
+
#
|
52
|
+
# @raise [:task_has_failed] when stop has failed
|
53
|
+
# @return [Object] the task result
|
54
|
+
#
|
55
|
+
def stop
|
56
|
+
throw :task_has_failed unless server.stop
|
57
|
+
end
|
58
|
+
|
59
|
+
# Called when `reload|r|z + enter` is pressed.
|
60
|
+
# This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
61
|
+
#
|
62
|
+
# @raise [:task_has_failed] when reload has failed
|
63
|
+
# @return [Object] the task result
|
64
|
+
#
|
65
|
+
def reload
|
66
|
+
server_success = server.reload(options)
|
67
|
+
runner_success = runner.reload(options)
|
68
|
+
throw :task_has_failed unless server_success && runner_success
|
69
|
+
end
|
70
|
+
|
71
|
+
# Called when just `enter` is pressed
|
72
|
+
# This method should be principally used for long action like running all specs/tests/...
|
73
|
+
#
|
74
|
+
# @raise [:task_has_failed] when run_all has failed
|
75
|
+
# @return [Object] the task result
|
76
|
+
#
|
77
|
+
def run_all
|
78
|
+
results = runner.test_all
|
79
|
+
throw :task_has_failed if results.failed?
|
80
|
+
end
|
81
|
+
|
82
|
+
# Called on file(s) additions that the Guard plugin watches.
|
83
|
+
#
|
84
|
+
# @param [Array<String>] paths the changes files or paths
|
85
|
+
# @raise [:task_has_failed] when run_on_additions has failed
|
86
|
+
# @return [Object] the task result
|
87
|
+
#
|
88
|
+
def run_on_additions(paths)
|
89
|
+
run_on_modifications(paths)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Called on file(s) modifications that the Guard plugin watches.
|
93
|
+
#
|
94
|
+
# @param [Array<String>] paths the changes files or paths
|
95
|
+
# @raise [:task_has_failed] when run_on_modifications has failed
|
96
|
+
# @return [Object] the task result
|
97
|
+
#
|
98
|
+
def run_on_modifications(paths)
|
99
|
+
results = runner.test_paths(paths)
|
100
|
+
throw :task_has_failed if results.failed?
|
101
|
+
end
|
102
|
+
|
103
|
+
# Called on file(s) removals that the Guard plugin watches.
|
104
|
+
#
|
105
|
+
# @param [Array<String>] paths the changes files or paths
|
106
|
+
# @raise [:task_has_failed] when run_on_removals has failed
|
107
|
+
# @return [Object] the task result
|
108
|
+
#
|
109
|
+
def run_on_removals(paths)
|
110
|
+
runner.remove_paths(paths)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'guard/compat/plugin'
|
2
|
+
|
3
|
+
module Guard
|
4
|
+
class Jest < Plugin
|
5
|
+
# The Guard::Jasmine formatter collects console and
|
6
|
+
# system notification methods and enhances them with
|
7
|
+
# some color information.
|
8
|
+
#
|
9
|
+
module Formatter
|
10
|
+
class << self
|
11
|
+
# Print an info message to the console.
|
12
|
+
#
|
13
|
+
# @param [String] message the message to print
|
14
|
+
# @param [Hash] options the output options
|
15
|
+
# @option options [Boolean] :reset reset the UI
|
16
|
+
#
|
17
|
+
def info(message, options = {})
|
18
|
+
Compat::UI.info(message, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Print a debug message to the console.
|
22
|
+
#
|
23
|
+
# @param [String] message the message to print
|
24
|
+
# @param [Hash] options the output options
|
25
|
+
# @option options [Boolean] :reset reset the UI
|
26
|
+
#
|
27
|
+
def debug(message, options = {})
|
28
|
+
Compat::UI.debug(message, options)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Print a red error message to the console.
|
32
|
+
#
|
33
|
+
# @param [String] message the message to print
|
34
|
+
# @param [Hash] options the output options
|
35
|
+
# @option options [Boolean] :reset reset the UI
|
36
|
+
#
|
37
|
+
def error(message, options = {})
|
38
|
+
Compat::UI.error(color(message, ';31'), options)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Print a green success message to the console.
|
42
|
+
#
|
43
|
+
# @param [String] message the message to print
|
44
|
+
# @param [Hash] options the output options
|
45
|
+
# @option options [Boolean] :reset reset the UI
|
46
|
+
#
|
47
|
+
def success(message, options = {})
|
48
|
+
Compat::UI.info(color(message, ';32'), options)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Print a yellow pending message to the console.
|
52
|
+
#
|
53
|
+
# @param [String] message the message to print
|
54
|
+
# @param [Hash] options the output options
|
55
|
+
# @option options [Boolean] :reset reset the UI
|
56
|
+
#
|
57
|
+
def spec_pending(message, options = {})
|
58
|
+
Compat::UI.info(color(message, ';33'), options)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Print a red spec failed message to the console.
|
62
|
+
#
|
63
|
+
# @param [String] message the message to print
|
64
|
+
# @param [Hash] options the output options
|
65
|
+
#
|
66
|
+
def spec_failed(message, options = {})
|
67
|
+
Compat::UI.info(color(message, ';31'), options)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Print a red spec failed message to the console.
|
71
|
+
#
|
72
|
+
# @param [String] message the message to print
|
73
|
+
# @param [Hash] options the output options
|
74
|
+
#
|
75
|
+
def suite_name(message, options = {})
|
76
|
+
Compat::UI.info(color(message, ';33'), options)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Outputs a system notification.
|
80
|
+
#
|
81
|
+
# @param [String] message the message to print
|
82
|
+
# @param [Hash] options the output options
|
83
|
+
# @option options [Symbol, String] :image the image to use, either :failed, :pending or :success, or an image path
|
84
|
+
# @option options [String] :title the title of the system notification
|
85
|
+
#
|
86
|
+
def notify(message, options = {})
|
87
|
+
Compat::UI.notify(message, options)
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Print a info message to the console.
|
93
|
+
#
|
94
|
+
# @param [String] text the text to colorize
|
95
|
+
# @param [String] color_code the color code
|
96
|
+
#
|
97
|
+
def color(text, color_code)
|
98
|
+
Compat::UI.color_enabled? ? "\e[0#{color_code}m#{text}\e[0m" : text
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'concurrent/atomic/atomic_boolean'
|
2
|
+
|
3
|
+
module Guard
|
4
|
+
class Jest < Plugin
|
5
|
+
class RunRequest
|
6
|
+
|
7
|
+
attr_reader :paths, :runner, :result
|
8
|
+
|
9
|
+
def initialize(runner, paths)
|
10
|
+
@runner = runner
|
11
|
+
@paths = paths
|
12
|
+
@end_time = Time.now
|
13
|
+
@start_time = Time.now
|
14
|
+
@is_complete = Concurrent::AtomicBoolean.new(false)
|
15
|
+
end
|
16
|
+
|
17
|
+
def all?
|
18
|
+
:all == paths
|
19
|
+
end
|
20
|
+
|
21
|
+
def elapsed_seconds
|
22
|
+
if satisfied?
|
23
|
+
@end_time - @start_time
|
24
|
+
else
|
25
|
+
Time.now - @start_time
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def satisfied?
|
30
|
+
@is_complete.true?
|
31
|
+
end
|
32
|
+
|
33
|
+
def satisfy(json)
|
34
|
+
@end_time = Time.now
|
35
|
+
@is_complete.make_true
|
36
|
+
@result = json
|
37
|
+
runner.notify(self)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'guard/ui'
|
2
|
+
require 'guard/compat/plugin'
|
3
|
+
|
4
|
+
module Guard
|
5
|
+
class Jest < Plugin
|
6
|
+
|
7
|
+
class Runner
|
8
|
+
attr_reader :server, :options
|
9
|
+
|
10
|
+
attr_accessor :last_failed_paths, :last_result
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
reload(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def reload(options)
|
17
|
+
@options = options
|
18
|
+
@server = options[:server]
|
19
|
+
self.last_failed_paths = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_all
|
23
|
+
server.run(RunRequest.new(self, :all))
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_paths(paths)
|
27
|
+
paths = paths.concat(last_failed_paths) if options[:keep_failed]
|
28
|
+
|
29
|
+
server.run(RunRequest.new(self, paths.uniq.compact))
|
30
|
+
end
|
31
|
+
|
32
|
+
def remove_paths(paths)
|
33
|
+
self.last_failed_paths -= paths
|
34
|
+
end
|
35
|
+
|
36
|
+
def notify(request)
|
37
|
+
@last_result = r = request.result
|
38
|
+
|
39
|
+
specs = r['numTotalTests'] - r['numPendingTests']
|
40
|
+
failed = r['numFailedTests']
|
41
|
+
specs_plural = specs == 1 ? '' : 's'
|
42
|
+
failed_plural = failed == 1 ? '' : 's'
|
43
|
+
|
44
|
+
Formatter.info("Finished in #{request.elapsed_seconds} seconds")
|
45
|
+
|
46
|
+
pending = r['numPendingTests'] > 0 ? " #{r['numPendingTests']} pending," : ''
|
47
|
+
message = "#{specs} spec#{specs_plural}," \
|
48
|
+
"#{pending} #{failed} failure#{failed_plural}"
|
49
|
+
full_message = "#{message}\nin #{request.elapsed_seconds} seconds"
|
50
|
+
|
51
|
+
if failed.zero?
|
52
|
+
Formatter.success(message)
|
53
|
+
Formatter.notify(full_message, title: 'Jest suite passed')
|
54
|
+
else
|
55
|
+
Formatter.error(
|
56
|
+
collect_spec_error_messages(r['testResults']).join("\n")
|
57
|
+
)
|
58
|
+
error_title = collect_spec_error_titles(r['testResults']).join("\n")
|
59
|
+
Formatter.notify("#{error_title}\n#{full_message}",
|
60
|
+
title: 'Jest test run failed', image: :failed, priority: 2)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def collect_spec_error_titles(specs)
|
65
|
+
specs.select { |s| s['status'] == 'failed' }.map do |s|
|
66
|
+
result = s['assertionResults'].detect { |res| res['status'] == 'failed' }
|
67
|
+
result ? result['title'] : 'Unknown failure'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def collect_spec_error_messages(specs)
|
72
|
+
specs.select { |s| s['status'] == 'failed' }.map do |s|
|
73
|
+
s['message']
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'concurrent/array'
|
2
|
+
require 'concurrent/atomic/atomic_boolean'
|
3
|
+
require 'pty'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Guard
|
7
|
+
class Jest < Plugin
|
8
|
+
|
9
|
+
class Server
|
10
|
+
CR = 13.chr
|
11
|
+
|
12
|
+
attr_reader :stdout, :stdin, :pid, :last_result, :options, :cmd, :pending
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
reload(options)
|
16
|
+
@work_in_progress = Concurrent::AtomicBoolean.new(false)
|
17
|
+
@pending = Concurrent::Array.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def run(request)
|
21
|
+
start unless alive?
|
22
|
+
pending << request
|
23
|
+
work_fifo_queue
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def failed?
|
28
|
+
@pid && !alive?
|
29
|
+
end
|
30
|
+
|
31
|
+
def busy?
|
32
|
+
@work_in_progress.true?
|
33
|
+
end
|
34
|
+
|
35
|
+
def wait_until_not_busy
|
36
|
+
sleep(0.1) while busy?
|
37
|
+
end
|
38
|
+
|
39
|
+
def start
|
40
|
+
@threads = []
|
41
|
+
@work_in_progress.make_true
|
42
|
+
@directory ? Dir.chdir(@directory) { spawn } : spawn
|
43
|
+
@threads << Thread.new do
|
44
|
+
@stdout.each { |line| record_result(line) }
|
45
|
+
end
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def alive?
|
50
|
+
return false unless pid
|
51
|
+
Process.kill(0, pid)
|
52
|
+
return true
|
53
|
+
rescue Errno::ESRCH # "No such process"
|
54
|
+
return false
|
55
|
+
rescue Errno::EPERM # "Operation not permitted, but it at least exists
|
56
|
+
return true
|
57
|
+
else
|
58
|
+
return true
|
59
|
+
end
|
60
|
+
|
61
|
+
def stop
|
62
|
+
stdin.write('q')
|
63
|
+
sleep(0.1)
|
64
|
+
return unless alive?
|
65
|
+
Process.kill("TERM", pid)
|
66
|
+
sleep(0.1)
|
67
|
+
Process.kill("KILL", pid) if alive?
|
68
|
+
@pid = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def reload(options)
|
72
|
+
@options = options
|
73
|
+
@directory = options[:directory]
|
74
|
+
@cmd = options[:jest_cmd] + ' --json --silent true --watch'
|
75
|
+
@cmd << " --config #{options[:config_file]}" if options[:config_file]
|
76
|
+
if alive?
|
77
|
+
stop
|
78
|
+
start
|
79
|
+
end
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def work_fifo_queue
|
86
|
+
return if busy? || pending.none?
|
87
|
+
request = pending.first
|
88
|
+
if request.all?
|
89
|
+
run_all
|
90
|
+
else
|
91
|
+
run_paths(request.paths)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def run_all
|
96
|
+
@work_in_progress.make_true
|
97
|
+
stdin.write('a')
|
98
|
+
end
|
99
|
+
|
100
|
+
def run_paths(paths)
|
101
|
+
@work_in_progress.make_true
|
102
|
+
stdin.write('p')
|
103
|
+
# the sleep values simply "seem to work ok" and may need refinement
|
104
|
+
sleep(0.1)
|
105
|
+
stdin.write(paths.join('|'))
|
106
|
+
sleep(0.1)
|
107
|
+
stdin.write(CR)
|
108
|
+
end
|
109
|
+
|
110
|
+
def record_result(line)
|
111
|
+
# looks vaguely jsonish if it starts with {"
|
112
|
+
return unless line.start_with?('{"')
|
113
|
+
begin
|
114
|
+
json = JSON.parse(line)
|
115
|
+
result = @pending.pop
|
116
|
+
result.satisfy(json) if result
|
117
|
+
@work_in_progress.make_false
|
118
|
+
work_fifo_queue
|
119
|
+
rescue => e
|
120
|
+
Jest.logger.warn e
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def spawn
|
125
|
+
Jest.logger.debug "starting jest with #{cmd}"
|
126
|
+
@stdout, @stdin, @pid = PTY.spawn(cmd)
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
metadata
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: guard-jest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nathan Stitt
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-01-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: guard-compat
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: concurrent-ruby
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.4
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.0.4
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.13'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.13'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.5'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.5'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: guard-rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '4.7'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '4.7'
|
97
|
+
description: Guard plugin for testing Javascript using the Jest test runner
|
98
|
+
email:
|
99
|
+
- nathan@stitt.org
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".rubocop.yml"
|
106
|
+
- ".travis.yml"
|
107
|
+
- Gemfile
|
108
|
+
- Guardfile
|
109
|
+
- LICENSE.txt
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- bin/console
|
113
|
+
- bin/setup
|
114
|
+
- guard-jest.gemspec
|
115
|
+
- lib/generators/guard_jest/install_generator.rb
|
116
|
+
- lib/generators/guard_jest/templates/Guardfile
|
117
|
+
- lib/guard/jest.rb
|
118
|
+
- lib/guard/jest/formatter.rb
|
119
|
+
- lib/guard/jest/run_request.rb
|
120
|
+
- lib/guard/jest/runner.rb
|
121
|
+
- lib/guard/jest/server.rb
|
122
|
+
- lib/guard/jest/version.rb
|
123
|
+
homepage: https://github.com/nathanstitt/guard-jest
|
124
|
+
licenses:
|
125
|
+
- MIT
|
126
|
+
metadata:
|
127
|
+
allowed_push_host: https://rubygems.org
|
128
|
+
post_install_message:
|
129
|
+
rdoc_options: []
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
requirements: []
|
143
|
+
rubyforge_project:
|
144
|
+
rubygems_version: 2.6.8
|
145
|
+
signing_key:
|
146
|
+
specification_version: 4
|
147
|
+
summary: Guard plugin for auto-running Jest specs
|
148
|
+
test_files: []
|