cucumber-websteps 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.travis.yml +4 -0
- data/.yardopts +3 -0
- data/Gemfile +4 -0
- data/README.md +23 -0
- data/Rakefile +16 -0
- data/cucumber-websteps.gemspec +27 -0
- data/cucumber.yml +3 -0
- data/features/CHANGELOG.md +38 -0
- data/features/README.md +47 -0
- data/features/defined_steps/forms_steps.feature +76 -0
- data/features/defined_steps/simple_steps.feature +27 -0
- data/features/setup/rack_test.rb +5 -0
- data/features/setup/selenium.rb +12 -0
- data/features/step_definitions/web_steps.rb +1 -0
- data/features/support/attachments/avatar.png +0 -0
- data/features/support/env.rb +18 -0
- data/features/support/paths.rb +39 -0
- data/features/support/selectors.rb +43 -0
- data/features/support/test_app.rb +29 -0
- data/features/support/test_app_views/congratulations.erb +4 -0
- data/features/support/test_app_views/form.erb +69 -0
- data/features/support/test_app_views/form_result.erb +29 -0
- data/features/support/test_app_views/home.erb +33 -0
- data/features/support/test_app_views/layout.erb +9 -0
- data/lib/_unsorted.rb +38 -0
- data/lib/cucumber/websteps.rb +23 -0
- data/lib/cucumber/websteps/browsing_steps.rb +82 -0
- data/lib/cucumber/websteps/debug_steps.rb +3 -0
- data/lib/cucumber/websteps/form_steps.rb +210 -0
- data/lib/cucumber/websteps/step_scoper.rb +9 -0
- data/lib/cucumber/websteps/version.rb +5 -0
- data/steps.jpg +0 -0
- metadata +132 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Cucumber web steps [![Build Status](http://travis-ci.org/kucaahbe/cucumber-websteps.png)](http://travis-ci.org/kucaahbe/cucumber-websteps)
|
2
|
+
==========================================================================================================================================
|
3
|
+
|
4
|
+
Description
|
5
|
+
-----------
|
6
|
+
|
7
|
+
[Capybara](http://github.com/jnicklas/capybara) powered common cucumber web steps for any web application (blackjack and hookers included).
|
8
|
+
|
9
|
+
<strong>Because!</strong>
|
10
|
+
|
11
|
+
* Here collected [all "web steps"](http://github.com/cucumber/cucumber-rails/blob/master/lib/generators/cucumber/install/templates/step_definitions/web_steps.rb.erb) you need and even more([<b>-=INCLUDED STEPS DOCUMENTATION LINK=-</b>](http://relishapp.com/kucaahbe/cucumber-websteps))
|
12
|
+
* Support your favorite testing framework (test/unit, minitest, rspec-1, rspec-2) <b>Note:</b> not yet tested with rspec-1, coming soon
|
13
|
+
* work with both :rack\_test and :selenium drivers
|
14
|
+
* all in one place, DRY.
|
15
|
+
* [launchy](http://rubygems.org/gems/launchy) included :)
|
16
|
+
* blackjack and hookers ;)
|
17
|
+
|
18
|
+
[Install](http://relishapp.com/kucaahbe/cucumber-websteps)
|
19
|
+
----------------------------------------------------------
|
20
|
+
|
21
|
+
and enjoy!
|
22
|
+
|
23
|
+
![Steps:)](http://github.com/kucaahbe/cucumber-websteps/raw/master/steps.jpg)
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require 'cucumber'
|
3
|
+
require 'cucumber/rake/task'
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
|
6
|
+
task :default => ['examples:rack_test']
|
7
|
+
#task :default => ['examples:rack_test','examples:selenium']
|
8
|
+
|
9
|
+
namespace :examples do
|
10
|
+
Cucumber::Rake::Task.new(:selenium) do |t|
|
11
|
+
t.profile='selenium'
|
12
|
+
end
|
13
|
+
Cucumber::Rake::Task.new(:rack_test) do |t|
|
14
|
+
t.profile='rack_test'
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "cucumber/websteps/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "cucumber-websteps"
|
7
|
+
s.version = Cucumber::Websteps::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["kucaahbe"]
|
10
|
+
s.email = ["kucaahbe@ukr.net"]
|
11
|
+
s.homepage = "http://relishapp.com/kucaahbe/cucumber-websteps"
|
12
|
+
s.summary = %q{Capybara powered common cucumber web steps for any web application (blackjack and hookers included).}
|
13
|
+
s.description = %q{Here cucumber web steps collected during BDD practice in different projects, all steps are tested to work with :rack_test and :selenium drivers. Test framework agnostic (you can use them with rspec-1, rspec-2, test/unit and minitest).}
|
14
|
+
|
15
|
+
s.rubyforge_project = "cucumber-websteps"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency 'capybara'
|
23
|
+
s.add_dependency 'cucumber'
|
24
|
+
s.add_dependency 'launchy'
|
25
|
+
|
26
|
+
s.add_development_dependency 'sinatra'
|
27
|
+
end
|
data/cucumber.yml
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
0.9.1
|
2
|
+
-----
|
3
|
+
|
4
|
+
* TODO: added 'about disabled form fields'
|
5
|
+
* TODO: handle disabled form fields
|
6
|
+
|
7
|
+
0.9.0
|
8
|
+
-----
|
9
|
+
|
10
|
+
* added and improved standard cucumber websteps:
|
11
|
+
* 'I fill in "field"...' :
|
12
|
+
|
13
|
+
When I fill in "TextArea" with:
|
14
|
+
"""
|
15
|
+
some
|
16
|
+
bla
|
17
|
+
bla
|
18
|
+
"""
|
19
|
+
|
20
|
+
* 'I fill in the following' now can handle selects, checkboxes and radiobuttons:
|
21
|
+
|
22
|
+
When I fill in the following:
|
23
|
+
| Name | my name |
|
24
|
+
| Sex (select) | Male |
|
25
|
+
| Accept user agrement (checkbox) | check |
|
26
|
+
| Send me letters (checkbox) | uncheck |
|
27
|
+
| radio 1 (radio) | choose |
|
28
|
+
| Avatar (file) | avatar.png |
|
29
|
+
* 'I attach file' step will raise exception if file not found and will search file in features/support/attachments
|
30
|
+
* improved 'I should see //...' step to add options to regexp
|
31
|
+
* added 'I should be redirected to' step(but not finished)
|
32
|
+
* added 'I select following values'
|
33
|
+
* added 'I unselect following values'
|
34
|
+
* added 'the select "select" should have following options'
|
35
|
+
* added 'the following values should be selected'
|
36
|
+
* added 'the following values should not be selected'
|
37
|
+
* added 'I should see 2 elements kind of ...'
|
38
|
+
* added 'I should not see elements kind of ...'
|
data/features/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
Cucumber web steps [![Build Status](http://travis-ci.org/kucaahbe/cucumber-websteps.png)](http://travis-ci.org/kucaahbe/cucumber-websteps)
|
2
|
+
==========================================================================================================================================
|
3
|
+
|
4
|
+
Description
|
5
|
+
-----------
|
6
|
+
|
7
|
+
[Capybara](http://github.com/jnicklas/capybara) powered common cucumber web steps for any web application (blackjack and hookers included).
|
8
|
+
|
9
|
+
<strong>Because!</strong>
|
10
|
+
|
11
|
+
* Here collected [all "web steps"](http://github.com/cucumber/cucumber-rails/blob/master/lib/generators/cucumber/install/templates/step_definitions/web_steps.rb.erb) you need and even more([<b>-=INCLUDED STEPS DOCUMENTATION LINK=-</b>](http://relishapp.com/kucaahbe/cucumber-websteps))
|
12
|
+
* Support your favorite testing framework (test/unit, minitest, rspec-1, rspec-2) <b>Note:</b> not yet tested with rspec-1, coming soon
|
13
|
+
* work with both :rack\_test and :selenium drivers
|
14
|
+
* all in one place, DRY.
|
15
|
+
* [launchy](http://rubygems.org/gems/launchy) included :)
|
16
|
+
* blackjack and hookers ;)
|
17
|
+
|
18
|
+
Install
|
19
|
+
-------
|
20
|
+
|
21
|
+
### install gem
|
22
|
+
|
23
|
+
run in console:
|
24
|
+
|
25
|
+
[sudo] gem install cucumber-websteps
|
26
|
+
|
27
|
+
or add to Gemfile:
|
28
|
+
|
29
|
+
gem 'cucumber-websteps'
|
30
|
+
|
31
|
+
### setup in application
|
32
|
+
|
33
|
+
if you are using rails, then you already <b>have (and know about)</b> following files:
|
34
|
+
|
35
|
+
<b>features/support/paths.rb</b>
|
36
|
+
|
37
|
+
<b>features/support/selectors.rb</b>
|
38
|
+
|
39
|
+
they should be as they is, if you do not have these files or don't know what they are then just add them with [this content](http://gist.github.com/993837)
|
40
|
+
<script src="https://gist.github.com/993837.js?file=paths.rb"></script>
|
41
|
+
<script src="https://gist.github.com/993837.js?file=selectors.rb"></script>
|
42
|
+
and replace (or add) <b>features/step_definitions/web_steps.rb</b> with [this content](http://gist.github.com/993837#file_web_steps.rb)
|
43
|
+
<script src="https://gist.github.com/993837.js?file=web_steps.rb"></script>
|
44
|
+
|
45
|
+
3. run your features!
|
46
|
+
|
47
|
+
TODO: add footer
|
@@ -0,0 +1,76 @@
|
|
1
|
+
Feature: Forms steps
|
2
|
+
|
3
|
+
Steps for various forms interaction
|
4
|
+
|
5
|
+
Scenario: Simple form steps
|
6
|
+
Given I am on the form page
|
7
|
+
When I fill in "Name" with "my name"
|
8
|
+
And I fill in "Describe yourself" with:
|
9
|
+
"""
|
10
|
+
this usefull
|
11
|
+
for textareas
|
12
|
+
"""
|
13
|
+
Then the select "Sex" should have following options:
|
14
|
+
| |
|
15
|
+
| Female |
|
16
|
+
| Male |
|
17
|
+
When I select "Male" from "Sex"
|
18
|
+
And I check "Accept user agrement"
|
19
|
+
And I uncheck "Send me letters"
|
20
|
+
And I choose "radio 1"
|
21
|
+
And I attach the file "avatar.png" to "Avatar"
|
22
|
+
Then the select "Your favorite colors" should have following options:
|
23
|
+
| black |
|
24
|
+
| white |
|
25
|
+
| red |
|
26
|
+
| green |
|
27
|
+
| yellow |
|
28
|
+
| blue |
|
29
|
+
When I select following values from "Your favorite colors":
|
30
|
+
| red |
|
31
|
+
| blue |
|
32
|
+
| green |
|
33
|
+
And I unselect following values from "Your favorite colors":
|
34
|
+
| green |
|
35
|
+
Then the "Name" field should contain "my name"
|
36
|
+
And the "Describe yourself" field should contain "this usefull\nfor textareas"
|
37
|
+
#And the "Name" field should not contain "not my name" --> failing in 1.9.2
|
38
|
+
And the "Accept user agrement" checkbox should be checked
|
39
|
+
And the "Do not touch me" checkbox should not be checked
|
40
|
+
#And the radio should be selected
|
41
|
+
And the following values should be selected in "Your favorite colors":
|
42
|
+
| red |
|
43
|
+
| blue |
|
44
|
+
And the following values should not be selected in "Your favorite colors":
|
45
|
+
| black |
|
46
|
+
| white |
|
47
|
+
| green |
|
48
|
+
| yellow |
|
49
|
+
When I press "Submit"
|
50
|
+
Then I should see "Form submited!"
|
51
|
+
And I should see "Name: my name"
|
52
|
+
And I should see "About me: this usefull for textareas"
|
53
|
+
And I should see "Sex: Male"
|
54
|
+
And I should see "Accepted user agrement: Yes"
|
55
|
+
And I should see "Like spam? No"
|
56
|
+
And I should see "radio: radio 1"
|
57
|
+
And I should see "Your favorite colors: red,blue"
|
58
|
+
#And I should see "TODO something with avatar"
|
59
|
+
|
60
|
+
Scenario: Massive form filling
|
61
|
+
Given I am on the form page
|
62
|
+
When I fill in the following:
|
63
|
+
| Name | my name |
|
64
|
+
| Sex (select) | Male |
|
65
|
+
| Accept user agrement (checkbox) | check |
|
66
|
+
| Send me letters (checkbox) | uncheck |
|
67
|
+
| radio 1 (radio) | choose |
|
68
|
+
| Avatar (file) | avatar.png |
|
69
|
+
Then the "Name" field should contain "my name"
|
70
|
+
#And the "Name" field should not contain "not my name" --> failing in 1.9.2
|
71
|
+
And the "Accept user agrement" checkbox should be checked
|
72
|
+
And the "Do not touch me" checkbox should not be checked
|
73
|
+
#And the radio should be selected
|
74
|
+
#And selectbox should contain
|
75
|
+
When I press "Submit"
|
76
|
+
Then I should see "Form submited!"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Feature: Simple browsing steps
|
2
|
+
|
3
|
+
With this steps you can:
|
4
|
+
|
5
|
+
* open pages
|
6
|
+
* click links
|
7
|
+
* match page contet
|
8
|
+
* match redirections
|
9
|
+
|
10
|
+
Scenario: Simple browsing steps copy-pasted (and maybe improved) from cucumber-rails generator
|
11
|
+
Given I am on the home page
|
12
|
+
When I follow "some link"
|
13
|
+
Then I should be on the congratulations page
|
14
|
+
And I should see "Great, you can click links!"
|
15
|
+
And I should not see "some bla-bla"
|
16
|
+
And I should see /great/i
|
17
|
+
And I should not see /bla-bla/
|
18
|
+
|
19
|
+
When I go to the home page
|
20
|
+
Then I should see 2 elements kind of table's header
|
21
|
+
And I should see 6 elements kind of "table tr"
|
22
|
+
And I should not see elements kind of paragraphs
|
23
|
+
And I should not see elements kind of "div.some_class"
|
24
|
+
|
25
|
+
When I go to the other page
|
26
|
+
Then I should be redirected to the congratulations page
|
27
|
+
And I should see "You was really redirected!"
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'cucumber/websteps'
|
Binary file
|
@@ -0,0 +1,18 @@
|
|
1
|
+
ENV['RACK_ENV']='test'
|
2
|
+
$LOAD_PATH.insert(0,File.dirname(__FILE__))
|
3
|
+
require 'test_app'
|
4
|
+
|
5
|
+
require 'capybara'
|
6
|
+
require 'capybara/dsl'
|
7
|
+
World(Capybara)
|
8
|
+
|
9
|
+
Capybara.app = TestApp
|
10
|
+
Capybara.default_selector = :css
|
11
|
+
|
12
|
+
begin
|
13
|
+
require 'minitest/unit'
|
14
|
+
World(MiniTest::Assertions)
|
15
|
+
rescue LoadError
|
16
|
+
require 'test/unit'
|
17
|
+
World(Test::Unit::Assertions)
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module NavigationHelpers
|
2
|
+
# Maps a name to a path. Used by the
|
3
|
+
#
|
4
|
+
# When /^I go to (.+)$/ do |page_name|
|
5
|
+
#
|
6
|
+
# step definition in web_steps.rb
|
7
|
+
#
|
8
|
+
def path_to(page_name)
|
9
|
+
case page_name
|
10
|
+
|
11
|
+
when /^the home\s?page$/
|
12
|
+
'/'
|
13
|
+
when /^the congratulations page$/
|
14
|
+
'/congratulations'
|
15
|
+
when /^the other page$/
|
16
|
+
'/other/page'
|
17
|
+
when /^the form page$/
|
18
|
+
'/form/page'
|
19
|
+
|
20
|
+
# Add more mappings here.
|
21
|
+
# Here is an example that pulls values out of the Regexp:
|
22
|
+
#
|
23
|
+
# when /^(.*)'s profile page$/i
|
24
|
+
# user_profile_path(User.find_by_login($1))
|
25
|
+
|
26
|
+
else
|
27
|
+
begin
|
28
|
+
page_name =~ /^the (.*) page$/
|
29
|
+
path_components = $1.split(/\s+/)
|
30
|
+
self.send(path_components.push('path').join('_').to_sym)
|
31
|
+
rescue NoMethodError, ArgumentError
|
32
|
+
raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
|
33
|
+
"Now, go and add a mapping in #{__FILE__}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
World(NavigationHelpers)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module HtmlSelectorsHelpers
|
2
|
+
# Maps a name to a selector. Used primarily by the
|
3
|
+
#
|
4
|
+
# When /^(.+) within (.+)$/ do |step, scope|
|
5
|
+
#
|
6
|
+
# step definitions in web_steps.rb
|
7
|
+
#
|
8
|
+
def selector_for(locator)
|
9
|
+
case locator
|
10
|
+
|
11
|
+
when "the page"
|
12
|
+
"html > body"
|
13
|
+
when "table's header"
|
14
|
+
"table > tr th"
|
15
|
+
when /^paragraphs?$/
|
16
|
+
"p"
|
17
|
+
|
18
|
+
# Add more mappings here.
|
19
|
+
# Here is an example that pulls values out of the Regexp:
|
20
|
+
#
|
21
|
+
# when /^the (notice|error|info) flash$/
|
22
|
+
# ".flash.#{$1}"
|
23
|
+
|
24
|
+
# You can also return an array to use a different selector
|
25
|
+
# type, like:
|
26
|
+
#
|
27
|
+
# when /the header/
|
28
|
+
# [:xpath, "//header"]
|
29
|
+
|
30
|
+
# This allows you to provide a quoted selector as the scope
|
31
|
+
# for "within" steps as was previously the default for the
|
32
|
+
# web steps:
|
33
|
+
when /^"(.+)"$/
|
34
|
+
$1
|
35
|
+
|
36
|
+
else
|
37
|
+
raise "Can't find mapping from \"#{locator}\" to a selector.\n" +
|
38
|
+
"Now, go and add a mapping in #{__FILE__}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
World(HtmlSelectorsHelpers)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
class TestApp < Sinatra::Base
|
3
|
+
|
4
|
+
set :views, File.dirname(__FILE__) + '/test_app_views'
|
5
|
+
|
6
|
+
get '/' do
|
7
|
+
erb :home
|
8
|
+
end
|
9
|
+
|
10
|
+
get '/congratulations' do
|
11
|
+
@redirected = params.has_key?("redirected")
|
12
|
+
erb :congratulations
|
13
|
+
end
|
14
|
+
|
15
|
+
get '/other/page' do
|
16
|
+
redirect to('/congratulations?redirected=true')
|
17
|
+
end
|
18
|
+
|
19
|
+
get '/form/page' do
|
20
|
+
erb :form
|
21
|
+
end
|
22
|
+
|
23
|
+
get '/form/submit' do
|
24
|
+
erb :form_result
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
TestApp.run! unless defined?(Cucumber)
|
@@ -0,0 +1,69 @@
|
|
1
|
+
<form action="/form/submit" method="get" accept-charset="utf-8">
|
2
|
+
|
3
|
+
<p>
|
4
|
+
<label for="text_input">Name:</label>
|
5
|
+
<input type="text" name="name" id="text_input"/>
|
6
|
+
</p>
|
7
|
+
|
8
|
+
<p>
|
9
|
+
<label for="text_area">Describe yourself:</label>
|
10
|
+
<textarea name="description" id="text_area"></textarea>
|
11
|
+
</p>
|
12
|
+
|
13
|
+
<p>
|
14
|
+
<label for="select_1">Sex:</label>
|
15
|
+
<select id="select_1" name="sex">
|
16
|
+
<option value=""></option>
|
17
|
+
<option value="Female">Female</option>
|
18
|
+
<option value="Male">Male</option>
|
19
|
+
</select>
|
20
|
+
</p>
|
21
|
+
|
22
|
+
<p>
|
23
|
+
<label for="select_2">Your favorite colors:</label>
|
24
|
+
<select id="select_2" name="colors[]" multiple="multiple">
|
25
|
+
<option value="black">black</option>
|
26
|
+
<option value="white">white</option>
|
27
|
+
<option value="red">red</option>
|
28
|
+
<option value="green">green</option>
|
29
|
+
<option value="yellow">yellow</option>
|
30
|
+
<option value="blue">blue</option>
|
31
|
+
</select>
|
32
|
+
</p>
|
33
|
+
|
34
|
+
<p>
|
35
|
+
<input type="checkbox" name="do_not_touch" id="unchecked_checkbox" value="1"/>
|
36
|
+
<label for="unchecked_checkbox">Do not touch me</label>
|
37
|
+
</p>
|
38
|
+
|
39
|
+
<p>
|
40
|
+
<input type="checkbox" name="agrement" id="checkbox_1" value="1"/>
|
41
|
+
<label for="checkbox_1">Accept user agrement</label>
|
42
|
+
</p>
|
43
|
+
|
44
|
+
<p>
|
45
|
+
<input type="checkbox" name="spam" id="checkbox_2" value="1" checked="checked"/>
|
46
|
+
<label for="checkbox_2">Send me letters</label>
|
47
|
+
</p>
|
48
|
+
|
49
|
+
<p>
|
50
|
+
<label for="radio_true">
|
51
|
+
<input id="radio_true" name="radio" type="radio" value="radio 1" />radio 1
|
52
|
+
</label>
|
53
|
+
</p>
|
54
|
+
|
55
|
+
<p>
|
56
|
+
<label for="radio_false">
|
57
|
+
<input id="radio_false" name="radio" type="radio" value="radio 2" />radio 2
|
58
|
+
</label>
|
59
|
+
</p>
|
60
|
+
|
61
|
+
<p>
|
62
|
+
<label for="file">Avatar:</label>
|
63
|
+
<input type="file" name="file" id="file"/>
|
64
|
+
</p>
|
65
|
+
|
66
|
+
<p>
|
67
|
+
<input type="submit" value="Submit"/>
|
68
|
+
</p>
|
69
|
+
</form>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<h1>Form submited!</h1>
|
2
|
+
|
3
|
+
<p>
|
4
|
+
<strong>Name:</strong> <%= params[:name] %>
|
5
|
+
</p>
|
6
|
+
|
7
|
+
<p>
|
8
|
+
<strong>About me:</strong> <%= params[:description] %>
|
9
|
+
</p>
|
10
|
+
|
11
|
+
<p>
|
12
|
+
<strong>Sex:</strong> <%= params[:sex] %>
|
13
|
+
</p>
|
14
|
+
|
15
|
+
<p>
|
16
|
+
<strong>Accepted user agrement:</strong> <%= params[:agrement]=="1" ? 'Yes' : 'No' %>
|
17
|
+
</p>
|
18
|
+
|
19
|
+
<p>
|
20
|
+
<strong>Like spam?</strong> <%= params[:spam]=="1" ? 'Yes' : 'No' %>
|
21
|
+
</p>
|
22
|
+
|
23
|
+
<p>
|
24
|
+
<strong>radio:</strong> <%= params[:radio] %>
|
25
|
+
</p>
|
26
|
+
|
27
|
+
<p>
|
28
|
+
<strong>Your favorite colors:</strong> <%= (params[:colors]||[]).join(',') %>
|
29
|
+
</p>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<a href="/congratulations">some link</a>
|
2
|
+
|
3
|
+
<table border="1">
|
4
|
+
<tr>
|
5
|
+
<th>header 1</th>
|
6
|
+
<th>header 2</th>
|
7
|
+
</tr>
|
8
|
+
|
9
|
+
<tr>
|
10
|
+
<td>bla</td>
|
11
|
+
<td>bla</td>
|
12
|
+
</tr>
|
13
|
+
|
14
|
+
<tr>
|
15
|
+
<td>bla</td>
|
16
|
+
<td>bla</td>
|
17
|
+
</tr>
|
18
|
+
|
19
|
+
<tr>
|
20
|
+
<td>bla</td>
|
21
|
+
<td>bla</td>
|
22
|
+
</tr>
|
23
|
+
|
24
|
+
<tr>
|
25
|
+
<td>bla</td>
|
26
|
+
<td>bla</td>
|
27
|
+
</tr>
|
28
|
+
|
29
|
+
<tr>
|
30
|
+
<td>bla</td>
|
31
|
+
<td>bla</td>
|
32
|
+
</tr>
|
33
|
+
</table>
|
data/lib/_unsorted.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# TL;DR: YOU SHOULD DELETE THIS FILE
|
2
|
+
#
|
3
|
+
# This file was generated by Cucumber-Rails and is only here to get you a head start
|
4
|
+
# These step definitions are thin wrappers around the Capybara/Webrat API that lets you
|
5
|
+
# visit pages, interact with widgets and make assertions about page content.
|
6
|
+
#
|
7
|
+
# If you use these step definitions as basis for your features you will quickly end up
|
8
|
+
# with features that are:
|
9
|
+
#
|
10
|
+
# * Hard to maintain
|
11
|
+
# * Verbose to read
|
12
|
+
#
|
13
|
+
# A much better approach is to write your own higher level step definitions, following
|
14
|
+
# the advice in the following blog posts:
|
15
|
+
#
|
16
|
+
# * http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories.html
|
17
|
+
# * http://dannorth.net/2011/01/31/whose-domain-is-it-anyway/
|
18
|
+
# * http://elabs.se/blog/15-you-re-cuking-it-wrong
|
19
|
+
#
|
20
|
+
|
21
|
+
|
22
|
+
#When /^(?:|I )fill in "([^"]*)" for "([^"]*)"$/ do |value, field|
|
23
|
+
#fill_in(field, :with => value)
|
24
|
+
#end
|
25
|
+
|
26
|
+
|
27
|
+
Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
|
28
|
+
query = URI.parse(current_url).query
|
29
|
+
actual_params = query ? CGI.parse(query) : {}
|
30
|
+
expected_params = {}
|
31
|
+
expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')}
|
32
|
+
|
33
|
+
if actual_params.respond_to? :should
|
34
|
+
actual_params.should == expected_params
|
35
|
+
else
|
36
|
+
assert_equal expected_params, actual_params
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
unless defined?(SUPPORT_DIR)
|
5
|
+
features_dir = caller.select { |path| path =~ /features/ }.first.split('/')
|
6
|
+
2.times { features_dir.pop }
|
7
|
+
SUPPORT_DIR = File.join(features_dir,'support')
|
8
|
+
end
|
9
|
+
|
10
|
+
require File.join(SUPPORT_DIR,'paths.rb')
|
11
|
+
require File.join(SUPPORT_DIR,'selectors.rb')
|
12
|
+
|
13
|
+
module WithinHelpers
|
14
|
+
def with_scope(locator)
|
15
|
+
locator ? within(*selector_for(locator)) { yield } : yield
|
16
|
+
end
|
17
|
+
end
|
18
|
+
World(WithinHelpers)
|
19
|
+
|
20
|
+
require 'cucumber/websteps/step_scoper'
|
21
|
+
require 'cucumber/websteps/browsing_steps'
|
22
|
+
require 'cucumber/websteps/form_steps'
|
23
|
+
require 'cucumber/websteps/debug_steps'
|
@@ -0,0 +1,82 @@
|
|
1
|
+
Given /^(?:|I )am on (.+)$/ do |page_name|
|
2
|
+
visit path_to(page_name)
|
3
|
+
end
|
4
|
+
|
5
|
+
When /^(?:|I )go to (.+)$/ do |page_name|
|
6
|
+
visit path_to(page_name)
|
7
|
+
end
|
8
|
+
|
9
|
+
When /^(?:|I )follow "([^"]*)"$/ do |link|
|
10
|
+
click_link(link)
|
11
|
+
end
|
12
|
+
|
13
|
+
Then /^I should be redirected to (.+)$/ do |page|
|
14
|
+
puts 'TODO: make this done'
|
15
|
+
Then "I should be on #{page}"
|
16
|
+
end
|
17
|
+
|
18
|
+
Then /^(?:|I )should see "([^"]*)"$/ do |text|
|
19
|
+
if page.respond_to? :should
|
20
|
+
page.should have_content(text)
|
21
|
+
else
|
22
|
+
assert page.has_content?(text)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
Then /^(?:|I )should not see "([^"]*)"$/ do |text|
|
27
|
+
if page.respond_to? :should
|
28
|
+
page.should have_no_content(text)
|
29
|
+
else
|
30
|
+
assert page.has_no_content?(text)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Then /^(?:|I )should see \/([^\/]*)\/([imxo])?$/ do |regexp,flags|
|
35
|
+
regexp_opts = [regexp,flags].compact
|
36
|
+
regexp = Regexp.new(*regexp_opts)
|
37
|
+
|
38
|
+
if page.respond_to? :should
|
39
|
+
page.should have_xpath('//*', :text => regexp)
|
40
|
+
else
|
41
|
+
assert page.has_xpath?('//*', :text => regexp)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
Then /^(?:|I )should not see \/([^\/]*)\/([imxo])?$/ do |regexp,flags|
|
46
|
+
regexp_opts = [regexp,flags].compact
|
47
|
+
regexp = Regexp.new(*regexp_opts)
|
48
|
+
|
49
|
+
if page.respond_to? :should
|
50
|
+
page.should have_no_xpath('//*', :text => regexp)
|
51
|
+
else
|
52
|
+
assert page.has_no_xpath?('//*', :text => regexp)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Then /^(?:|I )should be on (.+)$/ do |page_name|
|
57
|
+
current_path = URI.parse(current_url).path
|
58
|
+
if current_path.respond_to? :should
|
59
|
+
current_path.should == path_to(page_name)
|
60
|
+
else
|
61
|
+
assert_equal path_to(page_name), current_path
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
Then /^I should see (\d+) elements? kind of (.+)$/ do |count, locator|
|
66
|
+
actual_count = all(selector_for(locator)).count
|
67
|
+
count = count.to_i
|
68
|
+
|
69
|
+
if actual_count.respond_to?(:should)
|
70
|
+
actual_count.should eq(count)
|
71
|
+
else
|
72
|
+
assert_equal count, actual_count
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
Then /^I should not see elements? kind of (.+)$/ do |locator|
|
77
|
+
if defined?(RSpec)
|
78
|
+
page.should_not have_css(selector_for(locator))
|
79
|
+
else
|
80
|
+
assert page.has_no_css?(selector_for(locator))
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
# Use this to fill in an entire form with data from a table. Example:
|
2
|
+
#
|
3
|
+
# When I fill in the following:
|
4
|
+
# | Account Number | 5002 |
|
5
|
+
# | Expiry date | 2009-11-01 |
|
6
|
+
# | Note | Nice guy |
|
7
|
+
# | Wants Email? | |
|
8
|
+
# | Sex (select) | Male |
|
9
|
+
# | Accept user agrement (checkbox) | check |
|
10
|
+
# | Send me letters (checkbox) | uncheck |
|
11
|
+
# | radio 1 (radio) | choose |
|
12
|
+
# | Avatar (file) | avatar.png |
|
13
|
+
#
|
14
|
+
When /^(?:|I )fill in the following:$/ do |fields|
|
15
|
+
|
16
|
+
select_tag = /^(.+\S+)\s*(?:\(select\))$/
|
17
|
+
check_box_tag = /^(.+\S+)\s*(?:\(checkbox\))$/
|
18
|
+
radio_button = /^(.+\S+)\s*(?:\(radio\))$/
|
19
|
+
file_field = /^(.+\S+)\s*(?:\(file\))$/
|
20
|
+
|
21
|
+
fields.rows_hash.each do |name, value|
|
22
|
+
case name
|
23
|
+
when select_tag
|
24
|
+
When %(I select "#{value}" from "#{$1}")
|
25
|
+
when check_box_tag
|
26
|
+
case value
|
27
|
+
when 'check'
|
28
|
+
When %(I check "#{$1}")
|
29
|
+
when 'uncheck'
|
30
|
+
When %(I uncheck "#{$1}")
|
31
|
+
else
|
32
|
+
raise 'checkbox values: check|uncheck!'
|
33
|
+
end
|
34
|
+
when radio_button
|
35
|
+
When %{I choose "#{$1}"}
|
36
|
+
when file_field
|
37
|
+
When %{I attach the file "#{value}" to "#{$1}"}
|
38
|
+
else
|
39
|
+
When %{I fill in "#{name}" with "#{value}"}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
When /^(?:|I )fill in "([^"]*)" with "([^"]*)"$/ do |field, value|
|
45
|
+
fill_in(field, :with => value)
|
46
|
+
end
|
47
|
+
|
48
|
+
When /^(?:|I )fill in "([^"]*)" with:$/ do |field, value|
|
49
|
+
fill_in(field, :with => value)
|
50
|
+
end
|
51
|
+
|
52
|
+
When /^(?:|I )select "([^"]*)" from "([^"]*)"$/ do |value, field|
|
53
|
+
select(value, :from => field)
|
54
|
+
end
|
55
|
+
|
56
|
+
When /^(?:|I )check "([^"]*)"$/ do |field|
|
57
|
+
check(field)
|
58
|
+
end
|
59
|
+
|
60
|
+
When /^(?:|I )uncheck "([^"]*)"$/ do |field|
|
61
|
+
uncheck(field)
|
62
|
+
end
|
63
|
+
|
64
|
+
When /^(?:|I )choose "([^"]*)"$/ do |field|
|
65
|
+
choose(field)
|
66
|
+
end
|
67
|
+
|
68
|
+
When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"$/ do |file, field|
|
69
|
+
path = File.expand_path(File.join(SUPPORT_DIR,"attachments/#{file}"))
|
70
|
+
raise RuntimeError, "file '#{path}' does not exists" unless File.exists?(path)
|
71
|
+
|
72
|
+
attach_file(field, path)
|
73
|
+
end
|
74
|
+
|
75
|
+
Then /^the "([^"]*)" field(?: within (.*))? should contain "([^"]*)"$/ do |field, parent, value|
|
76
|
+
with_scope(parent) do
|
77
|
+
field = find_field(field)
|
78
|
+
field_value = (field.tag_name == 'textarea') ? field.text : field.value
|
79
|
+
if field_value.respond_to? :should
|
80
|
+
field_value.should =~ /#{value}/
|
81
|
+
else
|
82
|
+
assert_match(/#{value}/, field_value)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
Then /^the "([^"]*)" field(?: within (.*))? should not contain "([^"]*)"$/ do |field, parent, value|
|
88
|
+
with_scope(parent) do
|
89
|
+
field = find_field(field)
|
90
|
+
field_value = (field.tag_name == 'textarea') ? field.text : field.value
|
91
|
+
if field_value.respond_to? :should_not
|
92
|
+
field_value.should_not =~ /#{value}/
|
93
|
+
else
|
94
|
+
assert_no_match(/#{value}/, field_value)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
Then /^the "([^"]*)" checkbox(?: within (.*))? should be checked$/ do |label, parent|
|
100
|
+
with_scope(parent) do
|
101
|
+
field_checked = find_field(label)['checked']
|
102
|
+
if field_checked.respond_to? :should
|
103
|
+
field_checked.should be_true
|
104
|
+
else
|
105
|
+
assert field_checked
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
Then /^the "([^"]*)" checkbox(?: within (.*))? should not be checked$/ do |label, parent|
|
111
|
+
with_scope(parent) do
|
112
|
+
field_checked = find_field(label)['checked']
|
113
|
+
if field_checked.respond_to? :should
|
114
|
+
field_checked.should be_false
|
115
|
+
else
|
116
|
+
assert !field_checked
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
When /^(?:|I )press "([^"]*)"$/ do |button|
|
122
|
+
click_button(button)
|
123
|
+
end
|
124
|
+
|
125
|
+
Then /^the select "([^"]*)" should have following options:$/ do |field, options|
|
126
|
+
options = options.transpose.raw
|
127
|
+
if options.size > 1
|
128
|
+
raise 'table should have only one column in this step!'
|
129
|
+
else
|
130
|
+
options = options.first
|
131
|
+
end
|
132
|
+
|
133
|
+
actual_options = find_field(field).all('option').map { |option| option.text }
|
134
|
+
|
135
|
+
if options.respond_to?(:should)
|
136
|
+
options.should eq(actual_options)
|
137
|
+
else
|
138
|
+
assert_equal options, actual_options
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
When /^(?:I|i) select following values from "([^"]*)":$/ do |field, values|
|
143
|
+
values = values.transpose.raw
|
144
|
+
if values.size > 1
|
145
|
+
raise 'table should have only one column in this step!'
|
146
|
+
else
|
147
|
+
values = values.first
|
148
|
+
end
|
149
|
+
|
150
|
+
values.each do |value|
|
151
|
+
select(value, :from => field)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
When /^(?:I|i) unselect following values from "([^"]*)":$/ do |field, values|
|
156
|
+
values = values.transpose.raw
|
157
|
+
if values.size > 1
|
158
|
+
raise 'table should have only one column in this step!'
|
159
|
+
else
|
160
|
+
values = values.first
|
161
|
+
end
|
162
|
+
|
163
|
+
values.each do |value|
|
164
|
+
unselect(value, :from => field)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
Then /^the following values should be selected in "([^"]*)":$/ do |select_box, values|
|
169
|
+
values = values.transpose.raw
|
170
|
+
if values.size > 1
|
171
|
+
raise 'table should have only one column in this step!'
|
172
|
+
else
|
173
|
+
values = values.first
|
174
|
+
end
|
175
|
+
|
176
|
+
select_box=find_field(select_box)
|
177
|
+
unless select_box['multiple']
|
178
|
+
raise "this is not multiple select box!"
|
179
|
+
else
|
180
|
+
values.each do |value|
|
181
|
+
if select_box.respond_to?(:should)
|
182
|
+
select_box.value.should include(value)
|
183
|
+
else
|
184
|
+
assert select_box.value.include?(value)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
Then /^the following values should not be selected in "([^"]*)":$/ do |select_box, values|
|
191
|
+
values = values.transpose.raw
|
192
|
+
if values.size > 1
|
193
|
+
raise 'table should have only one column in this step!'
|
194
|
+
else
|
195
|
+
values = values.first
|
196
|
+
end
|
197
|
+
|
198
|
+
select_box=find_field(select_box)
|
199
|
+
unless select_box['multiple']
|
200
|
+
raise "this is not multiple select box!"
|
201
|
+
else
|
202
|
+
values.each do |value|
|
203
|
+
if select_box.respond_to?(:should)
|
204
|
+
select_box.value.should_not include(value)
|
205
|
+
else
|
206
|
+
assert !select_box.value.include?(value)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# Single-line step scoper
|
2
|
+
When /^(.*) within ([^:]+)$/ do |step, parent|
|
3
|
+
with_scope(parent) { When step }
|
4
|
+
end
|
5
|
+
|
6
|
+
# Multi-line step scoper
|
7
|
+
When /^(.*) within ([^:]+):$/ do |step, parent, table_or_string|
|
8
|
+
with_scope(parent) { When "#{step}:", table_or_string }
|
9
|
+
end
|
data/steps.jpg
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cucumber-websteps
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.9.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- kucaahbe
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-05-26 00:00:00 +03:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: capybara
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: cucumber
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: "0"
|
36
|
+
type: :runtime
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: launchy
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: sinatra
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
type: :development
|
59
|
+
version_requirements: *id004
|
60
|
+
description: Here cucumber web steps collected during BDD practice in different projects, all steps are tested to work with :rack_test and :selenium drivers. Test framework agnostic (you can use them with rspec-1, rspec-2, test/unit and minitest).
|
61
|
+
email:
|
62
|
+
- kucaahbe@ukr.net
|
63
|
+
executables: []
|
64
|
+
|
65
|
+
extensions: []
|
66
|
+
|
67
|
+
extra_rdoc_files: []
|
68
|
+
|
69
|
+
files:
|
70
|
+
- .gitignore
|
71
|
+
- .travis.yml
|
72
|
+
- .yardopts
|
73
|
+
- Gemfile
|
74
|
+
- README.md
|
75
|
+
- Rakefile
|
76
|
+
- cucumber-websteps.gemspec
|
77
|
+
- cucumber.yml
|
78
|
+
- features/CHANGELOG.md
|
79
|
+
- features/README.md
|
80
|
+
- features/defined_steps/forms_steps.feature
|
81
|
+
- features/defined_steps/simple_steps.feature
|
82
|
+
- features/setup/rack_test.rb
|
83
|
+
- features/setup/selenium.rb
|
84
|
+
- features/step_definitions/web_steps.rb
|
85
|
+
- features/support/attachments/avatar.png
|
86
|
+
- features/support/env.rb
|
87
|
+
- features/support/paths.rb
|
88
|
+
- features/support/selectors.rb
|
89
|
+
- features/support/test_app.rb
|
90
|
+
- features/support/test_app_views/congratulations.erb
|
91
|
+
- features/support/test_app_views/form.erb
|
92
|
+
- features/support/test_app_views/form_result.erb
|
93
|
+
- features/support/test_app_views/home.erb
|
94
|
+
- features/support/test_app_views/layout.erb
|
95
|
+
- lib/_unsorted.rb
|
96
|
+
- lib/cucumber/websteps.rb
|
97
|
+
- lib/cucumber/websteps/browsing_steps.rb
|
98
|
+
- lib/cucumber/websteps/debug_steps.rb
|
99
|
+
- lib/cucumber/websteps/form_steps.rb
|
100
|
+
- lib/cucumber/websteps/step_scoper.rb
|
101
|
+
- lib/cucumber/websteps/version.rb
|
102
|
+
- steps.jpg
|
103
|
+
has_rdoc: true
|
104
|
+
homepage: http://relishapp.com/kucaahbe/cucumber-websteps
|
105
|
+
licenses: []
|
106
|
+
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: "0"
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: "0"
|
124
|
+
requirements: []
|
125
|
+
|
126
|
+
rubyforge_project: cucumber-websteps
|
127
|
+
rubygems_version: 1.5.2
|
128
|
+
signing_key:
|
129
|
+
specification_version: 3
|
130
|
+
summary: Capybara powered common cucumber web steps for any web application (blackjack and hookers included).
|
131
|
+
test_files: []
|
132
|
+
|