yordi_tests 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +124 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/yordi +3 -0
- data/lib/yordi_tests/cli.rb +78 -0
- data/lib/yordi_tests/client.rb +59 -0
- data/lib/yordi_tests/data_manager.rb +190 -0
- data/lib/yordi_tests/generators/report.html.erb +100 -0
- data/lib/yordi_tests/generators/report.rb +19 -0
- data/lib/yordi_tests/image_compare.rb +92 -0
- data/lib/yordi_tests/local_store.rb +75 -0
- data/lib/yordi_tests/version.rb +3 -0
- data/lib/yordi_tests.rb +41 -0
- data/yordi_tests.gemspec +41 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 783043661bdec27c6adf4c9f4d3b1d545d675ef4
|
4
|
+
data.tar.gz: 64fc1f61a1a524e3e61aaedc4931c6dce74523b7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7784cb732fcd3cb3794b3058d39efa00cace7d44dca2778f3a2c09549831997dffa5ce058063f553af65d476f34de4fe7334bb4bf0c2394a61efe5fbead0119f
|
7
|
+
data.tar.gz: 4a2b864d577a4d56f762ca2d57d62226c5e83fa08cee69aeb68f2dc35b0078c7f511621d5961670ab75bf4a8d30f62ed35f385cba8ccf714325079e5af65d002
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Brian OQR
|
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,124 @@
|
|
1
|
+
# YordiTests CLI
|
2
|
+
|
3
|
+
The yordi_tests gem is a companion for [YordiTests](https://yorditests.com).
|
4
|
+
This project is rapidly changing and in alpha phase, use at your own risk.
|
5
|
+
|
6
|
+
Basically YordiTests compares screenshots with benchmarks and can apply masks to hide dynamic content.
|
7
|
+
|
8
|
+
It only works with PNG files and requires the files to have the extensions .png for now and does not recurse directories.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
<!-- Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'yordi_tests'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
Or -->
|
22
|
+
Install it yourself as:
|
23
|
+
|
24
|
+
$ gem install yordi_tests
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
It's a CLI with Thor so calling help on the command line will give you more details
|
28
|
+
``` ruby
|
29
|
+
yordi help
|
30
|
+
```
|
31
|
+
To start a .yordi_tests directory must be initialized, either of these commands will create the directory relative
|
32
|
+
to the where you execute it. Using an apikey will automatically sync your local test directory with the benchmarks online.
|
33
|
+
``` ruby
|
34
|
+
yordi help init # for all the options
|
35
|
+
yordi init
|
36
|
+
yordi init --api_key apikey_from_yorditests.com
|
37
|
+
```
|
38
|
+
To test your screenshots
|
39
|
+
``` ruby
|
40
|
+
yordi help test # for more options
|
41
|
+
yordi test /path/to/screenshots
|
42
|
+
yordi test /path/to/screenshots --sync-all # push the new screenshots to yorditests.com
|
43
|
+
yordi test /path/to/screenshots --clean-dir # after the tests it deletes the screenshots tested
|
44
|
+
```
|
45
|
+
Show the report (only works on OSX, the report is located at .yordi_tests/report.html)
|
46
|
+
``` ruby
|
47
|
+
yordi open_report # opens the last generated html report
|
48
|
+
```
|
49
|
+
Refetch benchmarks and masks from online
|
50
|
+
``` ruby
|
51
|
+
yordi help fetch # for more options
|
52
|
+
yordi fetch -bm # fetches all benchmark details and masks from online for testing
|
53
|
+
```
|
54
|
+
Push benchmarks and masks to online store (not yet implemented)
|
55
|
+
``` ruby
|
56
|
+
yordi help push # for more options
|
57
|
+
yordi push -bm # pushes all benchmark details and masks to online store
|
58
|
+
```
|
59
|
+
### Standalone Scenario
|
60
|
+
So you have run an appium or selenium test that made a lot of screenshots.
|
61
|
+
Wherever you want to run your yordi test do the following.
|
62
|
+
``` ruby
|
63
|
+
yordi init
|
64
|
+
yordi test path/to/screenshot
|
65
|
+
yordi open_report
|
66
|
+
```
|
67
|
+
At which point you will see a report and all of your screens will have passed the test as there was no benchmark.
|
68
|
+
So run your appium or selenium tests again and create the screenshots a second time.
|
69
|
+
``` ruby
|
70
|
+
yordi test path/to/screenshot
|
71
|
+
yordi open_report
|
72
|
+
```
|
73
|
+
Now there is a good chance that some of your screenshots haved failed the test due to status bars with time or other
|
74
|
+
dynamic issues. If the screen changes are different but valid you need to mask this dynamic area to ensure 100% on the test.
|
75
|
+
This is easiest done with YordiTests.com editor, however the masks are simple JSON areas.
|
76
|
+
|
77
|
+
So if you don't want to use YordiTests.com you can manually open the config.json at .yordi_tests/config.json
|
78
|
+
and add some rectangles for masks as a value for masked_area: in the json,
|
79
|
+
you can use any image editing program to get the x, y, width and height you want
|
80
|
+
|
81
|
+
Here is an example of a masked_area value, masked_area is an array.
|
82
|
+
``` javascript
|
83
|
+
masked_area: [
|
84
|
+
{
|
85
|
+
"x": 0,
|
86
|
+
"y": 0,
|
87
|
+
"width": 800,
|
88
|
+
"height": 48
|
89
|
+
},
|
90
|
+
{
|
91
|
+
"x": 419,
|
92
|
+
"y": 385,
|
93
|
+
"width": 619,
|
94
|
+
"height": 372
|
95
|
+
},
|
96
|
+
|
97
|
+
|
98
|
+
]
|
99
|
+
```
|
100
|
+
In the config.json the property masked_area: is available in the root and in individual screens in the test_benchmarks array.
|
101
|
+
Placing a mask in root will have the mask be applied to all screens during the comparision. Placing the mask on a screen will apply the mask only to that screen.
|
102
|
+
So the screens get compared after both the benchmark and resulting screenshot have all global and local masks applied.
|
103
|
+
|
104
|
+
At some point you will want to update your benchmarks, for example if the design has changed. If you are using YordiTests.com
|
105
|
+
then if you sync your tests with the online store you can make the screenshot the benchmark online
|
106
|
+
and then re-fetch the benchmarks from the comand with `yordi fetch -b`.
|
107
|
+
|
108
|
+
However if you are running in standalone mode to update a benchmark at the moment you must manually copy the
|
109
|
+
screenshot into the benchmark folder in the subdirectories of .yordi_tests. Eventually a command will be added to make this easier.
|
110
|
+
|
111
|
+
|
112
|
+
## Development
|
113
|
+
|
114
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
115
|
+
|
116
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
117
|
+
|
118
|
+
## Contributing
|
119
|
+
|
120
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/PurelyCode/gem-yorditests.
|
121
|
+
|
122
|
+
## License
|
123
|
+
|
124
|
+
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 "yordi_tests"
|
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(__FILE__)
|
data/bin/setup
ADDED
data/exe/yordi
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'yordi_tests'
|
3
|
+
require 'yordi_tests/data_manager'
|
4
|
+
module YordiTests
|
5
|
+
class CLI < Thor
|
6
|
+
package_name 'YordiTests'
|
7
|
+
|
8
|
+
desc 'init --api_key', 'Initializes a Yordi test directory at .yordi_tests relative to where you execute the command'
|
9
|
+
method_option :api_key, type: :string, aliases: '-k', desc: 'APIKEY from YordiTest.com if you want to sync results online'
|
10
|
+
|
11
|
+
def init
|
12
|
+
puts 'init'
|
13
|
+
if Dir.exist?(YORDI_DIR)
|
14
|
+
puts 'Already initialized'
|
15
|
+
exit(1)
|
16
|
+
end
|
17
|
+
puts "Creating #{YORDI_DIR} directory"
|
18
|
+
DataManager.create_store
|
19
|
+
|
20
|
+
store = DataManager.default_store(options.api_key)
|
21
|
+
puts "Saving store #{CONFIG_FILE}"
|
22
|
+
DataManager.save_store store
|
23
|
+
|
24
|
+
return unless options.api_key
|
25
|
+
puts 'Fetching remote store.'
|
26
|
+
DataManager.fetch(true, true, nil)
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
desc 'test PATH_TO_SCREENSHOTS', 'Run a Yordi test on all images in folder'
|
31
|
+
method_option :name, type: :string, default: 'Generic Test', aliases: '-n', desc: 'Name for this test in report.'
|
32
|
+
method_option :clean_dir, type: :boolean, default: false, aliases: '-D', desc: 'This will DELETE all screenshots in PATH_TO_SCREENSHOTS after test completes.'
|
33
|
+
method_option :sync_all, type: :boolean, default: false, aliases: '-a', desc: 'Push all screens to YordiTests.com for evaluation.'
|
34
|
+
method_option :sync_failures, type: :boolean, default: false, aliases: '-f', desc: 'Push only failed screens to YordiTests.com for evaluation.'
|
35
|
+
method_option :filenames, type: :array, aliases: '-i', desc: 'Only test these specific files'
|
36
|
+
method_option :screens, type: :array, aliases: '-s', desc: 'Give the files a name other than there filename in the report (best used with -i option)'
|
37
|
+
|
38
|
+
def test(path_to_screens)
|
39
|
+
puts "Testing #{path_to_screens} name: #{options.name}, clean_dir: #{options.clean_dir}, sync_all: #{options.sync_all}, sync_failures: #{options.sync_failures}, filenames: #{options.filenames}, screens: #{options.screens}"
|
40
|
+
|
41
|
+
DataManager.run_test(path_to_screens, options.name, options.clean_dir, options.sync_all, options.sync_failures, options.filenames, options.screens)
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'make_report', 'Regenerate a Yordi report based on json report'
|
45
|
+
|
46
|
+
def make_report
|
47
|
+
DataManager.generate_report
|
48
|
+
end
|
49
|
+
|
50
|
+
desc 'fetch', 'Fetch remote data from YordiTests.com'
|
51
|
+
method_option :benchmarks, type: :boolean, default: false, aliases: '-b', desc: 'Fetch benchmarks'
|
52
|
+
method_option :masks, type: :boolean, default: false, aliases: '-m', desc: 'Fetch masks'
|
53
|
+
method_option :screens, type: :array, aliases: '-s', desc: 'Only fetch items associated with this list of screen names'
|
54
|
+
|
55
|
+
def fetch
|
56
|
+
puts "Fetching with options benchmarks: #{options.benchmarks}, masks: #{options.masks}, screens: #{options.screens}"
|
57
|
+
DataManager.fetch(options.benchmarks, options.masks, options.screens)
|
58
|
+
end
|
59
|
+
|
60
|
+
desc 'push', 'Push local data to YordiTests.com'
|
61
|
+
method_option :benchmarks, type: :boolean, default: false, aliases: '-b', desc: 'Push benchmarks'
|
62
|
+
method_option :masks, type: :boolean, default: false, aliases: '-m', desc: 'Push masks'
|
63
|
+
method_option :screens, type: :array, aliases: '-s', desc: 'Only push items associated with this list of screen names'
|
64
|
+
|
65
|
+
def push(path_to_screens)
|
66
|
+
puts "test #{path_to_screens}"
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
desc 'open_report', 'Open the last generated report'
|
71
|
+
|
72
|
+
def open_report
|
73
|
+
system "open #{REPORT_HTML}"
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
require 'yordi_tests'
|
3
|
+
module YordiTests
|
4
|
+
|
5
|
+
module Client
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def apikey
|
9
|
+
@api_key
|
10
|
+
end
|
11
|
+
|
12
|
+
def apikey=(v)
|
13
|
+
@api_key = v
|
14
|
+
end
|
15
|
+
|
16
|
+
def auth_header
|
17
|
+
"Bearer #{@api_key}"
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param [String] test_name
|
21
|
+
def start(test_name)
|
22
|
+
response = RestClient::Request.execute(method: :get, url: HOST + START_PATH,
|
23
|
+
payload: {name: test_name}, headers: {authorization: auth_header})
|
24
|
+
@test_key = JSON(response)['testkey']
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param [String] screenshot_path
|
29
|
+
# @param [String] screen_name
|
30
|
+
def upload(screenshot_path, screen_name)
|
31
|
+
RestClient.post(HOST + UPLOAD_PATH,
|
32
|
+
{screenshot: File.new(screenshot_path),
|
33
|
+
screenname: screen_name, testkey: @test_key},
|
34
|
+
authorization: auth_header)
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def stop
|
39
|
+
RestClient::Request.execute(method: :get, url: HOST + STOP_PATH,
|
40
|
+
payload: {testkey: @test_key}, headers: {authorization: auth_header})
|
41
|
+
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def fetch_application
|
46
|
+
response = RestClient::Request.execute(method: :get, url: HOST + FETCH_APPLICATION,
|
47
|
+
payload: {}, headers: {authorization: auth_header})
|
48
|
+
JSON(response)
|
49
|
+
end
|
50
|
+
|
51
|
+
def fetch_benchmark(screen_name)
|
52
|
+
RestClient::Request.execute(method: :get, url: HOST + FETCH_BENCHMARK,
|
53
|
+
payload: {screenname: screen_name}, headers: {authorization: auth_header})
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'yordi_tests'
|
2
|
+
require 'yordi_tests/local_store'
|
3
|
+
require 'yordi_tests/image_compare'
|
4
|
+
require 'yordi_tests/generators/report'
|
5
|
+
module YordiTests
|
6
|
+
module DataManager
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def default_store(apikey)
|
10
|
+
{title: 'YordiTests', apikey: apikey, test_benchmarks: []}
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_store
|
14
|
+
Dir.mkdir(YORDI_DIR) unless Dir.exist? YORDI_DIR
|
15
|
+
end
|
16
|
+
|
17
|
+
def read_store
|
18
|
+
read_json(CONFIG_FILE)
|
19
|
+
end
|
20
|
+
|
21
|
+
def read_report
|
22
|
+
read_json(REPORT_FILE)
|
23
|
+
end
|
24
|
+
|
25
|
+
def read_json(path)
|
26
|
+
if File.exist? path
|
27
|
+
file = File.read path
|
28
|
+
JSON(file)
|
29
|
+
else
|
30
|
+
puts "No file at: #{path} did you call initialize this directory"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def save_store(store_hash)
|
36
|
+
File.open(CONFIG_FILE, 'w') {|file| file.write(store_hash.to_json)}
|
37
|
+
end
|
38
|
+
|
39
|
+
# Test entry from the CLI
|
40
|
+
def run_test(path_to_screens, name, clean_dir, sync_all, sync_failures, filenames, screens)
|
41
|
+
local_store = LocalStore.new(read_store)
|
42
|
+
if filenames
|
43
|
+
files = []
|
44
|
+
filenames.each do |filename|
|
45
|
+
files << path_to_screens + '/' + filename
|
46
|
+
end
|
47
|
+
else
|
48
|
+
files = Dir.glob(path_to_screens + '/*.png').sort
|
49
|
+
end
|
50
|
+
|
51
|
+
Dir.mkdir(BENCHMARKS_PATH) unless Dir.exist? BENCHMARKS_PATH
|
52
|
+
Dir.mkdir(SCREENS_PATH) unless Dir.exist? SCREENS_PATH
|
53
|
+
responses = []
|
54
|
+
files.each_with_index do |item, index|
|
55
|
+
puts "Testing #{item}"
|
56
|
+
screenname = (!screens.nil? && screens.size > index) ? screens[index] : File.basename(item, '.*')
|
57
|
+
benchmark = local_store.benchmark_by_screenname(screenname)
|
58
|
+
local_name = benchmark.nil? ? sanitize(screenname) + File.extname(item) : benchmark[LOCAL_FILENAME]
|
59
|
+
benchmark_path = BENCHMARKS_PATH + '/' + local_name
|
60
|
+
screenshot_path = SCREENS_PATH + '/' + local_name
|
61
|
+
FileUtils.copy item, screenshot_path
|
62
|
+
if benchmark.nil?
|
63
|
+
FileUtils.copy item, benchmark_path
|
64
|
+
benchmark = {SCREENNAME => screenname,
|
65
|
+
MASKED_AREA => nil,
|
66
|
+
FILENAME => local_name,
|
67
|
+
LOCAL_FILENAME => local_name}
|
68
|
+
local_store.update_benchmark(benchmark)
|
69
|
+
save_store local_store.data
|
70
|
+
end
|
71
|
+
global_mask = local_store.get(MASKED_AREA)
|
72
|
+
screen_mask = benchmark.nil? ? nil : benchmark[MASKED_AREA]
|
73
|
+
response = ImageCompare.perform(benchmark_path, screenshot_path, global_mask, screen_mask)
|
74
|
+
response[SCREENNAME] = screenname
|
75
|
+
response[LOCAL_FILENAME] = local_name
|
76
|
+
responses << response
|
77
|
+
File.delete item if clean_dir
|
78
|
+
end
|
79
|
+
report_hash = {name: name, tests: responses}
|
80
|
+
File.open(REPORT_FILE, 'w') {|file| file.write(report_hash.to_json)}
|
81
|
+
|
82
|
+
# sync with yorditests.com if desired
|
83
|
+
sync_with_yordi local_store, sync_all, sync_failures if sync_all || sync_failures
|
84
|
+
generate_report
|
85
|
+
end
|
86
|
+
|
87
|
+
def generate_report
|
88
|
+
# generate report
|
89
|
+
YordiTests::Generators::Report.start([REPORT_HTML, read_report])
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
def sync_with_yordi(store, sync_all, sync_failures)
|
94
|
+
# sync with remote
|
95
|
+
puts 'Syncing with YordiTests'
|
96
|
+
report = read_report
|
97
|
+
client = YordiTests.client
|
98
|
+
client.apikey = store.apikey if store.apikey
|
99
|
+
if !report.nil? && !report['tests'].empty?
|
100
|
+
reports = report['tests']
|
101
|
+
failures = 0
|
102
|
+
if sync_failures
|
103
|
+
reports.each do |item|
|
104
|
+
failures += 1 unless item['passed']
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
if sync_all || failures > 0
|
109
|
+
client.start report['name']
|
110
|
+
reports.each do |item|
|
111
|
+
next unless sync_all || !item['passed']
|
112
|
+
puts item[SCREENNAME]
|
113
|
+
benchmark = store.benchmark_by_screenname(item[SCREENNAME])
|
114
|
+
filename = SCREENS_PATH + '/' + benchmark[LOCAL_FILENAME]
|
115
|
+
client.upload filename, item[SCREENNAME]
|
116
|
+
end
|
117
|
+
client.stop
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Fetch entry from the CLI
|
123
|
+
def fetch(get_benchmarks, get_masks, screens)
|
124
|
+
local_store = LocalStore.new(read_store)
|
125
|
+
## no api key
|
126
|
+
return unless local_store.apikey
|
127
|
+
|
128
|
+
client = YordiTests.client
|
129
|
+
client.apikey = local_store.apikey
|
130
|
+
remote_data = client.fetch_application
|
131
|
+
|
132
|
+
## no remote store
|
133
|
+
return unless remote_data
|
134
|
+
|
135
|
+
remote_store = LocalStore.new(remote_data)
|
136
|
+
|
137
|
+
update_store_base(local_store, remote_store)
|
138
|
+
replace_benchmarks(client, local_store, remote_store, screens) if get_benchmarks
|
139
|
+
replace_masks(local_store, remote_store, screens) if get_masks
|
140
|
+
|
141
|
+
save_store local_store.data
|
142
|
+
end
|
143
|
+
|
144
|
+
def update_store_base(local_store, remote_store)
|
145
|
+
local_store.put(TITLE, remote_store.get(TITLE))
|
146
|
+
local_store.put(APIKEY, remote_store.apikey)
|
147
|
+
end
|
148
|
+
|
149
|
+
def replace_benchmarks(client, local_store, remote_store, screens)
|
150
|
+
# no api key
|
151
|
+
screens = remote_store.all_screens unless screens
|
152
|
+
|
153
|
+
Dir.mkdir(BENCHMARKS_PATH) unless Dir.exist? BENCHMARKS_PATH
|
154
|
+
|
155
|
+
screens.each do |screen|
|
156
|
+
benchmark = remote_store.benchmark_by_screenname(screen)
|
157
|
+
next unless benchmark
|
158
|
+
|
159
|
+
benchmark[LOCAL_FILENAME] = sanitize(screen) + File.extname(benchmark[FILENAME])
|
160
|
+
|
161
|
+
# download benchmark
|
162
|
+
puts "Downloading #{screen}"
|
163
|
+
benchmark_image = client.fetch_benchmark(screen)
|
164
|
+
|
165
|
+
# update store to reflex file name of benchmark
|
166
|
+
file_path = "#{BENCHMARKS_PATH}/#{benchmark[LOCAL_FILENAME]}"
|
167
|
+
File.open(file_path, 'w') {|file| file.write(benchmark_image)}
|
168
|
+
local_store.update_benchmark(benchmark)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def replace_masks(local_store, remote_store, screens)
|
173
|
+
# global mask
|
174
|
+
local_store.put(MASKED_AREA, remote_store.get(MASKED_AREA))
|
175
|
+
screens = remote_store.all_screens unless screens
|
176
|
+
screens.each do |screen|
|
177
|
+
benchmark = remote_store.benchmark_by_screenname(screen)
|
178
|
+
# download benchmark
|
179
|
+
puts "Update mask for #{screen}"
|
180
|
+
local_store.update_mask(benchmark) if benchmark
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
def sanitize(filename)
|
186
|
+
# Remove any character that aren't 0-9, A-Z, or a-z
|
187
|
+
filename.gsub(/[^0-9A-Z]/i, '_')
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title><%= report_data['name'] %></title>
|
4
|
+
<style>
|
5
|
+
body {
|
6
|
+
font-family: "Roboto", Helvetica, sans-serif;
|
7
|
+
background-color: #8F5699;
|
8
|
+
}
|
9
|
+
|
10
|
+
.passed {
|
11
|
+
display: none;
|
12
|
+
}
|
13
|
+
|
14
|
+
table {
|
15
|
+
background: #fff;
|
16
|
+
border-radius: 4px;
|
17
|
+
padding: 10px;
|
18
|
+
margin: 2%;
|
19
|
+
}
|
20
|
+
|
21
|
+
h1 {
|
22
|
+
color:white;
|
23
|
+
margin-left: 2%;
|
24
|
+
}
|
25
|
+
|
26
|
+
h2 {
|
27
|
+
margin-top: 20px;
|
28
|
+
}
|
29
|
+
|
30
|
+
.toggle {
|
31
|
+
margin-right: 2%;
|
32
|
+
display: inline-block;
|
33
|
+
color: white;
|
34
|
+
float: right;
|
35
|
+
clear: both;
|
36
|
+
}
|
37
|
+
|
38
|
+
</style>
|
39
|
+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
|
40
|
+
<script>
|
41
|
+
$(document).ready(function () {
|
42
|
+
$('.toggle').on('click', function () {
|
43
|
+
if ($('.passed').css('display') == 'none') {
|
44
|
+
$('.passed').css('display', 'table-row');
|
45
|
+
$('.toggle').text('Hide successful tests');
|
46
|
+
} else {
|
47
|
+
|
48
|
+
$('.passed').css('display', 'none');
|
49
|
+
$('.toggle').text('Show successful tests');
|
50
|
+
}
|
51
|
+
})
|
52
|
+
});
|
53
|
+
</script>
|
54
|
+
</head>
|
55
|
+
<body>
|
56
|
+
<a class="toggle" href="#">
|
57
|
+
Show successful tests
|
58
|
+
</a>
|
59
|
+
<h1><%= report_data['name'] %></h1>
|
60
|
+
|
61
|
+
<table style="width:96%">
|
62
|
+
|
63
|
+
<% report_data['tests'].each do |item|
|
64
|
+
class_name = item['passed'] ? 'passed' : 'failed'
|
65
|
+
%>
|
66
|
+
<tr class="<%= class_name %>">
|
67
|
+
<td colspan="3">
|
68
|
+
<h2> <%= item['screenname'] %></h2>
|
69
|
+
<p>
|
70
|
+
<b style="font-size:24px;"><%= item['passed'] ? 'Passed' : 'Failed' %></b><br/>
|
71
|
+
<%= item['passed'] ? item['message'] : '' %><br/>
|
72
|
+
</p>
|
73
|
+
|
74
|
+
</td>
|
75
|
+
</tr>
|
76
|
+
<tr class="<%= class_name %>">
|
77
|
+
<td>
|
78
|
+
<b>Benchmark</b><br/>
|
79
|
+
<img style="width:100%" src="benchmarks/<%= item['local_filename'] %>"/>
|
80
|
+
|
81
|
+
</td>
|
82
|
+
|
83
|
+
<td>
|
84
|
+
<% if item['has_diff'] %>
|
85
|
+
<b>Diff</b><br/>
|
86
|
+
<img style="width:100%" src="screenshots/<%= item['local_filename'] %>.diff.png"/>
|
87
|
+
<% end %>
|
88
|
+
</td>
|
89
|
+
|
90
|
+
<td>
|
91
|
+
<b>Result</b><br/>
|
92
|
+
<img style="width:100%" src="benchmarks/<%= item['local_filename'] %>"/>
|
93
|
+
|
94
|
+
</td>
|
95
|
+
</tr>
|
96
|
+
<% end %>
|
97
|
+
|
98
|
+
</table>
|
99
|
+
</body>
|
100
|
+
</html>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'thor/group'
|
2
|
+
module YordiTests
|
3
|
+
module Generators
|
4
|
+
class Report < Thor::Group
|
5
|
+
include Thor::Actions
|
6
|
+
argument :report_path, type: :string
|
7
|
+
argument :report_data, type: :hash, default: {name: 'Unknown'}
|
8
|
+
|
9
|
+
def self.source_root
|
10
|
+
File.dirname(__FILE__)
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_report
|
14
|
+
template("report.html.erb", report_path, force: true)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'yordi_tests'
|
2
|
+
require 'mini_magick'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'yordi_tests/local_store'
|
5
|
+
module YordiTests
|
6
|
+
module ImageCompare
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def addMask(image_path, global_mask, screen_mask)
|
10
|
+
output_path = "#{image_path}#{SecureRandom.hex}.png"
|
11
|
+
convert = MiniMagick::Tool::Convert.new(whiny: false)
|
12
|
+
convert << image_path
|
13
|
+
convert.fill('white')
|
14
|
+
convert.stroke('none')
|
15
|
+
unless screen_mask.nil?
|
16
|
+
screen_mask.each do |item|
|
17
|
+
convert.draw("rectangle #{item['x']}, #{item['y']} #{item['x'] + item['width']},#{item['y'] + item['height']}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
unless global_mask.nil?
|
21
|
+
global_mask.each do |item|
|
22
|
+
convert.draw("rectangle #{item['x']}, #{item['y']} #{item['x'] + item['width']},#{item['y'] + item['height']}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
convert << output_path
|
26
|
+
|
27
|
+
convert.call do |stdout, stderr, status|
|
28
|
+
unless stderr.nil?
|
29
|
+
# error_count = stderr.to_i
|
30
|
+
end
|
31
|
+
end
|
32
|
+
output_path
|
33
|
+
end
|
34
|
+
|
35
|
+
def perform( benchmark_path, screenshot_path, global_mask, screen_mask)
|
36
|
+
response = { passed: false, message: 'None', has_diff: false, diff: nil}
|
37
|
+
|
38
|
+
#compare images
|
39
|
+
# begin
|
40
|
+
screenshot = MiniMagick::Image.open(screenshot_path)
|
41
|
+
benchmark_screenshot = MiniMagick::Image.open(benchmark_path)
|
42
|
+
if screenshot.nil?
|
43
|
+
response[:message] = 'Screen shot unreadable'
|
44
|
+
elsif benchmark_screenshot.nil?
|
45
|
+
response[:message] = 'Benchmark image unreadable'
|
46
|
+
elsif screenshot.width != benchmark_screenshot.width || screenshot.height != benchmark_screenshot.height
|
47
|
+
response[:message] = 'Screenshot has wrong dimensions'
|
48
|
+
|
49
|
+
else
|
50
|
+
if screenshot.signature == benchmark_screenshot.signature
|
51
|
+
response[:passed] = true
|
52
|
+
response[:message] = 'Screenshot is perfect'
|
53
|
+
else
|
54
|
+
compare_mask_one = addMask(benchmark_path, global_mask, screen_mask)
|
55
|
+
compare_mask_two = addMask(screenshot_path, global_mask, screen_mask)
|
56
|
+
|
57
|
+
image_diff = "#{screenshot_path}.diff.png"
|
58
|
+
compare = MiniMagick::Tool::Compare.new(whiny: false)
|
59
|
+
compare.metric('AE')
|
60
|
+
compare.fuzz('5%')
|
61
|
+
|
62
|
+
compare << compare_mask_one
|
63
|
+
compare << compare_mask_two
|
64
|
+
# compare.compose('Src')
|
65
|
+
compare << image_diff
|
66
|
+
|
67
|
+
error_count = 0
|
68
|
+
compare.call do |stdout, stderr, status|
|
69
|
+
unless stderr.nil?
|
70
|
+
error_count = stderr.to_i
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
if error_count.zero?
|
75
|
+
response[:passed] = true
|
76
|
+
response[:message] = 'Screenshot seems to be the same.'
|
77
|
+
else
|
78
|
+
response[:message] = 'Hmm, has something changed, WTF!'
|
79
|
+
end
|
80
|
+
|
81
|
+
response[:diff] = image_diff
|
82
|
+
response[:has_diff] = true
|
83
|
+
File.delete(compare_mask_one) unless compare_mask_one.nil?
|
84
|
+
File.delete(compare_mask_two) unless compare_mask_two.nil?
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
response
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'yordi_tests'
|
2
|
+
module YordiTests
|
3
|
+
BENCHMARKS = 'test_benchmarks'.freeze
|
4
|
+
SCREENNAME = 'screenname'.freeze
|
5
|
+
FILENAME = 'filename'.freeze
|
6
|
+
MASKED_AREA = 'masked_area'.freeze
|
7
|
+
APIKEY = 'apikey'.freeze
|
8
|
+
LOCAL_FILENAME = 'local_filename'.freeze
|
9
|
+
TITLE = 'title'.freeze
|
10
|
+
|
11
|
+
class LocalStore
|
12
|
+
|
13
|
+
def initialize(json_store)
|
14
|
+
@store = json_store
|
15
|
+
end
|
16
|
+
|
17
|
+
def apikey
|
18
|
+
get(APIKEY)
|
19
|
+
end
|
20
|
+
|
21
|
+
def data
|
22
|
+
@store
|
23
|
+
end
|
24
|
+
|
25
|
+
def put(key, data)
|
26
|
+
@store[key] = data
|
27
|
+
end
|
28
|
+
|
29
|
+
def get(key)
|
30
|
+
@store[key]
|
31
|
+
end
|
32
|
+
|
33
|
+
def all_screens
|
34
|
+
screens = []
|
35
|
+
@store[BENCHMARKS].each do |benchmark|
|
36
|
+
screens << benchmark[SCREENNAME]
|
37
|
+
end
|
38
|
+
screens
|
39
|
+
end
|
40
|
+
|
41
|
+
def update_benchmark(benchmark)
|
42
|
+
benchmark_pos = benchmark_pos_by_screenname(benchmark[SCREENNAME])
|
43
|
+
if benchmark_pos < 0
|
44
|
+
@store[BENCHMARKS] << benchmark.dup
|
45
|
+
else
|
46
|
+
@store[BENCHMARKS][benchmark_pos][SCREENNAME] = benchmark[SCREENNAME]
|
47
|
+
@store[BENCHMARKS][benchmark_pos][FILENAME] = benchmark[FILENAME]
|
48
|
+
@store[BENCHMARKS][benchmark_pos][LOCAL_FILENAME] = benchmark[LOCAL_FILENAME]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def update_mask(benchmark)
|
53
|
+
benchmark_pos = benchmark_pos_by_screenname(benchmark[SCREENNAME])
|
54
|
+
if benchmark_pos < 0
|
55
|
+
@store[BENCHMARKS] << benchmark.dup
|
56
|
+
else
|
57
|
+
@store[BENCHMARKS][benchmark_pos][MASKED_AREA] = benchmark[MASKED_AREA]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def benchmark_by_screenname(screenname)
|
62
|
+
@store[BENCHMARKS].each do |benchmark|
|
63
|
+
return benchmark.dup if screenname.eql? benchmark[SCREENNAME]
|
64
|
+
end
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def benchmark_pos_by_screenname(screenname)
|
69
|
+
@store[BENCHMARKS].each_with_index do |benchmark, index|
|
70
|
+
return index if screenname.eql? benchmark[SCREENNAME]
|
71
|
+
end
|
72
|
+
-1
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/yordi_tests.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'yordi_tests/version'
|
2
|
+
require 'yordi_tests/client'
|
3
|
+
module YordiTests
|
4
|
+
|
5
|
+
HOST = ENV['YORDI_HOST'].freeze || 'https://yorditests.com'.freeze
|
6
|
+
START_PATH = '/api/v1/start'.freeze
|
7
|
+
UPLOAD_PATH = '/api/v1/upload'.freeze
|
8
|
+
STOP_PATH = '/api/v1/stop'.freeze
|
9
|
+
FETCH_APPLICATION = '/api/v1/fetch_application'.freeze
|
10
|
+
PUSH_BENCHMARK = '/api/v1/push_benchmarks'.freeze
|
11
|
+
PUSH_MASK = '/api/v1/push_masks'.freeze
|
12
|
+
FETCH_BENCHMARK = '/api/v1/benchmark'.freeze
|
13
|
+
|
14
|
+
|
15
|
+
YORDI_DIR = ENV['YORDI_DIR'].freeze || '.yordi_tests'.freeze
|
16
|
+
BENCHMARKS_PATH = YORDI_DIR + '/benchmarks'.freeze
|
17
|
+
SCREENS_PATH = YORDI_DIR + '/screenshots'.freeze
|
18
|
+
CONFIG_FILE = YORDI_DIR + '/config.json'.freeze
|
19
|
+
REPORT_FILE = YORDI_DIR + '/report.json'.freeze
|
20
|
+
REPORT_HTML = YORDI_DIR + '/report.html'.freeze
|
21
|
+
|
22
|
+
module_function
|
23
|
+
|
24
|
+
def client=(v)
|
25
|
+
@client = v
|
26
|
+
end
|
27
|
+
|
28
|
+
def client
|
29
|
+
@client
|
30
|
+
end
|
31
|
+
|
32
|
+
# set default value
|
33
|
+
@client = Client
|
34
|
+
|
35
|
+
def new_client(apikey)
|
36
|
+
client = new
|
37
|
+
client.extend(Client)
|
38
|
+
client.apikey = apikey
|
39
|
+
client
|
40
|
+
end
|
41
|
+
end
|
data/yordi_tests.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "yordi_tests"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "yordi_tests"
|
8
|
+
spec.version = YordiTests::VERSION
|
9
|
+
spec.authors = ["Brian OQR"]
|
10
|
+
spec.email = ["oqrbrian@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = "This is a CLI to run standalone or integrate with online YordiTests.com"
|
13
|
+
spec.description = "This is an alpha version and under continual development, not yet stable"
|
14
|
+
spec.homepage = "https://www.yorditests.com"
|
15
|
+
spec.license = "MIT"
|
16
|
+
spec.required_ruby_version = '~> 2.0'
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
21
|
+
else
|
22
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
23
|
+
"public gem pushes."
|
24
|
+
end
|
25
|
+
|
26
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
f.match(%r{^(test|spec|features)/})
|
28
|
+
end
|
29
|
+
spec.bindir = "exe"
|
30
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ["lib"]
|
32
|
+
|
33
|
+
spec.add_dependency 'thor', '~>0.20'
|
34
|
+
spec.add_dependency 'rest-client', '~>2.0'
|
35
|
+
spec.add_dependency 'mini_magick', '~>4.8'
|
36
|
+
|
37
|
+
|
38
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
39
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
40
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yordi_tests
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.7
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian OQR
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-12-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.20'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.20'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rest-client
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: mini_magick
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.8'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.8'
|
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.15'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.15'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: minitest
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '5.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '5.0'
|
97
|
+
description: This is an alpha version and under continual development, not yet stable
|
98
|
+
email:
|
99
|
+
- oqrbrian@gmail.com
|
100
|
+
executables:
|
101
|
+
- yordi
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- ".gitignore"
|
106
|
+
- ".travis.yml"
|
107
|
+
- Gemfile
|
108
|
+
- LICENSE.txt
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- bin/console
|
112
|
+
- bin/setup
|
113
|
+
- exe/yordi
|
114
|
+
- lib/yordi_tests.rb
|
115
|
+
- lib/yordi_tests/cli.rb
|
116
|
+
- lib/yordi_tests/client.rb
|
117
|
+
- lib/yordi_tests/data_manager.rb
|
118
|
+
- lib/yordi_tests/generators/report.html.erb
|
119
|
+
- lib/yordi_tests/generators/report.rb
|
120
|
+
- lib/yordi_tests/image_compare.rb
|
121
|
+
- lib/yordi_tests/local_store.rb
|
122
|
+
- lib/yordi_tests/version.rb
|
123
|
+
- yordi_tests.gemspec
|
124
|
+
homepage: https://www.yorditests.com
|
125
|
+
licenses:
|
126
|
+
- MIT
|
127
|
+
metadata:
|
128
|
+
allowed_push_host: https://rubygems.org
|
129
|
+
post_install_message:
|
130
|
+
rdoc_options: []
|
131
|
+
require_paths:
|
132
|
+
- lib
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - "~>"
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '2.0'
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
requirements: []
|
144
|
+
rubyforge_project:
|
145
|
+
rubygems_version: 2.6.13
|
146
|
+
signing_key:
|
147
|
+
specification_version: 4
|
148
|
+
summary: This is a CLI to run standalone or integrate with online YordiTests.com
|
149
|
+
test_files: []
|