sir 0.2.3
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 +20 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +33 -0
- data/Rakefile +2 -0
- data/bin/sir +9 -0
- data/lib/sir.rb +38 -0
- data/lib/sir/capybara_ext.rb +12 -0
- data/lib/sir/parser.rb +185 -0
- data/lib/sir/version.rb +3 -0
- data/lib/sir/wrapper.rb +347 -0
- data/lib/sir/yaml/commands_mapping.yml +24 -0
- data/sir.gemspec +26 -0
- data/spec/fixtures/commands.yml +60 -0
- data/spec/fixtures/commands_info.yml +4 -0
- data/spec/fixtures/initial_state.yml +16 -0
- data/spec/fixtures/line_with_equation.yml +5 -0
- data/spec/fixtures/line_with_regex.yml +5 -0
- data/spec/fixtures/line_with_variable.yml +8 -0
- data/spec/fixtures/short_test_result.yml +27 -0
- data/spec/parser_spec.rb +137 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/fixtures.rb +14 -0
- metadata +181 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c2411c7dd813a1cccfe7b38b331086ff52c5fd7b
|
4
|
+
data.tar.gz: 1f68a77c0eafb6de9d0736c096702db61bc1653e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c43839ae12e2ebaa0d35457478b1b2501cc8d215efeae7d19313091eff26bbef61344cedb811a6a897cc2d061e4faccf22ab097902748721863ae36ee1385af9
|
7
|
+
data.tar.gz: 92a7868f66d2a04dd7efc481420715af3ad0ca82037909c3dcf55b19572976f4220fe3739c1b377981d3cd3effb377b092dedd3d862414c1905aec95ecb3723c
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Sergey Smagin
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Sir
|
2
|
+
|
3
|
+
Sir is Selenium InterpreteR. It allows to run your selenium tests without firefox => right on server. And it's pretty cool.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'sir'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install sir
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
You can either run executable file or include as library.
|
24
|
+
|
25
|
+
To run executable:
|
26
|
+
|
27
|
+
./bin/sir path/to/file path/to/initial_state
|
28
|
+
|
29
|
+
To use it inside your code:
|
30
|
+
|
31
|
+
Sir.run!(path_to_file, initial_state)
|
32
|
+
#=> run commands, return hash with that commands
|
33
|
+
|
data/Rakefile
ADDED
data/bin/sir
ADDED
data/lib/sir.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'execjs'
|
4
|
+
require 'capybara/poltergeist'
|
5
|
+
require_relative 'sir/capybara_ext'
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
Capybara.register_driver :poltergeist do |app|
|
9
|
+
options = { timeout: 180,
|
10
|
+
js_errors: false,
|
11
|
+
phantomjs_options: ['--ignore-ssl-errors=yes'],
|
12
|
+
window_size: [1680, 1050] }
|
13
|
+
Capybara::Poltergeist::Driver.new(app, options)
|
14
|
+
end
|
15
|
+
Capybara.default_driver = :poltergeist
|
16
|
+
|
17
|
+
module Sir
|
18
|
+
class << self
|
19
|
+
attr_accessor :state
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
require_relative 'sir/version'
|
24
|
+
require_relative 'sir/parser'
|
25
|
+
require_relative 'sir/wrapper'
|
26
|
+
|
27
|
+
module Sir
|
28
|
+
def self.run!(path, initial_state = {})
|
29
|
+
parser = Sir::Parser.new(path, initial_state)
|
30
|
+
result = parser.run
|
31
|
+
destroy_session parser.wrapper.session rescue nil
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.destroy_session(session)
|
36
|
+
session.driver.quit
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Capybara::Poltergeist::Browser
|
2
|
+
def find(method, selector)
|
3
|
+
result = command('find', method, selector)
|
4
|
+
|
5
|
+
# FIXME workaround for this bug https://github.com/teampoltergeist/poltergeist/issues/482
|
6
|
+
if result == true
|
7
|
+
result = command('find', method, selector)
|
8
|
+
end
|
9
|
+
|
10
|
+
result['ids'].map { |id| [result['page_id'], id] }
|
11
|
+
end
|
12
|
+
end
|
data/lib/sir/parser.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
# Sir::Parser is class that parse html and generate commands that may be sent
|
2
|
+
# into Capybara.
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
# Sir::Parser.new('path/to/file').parse!
|
6
|
+
# #=> [ { command: 'select', args: [ { id: 'ccid', label: /expire/ } ] } ]
|
7
|
+
#
|
8
|
+
class Sir::Parser
|
9
|
+
attr_reader :document, :wrapper
|
10
|
+
|
11
|
+
# Map selenium methods to wrapper's ones.
|
12
|
+
METHODS = YAML.load_file File.expand_path('yaml/commands_mapping.yml', __dir__)
|
13
|
+
NO_EQUATIONS_METHODS = %w(visit expect_location wait_for_loading)
|
14
|
+
|
15
|
+
def initialize(path, initial_state = nil, wrapper = nil)
|
16
|
+
@path = path
|
17
|
+
@document = Nokogiri::HTML File.read path
|
18
|
+
@wrapper = wrapper || Sir::Wrapper.new
|
19
|
+
@wrapper.state = initial_state if initial_state
|
20
|
+
end
|
21
|
+
|
22
|
+
# Make it possible to run command right from Sir::Parser.
|
23
|
+
#
|
24
|
+
def self.run!(path, initial_state = {}, wrapper = nil)
|
25
|
+
new(path, initial_state, wrapper).run
|
26
|
+
end
|
27
|
+
|
28
|
+
# Run commands from file.
|
29
|
+
# It makes interpretation itself.
|
30
|
+
#
|
31
|
+
def run
|
32
|
+
result = commands.inject([]) do |r, x|
|
33
|
+
begin
|
34
|
+
command = get_command x
|
35
|
+
next (r) unless command
|
36
|
+
r << wrapper.send_command(command)
|
37
|
+
rescue NoMethodError => e
|
38
|
+
if command[:args].last == "*[aria-label=Delete]" # workaround for gmail
|
39
|
+
return {
|
40
|
+
success: false,
|
41
|
+
output: (r << command),
|
42
|
+
}
|
43
|
+
else
|
44
|
+
binding.pry
|
45
|
+
return {
|
46
|
+
success: false,
|
47
|
+
output: (r << command),
|
48
|
+
error: { message: e.message, backtrace: e.backtrace }
|
49
|
+
}
|
50
|
+
end
|
51
|
+
rescue => e
|
52
|
+
binding.pry
|
53
|
+
return {
|
54
|
+
success: false,
|
55
|
+
output: (r << command),
|
56
|
+
error: { message: e.message, backtrace: e.backtrace }
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
failed = result.any? { |x| x[:command] == 'run' && x[:result][:success] == false }
|
61
|
+
{ success: !failed, output: result }
|
62
|
+
end
|
63
|
+
|
64
|
+
# Parse one command.
|
65
|
+
# @param c [Nokogiri::Element] one command on selenium language.
|
66
|
+
#
|
67
|
+
def get_command(c)
|
68
|
+
if path = external_script_link(c)
|
69
|
+
{ command: 'run', args: [full_path(path)] }
|
70
|
+
elsif title?(c)
|
71
|
+
nil
|
72
|
+
else parse_command(*c.css('td').map(&:text))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Get commands from html structure and parse them.
|
77
|
+
#
|
78
|
+
def commands
|
79
|
+
document.css('tbody tr')
|
80
|
+
end
|
81
|
+
|
82
|
+
# Parse command from array to hash
|
83
|
+
#
|
84
|
+
# @param command [String] selenium command name
|
85
|
+
# @param args [Array] selenium arguments
|
86
|
+
#
|
87
|
+
# @example
|
88
|
+
# parse_command(*["open", "${BOX}/application", ""])
|
89
|
+
# #=> { command: 'visit', args: ['#http://value/of/box/application'] }
|
90
|
+
#
|
91
|
+
def parse_command(command, *args)
|
92
|
+
method = METHODS[command]
|
93
|
+
fail "Unrecognized command: #{command}" if method.nil?
|
94
|
+
arguments = args.reject(&:empty?).map do |line|
|
95
|
+
with_vars = parse_js(replace_variables(line))
|
96
|
+
next with_vars if NO_EQUATIONS_METHODS.include?(method)
|
97
|
+
EQUATION_WITH_REGEX.match(with_vars) ?
|
98
|
+
parse_equation_with_regex(with_vars) : parse_equation(with_vars)
|
99
|
+
end.flatten
|
100
|
+
{ command: method, args: arguments }
|
101
|
+
end
|
102
|
+
|
103
|
+
protected
|
104
|
+
|
105
|
+
# Search link to other file in tr.
|
106
|
+
# @params tr [Nokogiri::Node] if fact any Nokogiri node.
|
107
|
+
#
|
108
|
+
# @returns nil when no link founded.
|
109
|
+
# @returns String with link if founded.
|
110
|
+
#
|
111
|
+
def external_script_link(tr)
|
112
|
+
tr.css('td a').map { |l| l['href'] }.first
|
113
|
+
end
|
114
|
+
|
115
|
+
# Check if tr is title. Title is written with bold font.
|
116
|
+
#
|
117
|
+
def title?(tr)
|
118
|
+
!!tr.css('td b').text[0]
|
119
|
+
end
|
120
|
+
|
121
|
+
# Expand path relative to path of file that we interpret.
|
122
|
+
#
|
123
|
+
# @params link [String] relative path
|
124
|
+
# @returns [String] full path
|
125
|
+
#
|
126
|
+
def full_path(link)
|
127
|
+
File.expand_path('../' + link, @path)
|
128
|
+
end
|
129
|
+
|
130
|
+
VARIABLE = /\$\{(\w*)\}/
|
131
|
+
EQUATION = /(\w*)=(.*)/
|
132
|
+
EQUATION_WITH_REGEX = /(\w*)=regexp:(.*)/
|
133
|
+
JS = /javascript\{(.*)\}/
|
134
|
+
ATTRIBUTE = /(.+)@([a-z\-\_]+)$/
|
135
|
+
PSEUDO_SELECTOR = /(.+):(.+)\('(.+)'\)/
|
136
|
+
|
137
|
+
# Replace variables that we have in state.
|
138
|
+
# Pattern of variable is '${var}'.
|
139
|
+
#
|
140
|
+
def replace_variables(line)
|
141
|
+
line.scan(VARIABLE).flatten
|
142
|
+
.reduce(line) { |r, x| r.gsub("${#{x}}", (wrapper.state[x] || '')) }
|
143
|
+
end
|
144
|
+
|
145
|
+
# Split equation into variable and value.
|
146
|
+
#
|
147
|
+
def parse_equation(line)
|
148
|
+
match_data = EQUATION.match(line)
|
149
|
+
return line unless match_data
|
150
|
+
left = match_data[1].to_sym
|
151
|
+
right = parse_pseudo_selector(match_data[2])
|
152
|
+
right = Array === right ? right : parse_attribute(match_data[2])
|
153
|
+
[left, *right]
|
154
|
+
end
|
155
|
+
|
156
|
+
def parse_equation_with_regex(line)
|
157
|
+
match_data = EQUATION_WITH_REGEX.match(line)
|
158
|
+
match_data ? [match_data[1].to_sym, /#{match_data[2]}/] : line
|
159
|
+
end
|
160
|
+
|
161
|
+
def parse_js(line)
|
162
|
+
match_data = JS.match(line)
|
163
|
+
match_data ? ExecJS.eval(match_data[1]).to_s : line
|
164
|
+
end
|
165
|
+
|
166
|
+
# Parse attribute. In selenium attribute is written like 'link@href'.
|
167
|
+
#
|
168
|
+
def parse_attribute(line)
|
169
|
+
match_data = ATTRIBUTE.match(line)
|
170
|
+
match_data ? match_data[1..2] : [line]
|
171
|
+
end
|
172
|
+
|
173
|
+
# Parse pseudo selector.
|
174
|
+
# For example: "div[role=link] span:contains('Purchase')"
|
175
|
+
#
|
176
|
+
def parse_pseudo_selector(line)
|
177
|
+
match_data = PSEUDO_SELECTOR.match(line)
|
178
|
+
replace_pseudo = { 'contains' => :text }
|
179
|
+
return line unless match_data
|
180
|
+
[
|
181
|
+
match_data[1],
|
182
|
+
{ (replace_pseudo[match_data[2]] || match_data[2]).to_sym => match_data[3] }
|
183
|
+
]
|
184
|
+
end
|
185
|
+
end
|
data/lib/sir/version.rb
ADDED
data/lib/sir/wrapper.rb
ADDED
@@ -0,0 +1,347 @@
|
|
1
|
+
# Capybara wrapper. Run capybara, rspec and ruby commands.
|
2
|
+
#
|
3
|
+
class Sir::Wrapper
|
4
|
+
class ExpectationError < StandardError
|
5
|
+
attr_accessor :message
|
6
|
+
|
7
|
+
def initialize(msg, expected, got)
|
8
|
+
@message = "#{msg}\n\texpected: #{expected}\n\tgot: #{got}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_accessor :session, :state
|
13
|
+
|
14
|
+
def initialize(session = nil)
|
15
|
+
@session = session || Capybara::Session.new(:poltergeist)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Example of hash:
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# send_command({ command: 'visit',
|
22
|
+
# args: ['http://value/of/box/application'] })
|
23
|
+
# #=> `visit 'http://value/of/box/application'` execution
|
24
|
+
#
|
25
|
+
def send_command(hash)
|
26
|
+
r = send(hash[:command], *hash[:args])
|
27
|
+
r = true if r == ''
|
28
|
+
hash.merge(result: r)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Run script by given path.
|
32
|
+
#
|
33
|
+
# @params path [String] path to script
|
34
|
+
# @return hash of commands that it run.
|
35
|
+
#
|
36
|
+
def run(path)
|
37
|
+
Sir::Parser.run!(path, state, self)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Wrapper of clearAllVisible cookies. Clear cookies.
|
41
|
+
#
|
42
|
+
def clean_cookies
|
43
|
+
session.driver.clear_cookies
|
44
|
+
end
|
45
|
+
|
46
|
+
# Simple capybara visit, except we use session to run it.
|
47
|
+
#
|
48
|
+
def visit(address)
|
49
|
+
tries ||= 0
|
50
|
+
session.visit address
|
51
|
+
try_box_crutch(address)
|
52
|
+
pp session.current_url
|
53
|
+
rescue Capybara::Poltergeist::TimeoutError => e
|
54
|
+
cookies = session.driver.cookies
|
55
|
+
session.driver.quit
|
56
|
+
@session = Capybara::Session.new(:poltergeist)
|
57
|
+
cookies.each { |k, v| session.driver.cookies[k] = v }
|
58
|
+
if retries < 2
|
59
|
+
retry
|
60
|
+
else
|
61
|
+
raise e
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def try_box_crutch(address)
|
66
|
+
url = session.current_url
|
67
|
+
if address =~ /auth\/box\?content_id=(\d+)/ && !url.include?('#videos')
|
68
|
+
visit url + "#videos/#{$~[1]}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Wrapper of verifyText.
|
73
|
+
# @example
|
74
|
+
# expect_text :id, 'user_id', 'John'
|
75
|
+
#
|
76
|
+
def expect_text(criterium, selector, text)
|
77
|
+
message = 'Failed to find text:'
|
78
|
+
expected = text
|
79
|
+
got = session.find(criterium, selector).text
|
80
|
+
fail ExpectationError.new(message, expected, got) unless expected == got
|
81
|
+
end
|
82
|
+
|
83
|
+
# Wrapper of assertLocation.
|
84
|
+
# @example
|
85
|
+
# expect_location 'ya.ru'
|
86
|
+
#
|
87
|
+
def expect_location(address)
|
88
|
+
message = 'Failed to be on expected page:'
|
89
|
+
expected = address
|
90
|
+
got = session.current_url
|
91
|
+
fail ExpectationError.new(message, expected, got) unless expected == got
|
92
|
+
end
|
93
|
+
|
94
|
+
def expect_attribute(criterium, selector, attribute, expected)
|
95
|
+
tries ||= 0
|
96
|
+
message = 'Failed to check attribute:'
|
97
|
+
got = session.first(criterium, selector, visible: [true, false])[attribute]
|
98
|
+
fail ExpectationError.new(message, expected, got) unless File.fnmatch(expected, got)
|
99
|
+
rescue => e
|
100
|
+
if (tries < 3)
|
101
|
+
retry
|
102
|
+
else
|
103
|
+
raise e
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Wrapper of sleep.
|
108
|
+
# @example
|
109
|
+
# sleep "5000" #=> do nothing 5 seconds.
|
110
|
+
#
|
111
|
+
def sleep(milliseconds)
|
112
|
+
Kernel.sleep(milliseconds.to_i / 1000)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Wrapper of select.
|
116
|
+
# @example
|
117
|
+
# select :css, '.hey.there', :id, 'username'
|
118
|
+
#
|
119
|
+
def select(*args)
|
120
|
+
session.find(*args.first(2)).select(args.last)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Wrapper of type.
|
124
|
+
# @example
|
125
|
+
# find_and_set :id, 'username', 'pahom'
|
126
|
+
#
|
127
|
+
def find_and_set(criterium, selector, value)
|
128
|
+
is_angular = false # !session.evaluate_script('angular').nil?
|
129
|
+
if selector.include?('file') && is_angular
|
130
|
+
angular_find_and_set selector, value
|
131
|
+
else
|
132
|
+
session.find(criterium, selector, visible: [true, false]).set value
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def angular_find_and_set(selector, value)
|
137
|
+
sleep 1000
|
138
|
+
unless session.first(:id, 'fakeFileInput')
|
139
|
+
session.execute_script "fakeFileInput = window.$('<input/>').attr({id: 'fakeFileInput', type:'file'}).appendTo('body');"
|
140
|
+
end
|
141
|
+
session.first(:id, 'fakeFileInput').set value
|
142
|
+
session.execute_script("angular.element(document.getElementById('#{selector}')).scope().scanProcess.fileSelect($('#fakeFileInput')[0].files)")
|
143
|
+
sleep 5000
|
144
|
+
end
|
145
|
+
|
146
|
+
# Wrapper of click.
|
147
|
+
# @example
|
148
|
+
# find_and_click :id, 'submit_form'
|
149
|
+
#
|
150
|
+
def find_and_click(criterium, *selectors)
|
151
|
+
tries ||= 0
|
152
|
+
session.first(criterium, *selectors, visible: [true, false]).click
|
153
|
+
rescue Capybara::Poltergeist::ObsoleteNode
|
154
|
+
sleep 1000
|
155
|
+
session.evaluate_script("$('#{selectors.join(' ')}').show()")
|
156
|
+
session.first(criterium, *selectors).click
|
157
|
+
rescue => e
|
158
|
+
if (tries < 3)
|
159
|
+
retry
|
160
|
+
else
|
161
|
+
raise e
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Same as find_and_click, but sleep 5 seconds after it.
|
166
|
+
# what the hell should it do in selenium?
|
167
|
+
#
|
168
|
+
def click_and_wait(criterium, *selectors)
|
169
|
+
sleep 2000
|
170
|
+
session.first(criterium, *selectors).click
|
171
|
+
rescue
|
172
|
+
case criterium
|
173
|
+
when :css then session.evaluate_script("$('#{selectors.join(' ')}').show()")
|
174
|
+
when :id then session.evaluate_script("$('##{selectors.join(' ')}').show()")
|
175
|
+
end
|
176
|
+
session.first(criterium, *selectors).click
|
177
|
+
ensure
|
178
|
+
sleep 5000
|
179
|
+
end
|
180
|
+
|
181
|
+
# Add url to global state. Wrapper of storeLocation.
|
182
|
+
#
|
183
|
+
def state_set_url(name)
|
184
|
+
state[name] = session.current_url
|
185
|
+
end
|
186
|
+
|
187
|
+
# Wrapper of storeText. Add finded text to global state.
|
188
|
+
# @example
|
189
|
+
# state_set_text :id, 'username', 'user_name'
|
190
|
+
#
|
191
|
+
def state_set_text(*args)
|
192
|
+
case args.size
|
193
|
+
when 2
|
194
|
+
criterium, selector, name = :xpath, *args
|
195
|
+
when 3
|
196
|
+
criterium, selector, name = *args
|
197
|
+
end
|
198
|
+
sleep 500
|
199
|
+
# workaround for purchase tickets.
|
200
|
+
if %w(with_membership without_membership).include?(selector)
|
201
|
+
text = (session.first(:id, 'with_membership') || session.first(:id, 'without_membership')).text
|
202
|
+
else
|
203
|
+
text = session.first(criterium, selector, visible: [true, false]).text
|
204
|
+
if text == true
|
205
|
+
text = session.first(criterium, selector, visible: [true, false]).text
|
206
|
+
end
|
207
|
+
end
|
208
|
+
state[name] = text
|
209
|
+
end
|
210
|
+
|
211
|
+
# Wrapper of storeAttribute. Add text at attribute to global state.
|
212
|
+
# @example
|
213
|
+
# state_set_text :id, 'username', 'href', 'user_name'
|
214
|
+
#
|
215
|
+
def state_set_attribute(criterium, selector, attribute, name)
|
216
|
+
x ||= 0
|
217
|
+
text = session.first(criterium, selector, visible: [true, false])[attribute]
|
218
|
+
state[name] = text
|
219
|
+
rescue
|
220
|
+
(x += 1; retry) if x < 3
|
221
|
+
end
|
222
|
+
|
223
|
+
def state_set_eval(code, name)
|
224
|
+
state[name] = session.evaluate_script(code)
|
225
|
+
end
|
226
|
+
|
227
|
+
def state_set(value, name)
|
228
|
+
state[name] = value
|
229
|
+
end
|
230
|
+
|
231
|
+
def keyup(criterium, selector, keycode)
|
232
|
+
selector = case criterium
|
233
|
+
when :css then selector
|
234
|
+
when :id then '#' + selector
|
235
|
+
else selector
|
236
|
+
end
|
237
|
+
keypress_script = "var e = $.Event('keyup', { keyCode: #{keycode} }); $('#{selector}').trigger(e);"
|
238
|
+
session.execute_script(keypress_script)
|
239
|
+
end
|
240
|
+
|
241
|
+
def wait_for_loading(address)
|
242
|
+
# session.visit address
|
243
|
+
sleep 5000
|
244
|
+
wait_for { File.fnmatch(address, session.current_url) }
|
245
|
+
end
|
246
|
+
|
247
|
+
# Wrapper of waitForElementPresent.
|
248
|
+
# @example
|
249
|
+
# wait_for_element :id, 'username'
|
250
|
+
#
|
251
|
+
def wait_for_element(criterium, *selectors)
|
252
|
+
wait_for { session.first(criterium, *selectors, visible: [true, false]) }
|
253
|
+
true
|
254
|
+
rescue Timeout::Error
|
255
|
+
url = session.current_url
|
256
|
+
url = session.current_url unless String === url
|
257
|
+
session.visit url
|
258
|
+
sleep 10000
|
259
|
+
session.first(criterium, *selectors, visible: [true, false])
|
260
|
+
ensure
|
261
|
+
true
|
262
|
+
end
|
263
|
+
|
264
|
+
# Wrapper of waitForVisible.
|
265
|
+
# @example
|
266
|
+
# wait_for_visible :id, 'username'
|
267
|
+
#
|
268
|
+
def wait_for_visible(criterium, selector, value = nil)
|
269
|
+
wait_for do
|
270
|
+
element = session.first(criterium, selector, visible: [true])
|
271
|
+
if element && value
|
272
|
+
File.fnmatch(value.to_s, element.text)
|
273
|
+
else
|
274
|
+
true
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# Wrapper of waitForText.
|
280
|
+
# @example
|
281
|
+
# wait_for_text :id, 'username', 'Pahom'
|
282
|
+
#
|
283
|
+
def wait_for_text(criterium, *selectors, text)
|
284
|
+
wait_for do
|
285
|
+
(x = session.first(criterium, *selectors, visible: [true, false])) &&
|
286
|
+
File.fnmatch(text, x.text)
|
287
|
+
end
|
288
|
+
rescue
|
289
|
+
# Fucking poltergeist can do this:
|
290
|
+
# [42] pry(#<Sir::Parser>)> wrapper.session.text
|
291
|
+
# NoMethodError: undefined method `map' for nil:NilClass
|
292
|
+
# from /home/s/work/sir/lib/sir/capybara_ext.rb:10:in `find'
|
293
|
+
# [43] pry(#<Sir::Parser>)> wrapper.session.text
|
294
|
+
# => "true"
|
295
|
+
# [44] pry(#<Sir::Parser>)> wrapper.session.current_url
|
296
|
+
# => "+You\nSearch\nImages\nMaps\nPlay\nYouTube\nGmail\nDrive\nMore\nrmtest011@gmail.com\n×\n\tA faster way to browse the web\nInstall Google Chrome\n\nRussia\n\n \t\n\n\nAdvanced search\nLanguage tools\n\nGoogle.ru offered in: русском\nAdvertising ProgramsAbout GoogleGoogle.com\n© 2015 - Privacy - Terms\n\n"
|
297
|
+
# FIXME: so just sleep 5 seconds and hope it will be fine.
|
298
|
+
sleep 5000
|
299
|
+
end
|
300
|
+
|
301
|
+
def wait_for_text_present(text)
|
302
|
+
wait_for do
|
303
|
+
session.text.include? text
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
# Wrapper of waitForValue.
|
308
|
+
# @example
|
309
|
+
# wait_for_value :id, 'username', 'Pahom'
|
310
|
+
#
|
311
|
+
def wait_for_value(criterium, selector, text)
|
312
|
+
wait_for do
|
313
|
+
session.first(criterium, selector, text)
|
314
|
+
end
|
315
|
+
true
|
316
|
+
end
|
317
|
+
|
318
|
+
def wait_for_content(content)
|
319
|
+
wait_for(CONTENT_TIMEOUT) { session.has_text? content }
|
320
|
+
end
|
321
|
+
|
322
|
+
def wait_for_ajax
|
323
|
+
previous = active_queries_count
|
324
|
+
wait_for(AJAX_TIMEOUT) { active_queries_count < previous }
|
325
|
+
end
|
326
|
+
|
327
|
+
protected
|
328
|
+
|
329
|
+
AJAX_TIMEOUT = 100
|
330
|
+
CONTENT_TIMEOUT = 100
|
331
|
+
STANDARD_TIMEOUT = 50
|
332
|
+
|
333
|
+
def wait_for(timeout = STANDARD_TIMEOUT)
|
334
|
+
result = nil
|
335
|
+
Timeout.timeout(timeout) do
|
336
|
+
loop do
|
337
|
+
sleep 1
|
338
|
+
break if result = yield
|
339
|
+
end
|
340
|
+
end
|
341
|
+
result
|
342
|
+
end
|
343
|
+
|
344
|
+
def active_queries_count
|
345
|
+
session.evaluate_script('jQuery.active')
|
346
|
+
end
|
347
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
open: visit
|
2
|
+
pause: sleep
|
3
|
+
select: select
|
4
|
+
type: find_and_set
|
5
|
+
click: find_and_click
|
6
|
+
clickAt: find_and_click
|
7
|
+
clickAndWait: click_and_wait
|
8
|
+
waitForElementPresent: wait_for_element
|
9
|
+
waitForLocation: wait_for_loading
|
10
|
+
waitForText: wait_for_text
|
11
|
+
waitForTextPresent: wait_for_text_present
|
12
|
+
waitForValue: wait_for_value
|
13
|
+
waitForVisible: wait_for_visible
|
14
|
+
storeAttribute: state_set_attribute
|
15
|
+
storeLocation: state_set_url
|
16
|
+
storeText: state_set_text
|
17
|
+
storeEval: state_set_eval
|
18
|
+
store: state_set
|
19
|
+
gotolabel: goto_label
|
20
|
+
verifyText: expect_text
|
21
|
+
verifyAttribute: expect_attribute
|
22
|
+
assertLocation: expect_location
|
23
|
+
deleteAllVisibleCookies: clean_cookies
|
24
|
+
keyUp: keyup
|
data/sir.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sir/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sir"
|
8
|
+
spec.version = Sir::VERSION
|
9
|
+
spec.authors = ["Sergey Smagin"]
|
10
|
+
spec.email = ["smaginsergey1310@gmail.com"]
|
11
|
+
spec.summary = %q{Run selenium tests with webkit. Headless.}
|
12
|
+
spec.license = "MIT"
|
13
|
+
|
14
|
+
spec.files = `git ls-files -z`.split("\x0")
|
15
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
16
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
20
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
21
|
+
spec.add_dependency "rspec"
|
22
|
+
spec.add_dependency "poltergeist"
|
23
|
+
spec.add_dependency "nokogiri"
|
24
|
+
spec.add_dependency "execjs"
|
25
|
+
spec.add_dependency "therubyracer"
|
26
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
:open:
|
2
|
+
:input: ['open', '${BOX}/application']
|
3
|
+
:result:
|
4
|
+
:command: visit
|
5
|
+
:args: [ 'box/application' ]
|
6
|
+
:pause:
|
7
|
+
:input: ['pause', '5000']
|
8
|
+
:result:
|
9
|
+
:command: sleep
|
10
|
+
:args: [ '5000' ]
|
11
|
+
:select:
|
12
|
+
:input: ['select', 'id=ccid', 'label=regexp:.*expire.*']
|
13
|
+
:result:
|
14
|
+
:command: select
|
15
|
+
:args: [ !ruby/sym id, 'ccid', !ruby/sym label, !ruby/regexp /.*expire.*/ ]
|
16
|
+
:find_and_set:
|
17
|
+
:input: ['type', 'id=amount', 'attestationAmount']
|
18
|
+
:result:
|
19
|
+
:command: find_and_set
|
20
|
+
:args: [ !ruby/sym id, 'amount', 'attestationAmount' ]
|
21
|
+
:click:
|
22
|
+
:input: ['click', "xpath=(//input[@name='can-share'])[2]"]
|
23
|
+
:result:
|
24
|
+
:command: find_and_click
|
25
|
+
:args: [ !ruby/sym xpath, "(//input[@name='can-share'])[2]" ]
|
26
|
+
:click_and_wait:
|
27
|
+
:input: ['clickAndWait', "xpath=(//input[@name='can-share'])[2]"]
|
28
|
+
:result:
|
29
|
+
:command: click_and_wait
|
30
|
+
:args: [ !ruby/sym xpath, "(//input[@name='can-share'])[2]" ]
|
31
|
+
:wait_for_element:
|
32
|
+
:input: ['waitForElementPresent', 'css=.icos']
|
33
|
+
:result:
|
34
|
+
:command: wait_for_element
|
35
|
+
:args: [ !ruby/sym css, '.icos' ]
|
36
|
+
:wait_for_loading:
|
37
|
+
:input: ['waitForLocation', '${BOX}/account/attestate']
|
38
|
+
:result:
|
39
|
+
:command: wait_for_loading
|
40
|
+
:args: [ 'box/account/attestate' ]
|
41
|
+
:wait_for_text:
|
42
|
+
:input: ['waitForText', 'css=h6.fcolor-green', 'Credit card approved']
|
43
|
+
:result:
|
44
|
+
:command: wait_for_text
|
45
|
+
:args: [ !ruby/sym css, 'h6.fcolor-green', 'Credit card approved' ]
|
46
|
+
:state_set_url:
|
47
|
+
:input: ['storeLocation', 'verify']
|
48
|
+
:result:
|
49
|
+
:command: state_set_url
|
50
|
+
:args: [ 'verify' ]
|
51
|
+
:state_set_text:
|
52
|
+
:input: ['storeText', 'css=td.def', 'attestationAmount']
|
53
|
+
:result:
|
54
|
+
:command: state_set_text
|
55
|
+
:args: [ !ruby/sym css, 'td.def', 'attestationAmount' ]
|
56
|
+
:expect_text:
|
57
|
+
:input: ['verifyText', 'css=button', 'I accept agreement']
|
58
|
+
:result:
|
59
|
+
:command: expect_text
|
60
|
+
:args: [ !ruby/sym css, 'button', 'I accept agreement' ]
|
@@ -0,0 +1,16 @@
|
|
1
|
+
LV: "http://lv.gtflix.com"
|
2
|
+
IDCERT: "https://gtfpass.gtflix.com"
|
3
|
+
PAYGATE: "https://paygate.gtflix.com"
|
4
|
+
BOX: "https://box.gtflix.com:9090"
|
5
|
+
CART: "http://cart.gtflix.com"
|
6
|
+
QA: "http://qa.gtflix.com"
|
7
|
+
CM2: "http://devcm2.gtflix.com"
|
8
|
+
gmailAccount: "amorpro3"
|
9
|
+
CCHolderName: "John Doe"
|
10
|
+
CCNumber: "4000000000000002"
|
11
|
+
CCCVV: "123"
|
12
|
+
CCYear: "2017"
|
13
|
+
CCMonth: "01"
|
14
|
+
cm2_user: "dan"
|
15
|
+
cm2_pass: "qwe123"
|
16
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
---
|
2
|
+
:success: true
|
3
|
+
:output:
|
4
|
+
- :command: visit
|
5
|
+
:args:
|
6
|
+
- http://ya.ru
|
7
|
+
:result:
|
8
|
+
status: success
|
9
|
+
- :command: find_and_click
|
10
|
+
:args:
|
11
|
+
- :id
|
12
|
+
- text
|
13
|
+
:result:
|
14
|
+
position:
|
15
|
+
x: 437
|
16
|
+
y: 353
|
17
|
+
- :command: find_and_set
|
18
|
+
:args:
|
19
|
+
- :id
|
20
|
+
- text
|
21
|
+
- good for you
|
22
|
+
:result: true
|
23
|
+
- :command: click_and_wait
|
24
|
+
:args:
|
25
|
+
- :css
|
26
|
+
- button.button_theme_websearch
|
27
|
+
:result: 0
|
data/spec/parser_spec.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe Sir::Parser do
|
4
|
+
let(:state) { Sir::Fixtures::LINE_WITH_VARIABLE[:state] }
|
5
|
+
let(:path) { File.expand_path '../fixtures/commands.html', __FILE__ }
|
6
|
+
subject { Sir::Parser.new(path, state) }
|
7
|
+
|
8
|
+
describe '.get_commands' do
|
9
|
+
it 'Get as many commands as file have' do
|
10
|
+
expect(subject.get_commands.size).
|
11
|
+
to eq(Sir::Fixtures::COMMANDS_INFO[:commands_amount])
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'Get first command properly' do
|
15
|
+
expect(subject.get_commands.first).
|
16
|
+
to eq(Sir::Fixtures::COMMANDS_INFO[:first_command])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '.replace_variables' do
|
21
|
+
it 'can replace varible like #{YO}' do
|
22
|
+
input = Sir::Fixtures::LINE_WITH_VARIABLE[:in]
|
23
|
+
out = Sir::Fixtures::LINE_WITH_VARIABLE[:out]
|
24
|
+
expect(subject.send(:replace_variables, input)).to eq(out)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'can handle incorrect variables' do
|
28
|
+
input = Sir::Fixtures::LINE_WITH_VARIABLE[:incorrect_in]
|
29
|
+
out = Sir::Fixtures::LINE_WITH_VARIABLE[:incorrect_out]
|
30
|
+
expect(subject.send(:replace_variables, input)).to eq(out)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '.parse_equation' do
|
35
|
+
it 'can split variables with equation' do
|
36
|
+
input = Sir::Fixtures::LINE_WITH_EQUATION[:in]
|
37
|
+
out = Sir::Fixtures::LINE_WITH_EQUATION[:out]
|
38
|
+
expect(subject.send(:parse_equation, input)).to eq(out)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'can handle incorrect variables' do
|
42
|
+
input = Sir::Fixtures::LINE_WITH_EQUATION[:incorrect_in]
|
43
|
+
out = Sir::Fixtures::LINE_WITH_EQUATION[:incorrect_in]
|
44
|
+
expect(subject.send(:parse_equation, input)).to eq(out)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'parse attributes' do
|
48
|
+
input = Sir::Fixtures::LINE_WITH_EQUATION[:with_attr]
|
49
|
+
out = Sir::Fixtures::LINE_WITH_EQUATION[:out_with_attr]
|
50
|
+
expect(subject.send(:parse_equation, input)).to eq(out)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '.parse_equation_with_regex' do
|
55
|
+
it 'can split variables with equation' do
|
56
|
+
input = Sir::Fixtures::LINE_WITH_REGEX[:in]
|
57
|
+
out = Sir::Fixtures::LINE_WITH_REGEX[:out]
|
58
|
+
expect(subject.send(:parse_equation_with_regex, input)).to eq(out)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'can handle incorrect variables' do
|
62
|
+
input = Sir::Fixtures::LINE_WITH_REGEX[:incorrect_in]
|
63
|
+
out = Sir::Fixtures::LINE_WITH_REGEX[:incorrect_in]
|
64
|
+
expect(subject.send(:parse_equation_with_regex, input)).to eq(out)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '.parse_command' do
|
69
|
+
it 'parse "open ${BOX}/application"' do
|
70
|
+
expect(subject.parse_command(*Sir::Fixtures::COMMANDS[:open][:input])).
|
71
|
+
to eq(Sir::Fixtures::COMMANDS[:open][:result])
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'parse pause' do
|
75
|
+
expect(subject.parse_command(*Sir::Fixtures::COMMANDS[:pause][:input])).
|
76
|
+
to eq(Sir::Fixtures::COMMANDS[:pause][:result])
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'parse select' do
|
80
|
+
expect(subject.parse_command(*Sir::Fixtures::COMMANDS[:select][:input])).
|
81
|
+
to eq(Sir::Fixtures::COMMANDS[:select][:result])
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'parse click' do
|
85
|
+
expect(subject.parse_command(*Sir::Fixtures::COMMANDS[:click][:input])).
|
86
|
+
to eq(Sir::Fixtures::COMMANDS[:click][:result])
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'parse click_and_wait' do
|
90
|
+
expect(subject.parse_command(*Sir::Fixtures::COMMANDS[:click_and_wait][:input])).
|
91
|
+
to eq(Sir::Fixtures::COMMANDS[:click_and_wait][:result])
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'parse wait_for_element' do
|
95
|
+
expect(subject.parse_command(*Sir::Fixtures::COMMANDS[:wait_for_element][:input])).
|
96
|
+
to eq(Sir::Fixtures::COMMANDS[:wait_for_element][:result])
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'parse wait_for_loading' do
|
100
|
+
expect(subject.parse_command(*Sir::Fixtures::COMMANDS[:wait_for_loading][:input])).
|
101
|
+
to eq(Sir::Fixtures::COMMANDS[:wait_for_loading][:result])
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'parse wait_for_text' do
|
105
|
+
expect(subject.parse_command(*Sir::Fixtures::COMMANDS[:wait_for_text][:input])).
|
106
|
+
to eq(Sir::Fixtures::COMMANDS[:wait_for_text][:result])
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'parse state_set_url' do
|
110
|
+
expect(subject.parse_command(*Sir::Fixtures::COMMANDS[:state_set_url][:input])).
|
111
|
+
to eq(Sir::Fixtures::COMMANDS[:state_set_url][:result])
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'parse state_set_text' do
|
115
|
+
expect(subject.parse_command(*Sir::Fixtures::COMMANDS[:state_set_text][:input])).
|
116
|
+
to eq(Sir::Fixtures::COMMANDS[:state_set_text][:result])
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'parse expect_text' do
|
120
|
+
expect(subject.parse_command(*Sir::Fixtures::COMMANDS[:expect_text][:input])).
|
121
|
+
to eq(Sir::Fixtures::COMMANDS[:expect_text][:result])
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'parse find_and_set' do
|
125
|
+
expect(subject.parse_command(*Sir::Fixtures::COMMANDS[:find_and_set][:input])).
|
126
|
+
to eq(Sir::Fixtures::COMMANDS[:find_and_set][:result])
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context '#run' do
|
131
|
+
it 'summarize script work' do
|
132
|
+
path = File.expand_path 'fixtures/short_test.html', __dir__
|
133
|
+
expect(Sir::Parser.run!(path)).to eq(Sir::Fixtures::SHORT_TEST_RESULT)
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
# When this file is loaded, for each fixture file, a singleton method is
|
4
|
+
# created within the Sir::Fixtures module with the same name as the fixture
|
5
|
+
# file, returning the value of the fixture.
|
6
|
+
#
|
7
|
+
module Sir::Fixtures; end
|
8
|
+
|
9
|
+
fixtures_path = File.expand_path('../../fixtures', __FILE__)
|
10
|
+
|
11
|
+
Dir[File.expand_path('*.yml', fixtures_path)].each do |filename|
|
12
|
+
const_name = File.basename(filename, '.*').upcase
|
13
|
+
Sir::Fixtures.const_set(const_name, YAML.load_file(filename))
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sir
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sergey Smagin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: poltergeist
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: nokogiri
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: execjs
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: therubyracer
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description:
|
112
|
+
email:
|
113
|
+
- smaginsergey1310@gmail.com
|
114
|
+
executables:
|
115
|
+
- sir
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- ".gitignore"
|
120
|
+
- Gemfile
|
121
|
+
- LICENSE.txt
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- bin/sir
|
125
|
+
- lib/sir.rb
|
126
|
+
- lib/sir/capybara_ext.rb
|
127
|
+
- lib/sir/parser.rb
|
128
|
+
- lib/sir/version.rb
|
129
|
+
- lib/sir/wrapper.rb
|
130
|
+
- lib/sir/yaml/commands_mapping.yml
|
131
|
+
- sir.gemspec
|
132
|
+
- spec/fixtures/commands.html
|
133
|
+
- spec/fixtures/commands.yml
|
134
|
+
- spec/fixtures/commands_info.yml
|
135
|
+
- spec/fixtures/initial_state.yml
|
136
|
+
- spec/fixtures/line_with_equation.yml
|
137
|
+
- spec/fixtures/line_with_regex.yml
|
138
|
+
- spec/fixtures/line_with_variable.yml
|
139
|
+
- spec/fixtures/short_test.html
|
140
|
+
- spec/fixtures/short_test_result.yml
|
141
|
+
- spec/parser_spec.rb
|
142
|
+
- spec/spec_helper.rb
|
143
|
+
- spec/support/fixtures.rb
|
144
|
+
homepage:
|
145
|
+
licenses:
|
146
|
+
- MIT
|
147
|
+
metadata: {}
|
148
|
+
post_install_message:
|
149
|
+
rdoc_options: []
|
150
|
+
require_paths:
|
151
|
+
- lib
|
152
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: '0'
|
157
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - ">="
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: '0'
|
162
|
+
requirements: []
|
163
|
+
rubyforge_project:
|
164
|
+
rubygems_version: 2.4.5
|
165
|
+
signing_key:
|
166
|
+
specification_version: 4
|
167
|
+
summary: Run selenium tests with webkit. Headless.
|
168
|
+
test_files:
|
169
|
+
- spec/fixtures/commands.html
|
170
|
+
- spec/fixtures/commands.yml
|
171
|
+
- spec/fixtures/commands_info.yml
|
172
|
+
- spec/fixtures/initial_state.yml
|
173
|
+
- spec/fixtures/line_with_equation.yml
|
174
|
+
- spec/fixtures/line_with_regex.yml
|
175
|
+
- spec/fixtures/line_with_variable.yml
|
176
|
+
- spec/fixtures/short_test.html
|
177
|
+
- spec/fixtures/short_test_result.yml
|
178
|
+
- spec/parser_spec.rb
|
179
|
+
- spec/spec_helper.rb
|
180
|
+
- spec/support/fixtures.rb
|
181
|
+
has_rdoc:
|