rainforest-cli 1.0.6 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 40e961e73be70a98532715ed684c099be7123ee2
4
- data.tar.gz: e9d7c1e437f736f86018c942c427d460214ba1e6
3
+ metadata.gz: 93bba481d733b42bc5f7f7fb501e931264c6e781
4
+ data.tar.gz: d4892987ecabb73e6fd9063e409052b54ac849e6
5
5
  SHA512:
6
- metadata.gz: 2af94476bfdda2979af022be631c24185bc3fa55aef4e555574ca5e96ccbe3515eaa26739d4dc4685517faf45873897c91a328433a85bada8300eebe009661c6
7
- data.tar.gz: f802da9550fd0821e5a265da33abe411047a28cea5c2e588fe7a2864cea4a07eafaaf82220c9c14226e048182c7b803de354ec5eece128c6b5f3911e3a7a0724
6
+ metadata.gz: 1e2b9375fef282917fd66997b4a11715740ea66de6420b8c6e8d1af452fe96cc09a59258bf363a3fda725024a70cc43952c29c51aaf4450d624a10581f91acbd
7
+ data.tar.gz: f829dfa712a368139bd81590fd47022f05b515bc7f3bb2aa4224854f3f1828b42f0f57027af21081dbe34a387a2691fd1d549426d2289e71871c2c6028c6d975
data/.gitignore CHANGED
@@ -18,3 +18,4 @@ tmp
18
18
  tags
19
19
 
20
20
  *.csv
21
+ spec/rainforest
data/.rvmrc CHANGED
@@ -1 +1 @@
1
- rvm use 2.2.0@rainforest-cli --create
1
+ rvm use 2.3.0@rainforest-cli --create
data/.travis.yml CHANGED
@@ -1,5 +1,7 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 2.3.0
4
+ - 2.2.3
3
5
  - 2.1.2
4
6
  - 2.0.0
5
7
  - 1.9.3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # Rainforest CLI Changelog
2
2
 
3
+ ## 1.1.0 - 6th January 2016
4
+ - Added support for tests stored in plain text format, so you can store them locally
5
+
3
6
  ## 1.0.5 - 25th August 2015
4
7
  - Added environment support (278f4fe9a1ca9f507fe1e4b11935d9c37056786b)
5
8
 
data/README.md CHANGED
@@ -56,7 +56,8 @@ The most popular options are:
56
56
  - `--browsers ie8` or `--browsers ie8,chrome` - specficy the browsers you wish to run against. This overrides the test own settings. Valid browsers are ie8, ie9, chrome, firefox and safari.
57
57
  - `--tag run-me` - only run tests which have this tag (recommended if you have lots of [test-steps](http://docs.rainforestqa.com/pages/example-test-suite.html#test_steps))!)
58
58
  - `--site-id` - only run tests for a specific site. Get in touch with us for help on getting that you site id if you are unable to.
59
- - `--conflict abort` - if you trigger rainforest more than once, anything running will be aborted and a fresh run started
59
+ - `--environment-id` - run your tests using this environment. Otherwise it will use your default environment
60
+ - `--conflict OPTION` - use the `abort` option to abort any runs in progress in the same environment as your new run. use the `abort-all` option to abort all runs in progress.
60
61
  - `--fg` - results in the foreground - rainforest-cli will not return until the run is complete. This is what you want to make the build pass / fail dependent on rainforest results
61
62
  - `--fail-fast` - fail the build as soon as the first failed result comes in. If you don't pass this it will wait until 100% of the run is done. Use with `--fg`.
62
63
  - `--custom-url` - use a custom url for this run. Example use case: an ad-hoc QA environment with [Fourchette](https://github.com/rainforestapp/fourchette). You will need to specify a `site_id` too for this to work. Note that we will be creating a new environment for this particular run.
data/bin/rainforest CHANGED
@@ -4,4 +4,4 @@ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), "../lib")
4
4
 
5
5
  require "rainforest/cli"
6
6
 
7
- Rainforest::Cli.start(ARGV)
7
+ RainforestCli.start(ARGV)
@@ -4,36 +4,58 @@ require "rainforest/cli/runner"
4
4
  require "rainforest/cli/http_client"
5
5
  require "rainforest/cli/git_trigger"
6
6
  require "rainforest/cli/csv_importer"
7
+ require "rainforest/cli/test_importer"
8
+ require "rainforest/cli/test_parser"
7
9
  require "erb"
8
10
  require "httparty"
9
11
  require "json"
10
12
  require "logger"
11
13
 
12
- module Rainforest
13
- module Cli
14
- def self.start(args)
15
- options = OptionParser.new(args)
16
- OptionParser.new(['--help']) if args.size == 0
14
+ module RainforestCli
15
+ def self.start(args)
16
+ options = OptionParser.new(args)
17
+ OptionParser.new(['--help']) if args.size == 0
17
18
 
18
- begin
19
- options.validate!
20
- rescue OptionParser::ValidationError => e
21
- logger.fatal e.message
22
- exit 2
23
- end
19
+ begin
20
+ options.validate!
21
+ rescue OptionParser::ValidationError => e
22
+ logger.fatal e.message
23
+ exit 2
24
+ end
24
25
 
26
+ case options.command
27
+ when 'run'
25
28
  runner = Runner.new(options)
26
29
  runner.run
30
+ when 'new'
31
+ t = TestImporter.new(options)
32
+ t.create_new
33
+ when 'validate'
34
+ t = TestImporter.new(options)
35
+ t.validate
36
+ when 'upload'
37
+ t = TestImporter.new(options)
38
+ t.upload
27
39
 
28
- true
40
+ # options.tags = ['ro']
41
+ # runner = Runner.new(options)
42
+ # runner.run
43
+ when 'export'
44
+ t = TestImporter.new(options)
45
+ t.export
46
+ else
47
+ logger.fatal "Unknown command"
48
+ exit 2
29
49
  end
30
50
 
31
- def self.logger
32
- @logger ||= Logger.new(STDOUT)
33
- end
51
+ true
52
+ end
34
53
 
35
- def self.logger=(logger)
36
- @logger = logger
37
- end
54
+ def self.logger
55
+ @logger ||= Logger.new(STDOUT)
56
+ end
57
+
58
+ def self.logger=(logger)
59
+ @logger = logger
38
60
  end
39
61
  end
@@ -4,49 +4,47 @@ require 'httparty'
4
4
  require 'parallel'
5
5
  require 'ruby-progressbar'
6
6
 
7
- module Rainforest
8
- module Cli
9
- class CSVImporter
10
- attr_reader :client
11
-
12
- def initialize name, file, token
13
- @generator_name = name
14
- @file = file
15
- @client = HttpClient.new token: token
7
+ module RainforestCli
8
+ class CSVImporter
9
+ attr_reader :client
10
+
11
+ def initialize name, file, token
12
+ @generator_name = name
13
+ @file = file
14
+ @client = HttpClient.new token: token
15
+ end
16
+
17
+ def row_data columns, values
18
+ Hash[(columns.map {|c| c['id']}).zip(values)]
19
+ end
20
+
21
+ def import
22
+ rows = []
23
+ CSV.foreach(@file, encoding: 'windows-1251:utf-8') do |row|
24
+ rows << row
16
25
  end
17
26
 
18
- def row_data columns, values
19
- Hash[(columns.map {|c| c['id']}).zip(values)]
27
+ # Create the generator
28
+ columns = rows.shift.map do |column|
29
+ {name: column.downcase.strip.gsub(/\s/, '_')}
20
30
  end
31
+ raise 'Invalid schema in CSV. You must include headers in first row.' if not columns
32
+
33
+ print "Creating custom step variable"
34
+ response = client.post "/generators", { name: @generator_name, description: @generator_name, columns: columns }
35
+ raise "Error creating custom step variable: #{response['error']}" if response['error']
36
+ puts "\t[OK]"
37
+
38
+ @columns = response['columns']
39
+ @generator_id = response['id']
40
+
41
+ puts "Uploading data..."
42
+ p = ProgressBar.create(title: 'Rows', total: rows.count, format: '%a %B %p%% %t')
21
43
 
22
- def import
23
- rows = []
24
- CSV.foreach(@file, encoding: 'windows-1251:utf-8') do |row|
25
- rows << row
26
- end
27
-
28
- # Create the generator
29
- columns = rows.shift.map do |column|
30
- {name: column.downcase.strip.gsub(/\s/, '_')}
31
- end
32
- raise 'Invalid schema in CSV. You must include headers in first row.' if not columns
33
-
34
- print "Creating custom step variable"
35
- response = client.post "/generators", { name: @generator_name, description: @generator_name, columns: columns }
36
- raise "Error creating custom step variable: #{response['error']}" if response['error']
37
- puts "\t[OK]"
38
-
39
- @columns = response['columns']
40
- @generator_id = response['id']
41
-
42
- puts "Uploading data..."
43
- p = ProgressBar.create(title: 'Rows', total: rows.count, format: '%a %B %p%% %t')
44
-
45
- # Insert the data
46
- Parallel.each(rows, in_threads: 16, finish: lambda { |item, i, result| p.increment }) do |row|
47
- response = client.post("/generators/#{@generator_id}/rows", {data: row_data(@columns, row)})
48
- raise response['error'] if response['error']
49
- end
44
+ # Insert the data
45
+ Parallel.each(rows, in_threads: 16, finish: lambda { |item, i, result| p.increment }) do |row|
46
+ response = client.post("/generators/#{@generator_id}/rows", {data: row_data(@columns, row)})
47
+ raise response['error'] if response['error']
50
48
  end
51
49
  end
52
50
  end
@@ -1,19 +1,17 @@
1
1
  require 'optparse'
2
2
 
3
- module Rainforest
4
- module Cli
5
- class GitTrigger
6
- def self.git_trigger_should_run?(commit_message)
7
- commit_message.include?('@rainforest')
8
- end
3
+ module RainforestCli
4
+ class GitTrigger
5
+ def self.git_trigger_should_run?(commit_message)
6
+ commit_message.include?('@rainforest')
7
+ end
9
8
 
10
- def self.extract_hashtags(commit_message)
11
- commit_message.scan(/#([\w_-]+)/).flatten.map {|s| s.gsub('#','') }
12
- end
9
+ def self.extract_hashtags(commit_message)
10
+ commit_message.scan(/#([\w_-]+)/).flatten.map {|s| s.gsub('#','') }
11
+ end
13
12
 
14
- def self.last_commit_message
15
- `git log -1 --pretty=%B`.strip
16
- end
13
+ def self.last_commit_message
14
+ `git log -1 --pretty=%B`.strip
17
15
  end
18
16
  end
19
17
  end
@@ -1,56 +1,57 @@
1
- module Rainforest
2
- module Cli
3
- class HttpClient
4
- API_URL = ENV.fetch("RAINFOREST_API_URL") do
5
- 'https://app.rainforestqa.com/api/1'
6
- end.freeze
7
-
8
- def initialize(options)
9
- @token = options.fetch(:token)
10
- end
1
+ module RainforestCli
2
+ class HttpClient
3
+ API_URL = ENV.fetch("RAINFOREST_API_URL") do
4
+ 'https://app.rainforestqa.com/api/1'
5
+ end.freeze
6
+
7
+ def initialize(options)
8
+ @token = options.fetch(:token)
9
+ end
11
10
 
12
- def delete(url, body = {})
13
- response = HTTParty.delete make_url(url), {
14
- body: body,
15
- headers: headers,
16
- }
11
+ def delete(url, body = {})
12
+ response = HTTParty.delete make_url(url), {
13
+ body: body,
14
+ headers: headers,
15
+ verify: false
16
+ }
17
17
 
18
- JSON.parse(response.body)
19
- end
18
+ JSON.parse(response.body)
19
+ end
20
20
 
21
- def post(url, body = {})
22
- response = HTTParty.post make_url(url), {
23
- body: body,
24
- headers: headers,
25
- }
21
+ def post(url, body = {})
22
+ response = HTTParty.post make_url(url), {
23
+ body: body,
24
+ headers: headers,
25
+ verify: false
26
+ }
26
27
 
27
- JSON.parse(response.body)
28
- end
28
+ JSON.parse(response.body)
29
+ end
29
30
 
30
- def get(url, body = {})
31
- response = HTTParty.get make_url(url), {
32
- body: body,
33
- headers: headers,
34
- }
35
-
36
- if response.code == 200
37
- JSON.parse(response.body)
38
- else
39
- nil
40
- end
41
- end
31
+ def get(url, body = {})
32
+ response = HTTParty.get make_url(url), {
33
+ body: body,
34
+ headers: headers,
35
+ verify: false
36
+ }
42
37
 
43
- private
44
- def make_url(url)
45
- File.join(API_URL, url)
38
+ if response.code == 200
39
+ JSON.parse(response.body)
40
+ else
41
+ nil
46
42
  end
43
+ end
47
44
 
48
- def headers
49
- {
50
- "CLIENT_TOKEN" => @token,
51
- "User-Agent" => "Rainforest-cli-#{Rainforest::Cli::VERSION}"
52
- }
53
- end
45
+ private
46
+ def make_url(url)
47
+ File.join(API_URL, url)
48
+ end
49
+
50
+ def headers
51
+ {
52
+ "CLIENT_TOKEN" => @token,
53
+ "User-Agent" => "Rainforest-cli-#{RainforestCli::VERSION}"
54
+ }
54
55
  end
55
56
  end
56
57
  end
@@ -1,133 +1,154 @@
1
1
  require 'optparse'
2
2
 
3
- module Rainforest
4
- module Cli
5
- class BrowserException < Exception
6
- def initialize browsers
7
- invalid_browsers = browsers - OptionParser::VALID_BROWSERS
8
- super "#{invalid_browsers.join(', ')} is not valid. Valid browsers: #{OptionParser::VALID_BROWSERS.join(', ')}"
9
- end
3
+ module RainforestCli
4
+ class BrowserException < Exception
5
+ def initialize browsers
6
+ invalid_browsers = browsers - OptionParser::VALID_BROWSERS
7
+ super "#{invalid_browsers.join(', ')} is not valid. Valid browsers: #{OptionParser::VALID_BROWSERS.join(', ')}"
10
8
  end
9
+ end
11
10
 
12
- class OptionParser
13
- attr_reader :command, :token, :tags, :conflict, :browsers, :site_id, :environment_id,
14
- :import_file_name, :import_name, :custom_url, :description, :folder
11
+ class OptionParser
12
+ attr_writer :file_name, :tags
13
+ attr_reader :command, :token, :tags, :conflict, :browsers, :site_id, :environment_id,
14
+ :import_file_name, :import_name, :custom_url, :description, :folder,
15
+ :debug, :file_name
15
16
 
16
- VALID_BROWSERS = %w{chrome firefox safari ie8 ie9}.freeze
17
+ VALID_BROWSERS = %w{chrome firefox safari ie8 ie9}.freeze
17
18
 
18
- def initialize(args)
19
- @args = args.dup
20
- @tags = []
21
- @browsers = nil
19
+ def initialize(args)
20
+ @args = args.dup
21
+ @tags = []
22
+ @browsers = nil
23
+ @require_token = true
24
+ @debug = false
22
25
 
23
- @parsed = ::OptionParser.new do |opts|
24
- opts.on("--import-variable-csv-file FILE", "Import step variables; CSV data") do |value|
25
- @import_file_name = value
26
- end
26
+ @parsed = ::OptionParser.new do |opts|
27
+ opts.on("--debug") do
28
+ @debug = true
29
+ end
27
30
 
28
- opts.on("--import-variable-name NAME", "Import step variables; Name of variable (note, will be replaced if exists)") do |value|
29
- @import_name = value
30
- end
31
+ opts.on("--file") do |value|
32
+ @file_name = value
33
+ end
31
34
 
32
- opts.on("--git-trigger", "Only run if the last commit contains @rainforestapp") do |value|
33
- @git_trigger = true
34
- end
35
+ opts.on("--import-variable-csv-file FILE", "Import step variables; CSV data") do |value|
36
+ @import_file_name = value
37
+ end
35
38
 
36
- opts.on("--fg", "Run the tests in foreground.") do |value|
37
- @foreground = value
38
- end
39
+ opts.on("--import-variable-name NAME", "Import step variables; Name of variable (note, will be replaced if exists)") do |value|
40
+ @import_name = value
41
+ end
39
42
 
40
- opts.on("--fail-fast", String, "Fail as soon as there is a failure (don't wait for completion)") do |value|
41
- @failfast = true
42
- end
43
+ opts.on("--git-trigger", "Only run if the last commit contains @rainforestapp") do |value|
44
+ @git_trigger = true
45
+ end
43
46
 
44
- opts.on("--token TOKEN", String, "Your rainforest API token.") do |value|
45
- @token = value
46
- end
47
+ opts.on("--fg", "Run the tests in foreground.") do |value|
48
+ @foreground = value
49
+ end
47
50
 
48
- opts.on("--tag TAG", String, "A tag to run the tests with") do |value|
49
- @tags << value
50
- end
51
+ opts.on("--fail-fast", String, "Fail as soon as there is a failure (don't wait for completion)") do |value|
52
+ @failfast = true
53
+ end
51
54
 
52
- opts.on("--folder ID", "Run tests in the specified folders") do |value|
53
- @folder = value
54
- end
55
+ opts.on("--token TOKEN", String, "Your rainforest API token.") do |value|
56
+ @token = value
57
+ end
55
58
 
56
- opts.on("--browsers LIST", "Run against the specified browsers") do |value|
57
- @browsers = value.split(',').map{|x| x.strip.downcase }.uniq
59
+ opts.on("--tag TAG", String, "A tag to run the tests with") do |value|
60
+ @tags << value
61
+ end
58
62
 
59
- raise BrowserException, @browsers unless (@browsers - VALID_BROWSERS).empty?
60
- end
63
+ opts.on("--folder ID", "Run tests in the specified folders") do |value|
64
+ @folder = value
65
+ end
61
66
 
62
- opts.on("--conflict MODE", String, "How should Rainforest handle existing in progress runs?") do |value|
63
- @conflict = value
64
- end
67
+ opts.on("--browsers LIST", "Run against the specified browsers") do |value|
68
+ @browsers = value.split(',').map{|x| x.strip.downcase }.uniq
65
69
 
66
- opts.on("--environment-id ID", Integer, "Run using this environment. If excluded, will use your default") do |value|
67
- @environment_id = value
68
- end
70
+ raise BrowserException, @browsers unless (@browsers - VALID_BROWSERS).empty?
71
+ end
69
72
 
70
- opts.on("--site-id ID", Integer, "Only run tests for a specific site") do |value|
71
- @site_id = value
72
- end
73
+ opts.on("--conflict MODE", String, "How should Rainforest handle existing in progress runs?") do |value|
74
+ @conflict = value
75
+ end
73
76
 
74
- opts.on("--custom-url URL", String, "Use a custom url for this run. You will need to specify a site_id too for this to work.") do |value|
75
- @custom_url = value
76
- end
77
+ opts.on("--environment-id ID", Integer, "Run using this environment. If excluded, will use your default") do |value|
78
+ @environment_id = value
79
+ end
77
80
 
78
- opts.on("--description DESCRIPTION", "Add a description for the run.") do |value|
79
- @description = value
80
- end
81
+ opts.on("--site-id ID", Integer, "Only run tests for a specific site") do |value|
82
+ @site_id = value
83
+ end
81
84
 
82
- opts.on_tail("--help", "Display help message and exit") do |value|
83
- puts opts
84
- exit 0
85
- end
85
+ opts.on("--custom-url URL", String, "Use a custom url for this run. You will need to specify a site_id too for this to work.") do |value|
86
+ @custom_url = value
87
+ end
86
88
 
87
- end.parse!(@args)
89
+ opts.on("--description DESCRIPTION", "Add a description for the run.") do |value|
90
+ @description = value
91
+ end
88
92
 
89
- @command = @args.shift
90
- @tests = @args.dup
91
- end
93
+ opts.on_tail("--help", "Display help message and exit") do |value|
94
+ puts opts
95
+ exit 0
96
+ end
92
97
 
93
- def tests
94
- @tests
95
- end
98
+ end.parse!(@args)
96
99
 
97
- def git_trigger?
98
- @git_trigger
99
- end
100
+ @command = @args.shift
100
101
 
101
- def failfast?
102
- @failfast
102
+ if @command == 'new'
103
+ @file_name = @args.shift
103
104
  end
104
105
 
105
- def foreground?
106
- @foreground
107
- end
106
+ @tests = @args.dup
107
+
108
+ # A couple of commands don't need the token
109
+ token_not_required = %w{new validate}
110
+ @require_token = false if token_not_required.include?(@command)
111
+ end
112
+
113
+ def tests
114
+ @tests
115
+ end
116
+
117
+ def git_trigger?
118
+ @git_trigger
119
+ end
120
+
121
+ def failfast?
122
+ @failfast
123
+ end
124
+
125
+ def foreground?
126
+ @foreground
127
+ end
108
128
 
109
- def validate!
129
+ def validate!
130
+ if @require_token
110
131
  unless token
111
132
  raise ValidationError, "You must pass your API token using: --token TOKEN"
112
133
  end
134
+ end
113
135
 
114
- if custom_url && site_id.nil?
115
- raise ValidationError, "The site-id and custom-url options are both required."
116
- end
117
-
118
- if import_file_name && import_name
119
- unless File.exists?(import_file_name)
120
- raise ValidationError, "Input file: #{import_file_name} not found"
121
- end
136
+ if custom_url && site_id.nil?
137
+ raise ValidationError, "The site-id and custom-url options are both required."
138
+ end
122
139
 
123
- elsif import_file_name || import_name
124
- raise ValidationError, "You must pass both --import-variable-csv-file and --import-variable-name"
140
+ if import_file_name && import_name
141
+ unless File.exists?(import_file_name)
142
+ raise ValidationError, "Input file: #{import_file_name} not found"
125
143
  end
126
- true
127
- end
128
144
 
129
- class ValidationError < RuntimeError
145
+ elsif import_file_name || import_name
146
+ raise ValidationError, "You must pass both --import-variable-csv-file and --import-variable-name"
130
147
  end
148
+ true
149
+ end
150
+
151
+ class ValidationError < RuntimeError
131
152
  end
132
153
  end
133
154
  end