capybara-screenshot 0.3.22 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +1 -1
- data/.travis.yml +9 -0
- data/Appraisals +31 -0
- data/CHANGELOG.md +16 -0
- data/Gemfile +1 -0
- data/README.md +23 -17
- data/Rakefile +9 -1
- data/capybara-screenshot.gemspec +7 -1
- data/gemfiles/cucumber.1.2.gemfile +9 -0
- data/gemfiles/cucumber.1.2.gemfile.lock +99 -0
- data/gemfiles/cucumber.1.3.0.gemfile +9 -0
- data/gemfiles/cucumber.1.3.0.gemfile.lock +100 -0
- data/gemfiles/latest.gemfile +8 -0
- data/gemfiles/latest.gemfile.lock +102 -0
- data/gemfiles/rspec.2.14.gemfile +9 -0
- data/gemfiles/rspec.2.14.gemfile.lock +98 -0
- data/gemfiles/rspec.2.99.gemfile +9 -0
- data/gemfiles/rspec.2.99.gemfile.lock +98 -0
- data/gemfiles/rspec.3.0.gemfile +9 -0
- data/gemfiles/rspec.3.0.gemfile.lock +102 -0
- data/gemfiles/spinach.0.7.gemfile +9 -0
- data/gemfiles/spinach.0.7.gemfile.lock +100 -0
- data/gemfiles/spinach.0.8.0.gemfile +9 -0
- data/gemfiles/spinach.0.8.0.gemfile.lock +100 -0
- data/lib/capybara-screenshot.rb +5 -0
- data/lib/capybara-screenshot/capybara.rb +12 -0
- data/lib/capybara-screenshot/cucumber.rb +18 -12
- data/lib/capybara-screenshot/minitest.rb +15 -16
- data/lib/capybara-screenshot/rspec.rb +19 -11
- data/lib/capybara-screenshot/rspec/text_reporter.rb +21 -3
- data/lib/capybara-screenshot/spinach.rb +22 -6
- data/lib/capybara-screenshot/testunit.rb +25 -7
- data/lib/capybara-screenshot/version.rb +1 -1
- data/spec/cucumber/cucumber_spec.rb +64 -0
- data/spec/cucumber/step_definitions/step_definitions.rb +18 -0
- data/spec/cucumber/support/env.rb +17 -0
- data/spec/feature/minitest_spec.rb +90 -0
- data/spec/feature/testunit_spec.rb +71 -0
- data/spec/rspec/rspec_spec.rb +101 -0
- data/spec/spec_helper.rb +9 -1
- data/spec/spinach/spinach_spec.rb +53 -0
- data/spec/spinach/support/spinach_failure.rb +41 -0
- data/spec/support/common_setup.rb +28 -0
- data/spec/support/html_reporter_context.rb +1 -1
- data/spec/support/test_app.rb +13 -0
- data/spec/{rspec → unit}/base_reporter_spec.rb +2 -2
- data/spec/{rspec_spec.rb → unit/capybara-screenshot_rspec_spec.rb} +15 -10
- data/spec/{capybara-screenshot_spec.rb → unit/capybara-screenshot_spec.rb} +8 -8
- data/spec/unit/capybara_spec.rb +50 -0
- data/spec/{rspec → unit/rspec_reporters}/html_embed_reporter_spec.rb +2 -2
- data/spec/{rspec → unit/rspec_reporters}/html_link_reporter_spec.rb +2 -2
- data/spec/unit/rspec_reporters/text_reporter_spec.rb +96 -0
- data/spec/{rspec → unit/rspec_reporters}/textmate_link_reporter_spec.rb +5 -3
- data/spec/unit/saver_spec.rb +269 -0
- metadata +161 -41
- data/spec/capybara-screenshot/capybara_spec.rb +0 -18
- data/spec/capybara-screenshot/saver_spec.rb +0 -269
- data/spec/rspec/text_reporter_spec.rb +0 -76
@@ -0,0 +1,18 @@
|
|
1
|
+
When(/^I click on a missing link$/) do
|
2
|
+
click_on "you'll never find me"
|
3
|
+
end
|
4
|
+
|
5
|
+
When(/^I click on a missing link on a different page in a different session$/) do
|
6
|
+
using_session :different_session do
|
7
|
+
visit '/different_page'
|
8
|
+
click_on "you'll never find me"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
When(/^I visit "([^"]*)"$/) do |path|
|
13
|
+
visit path
|
14
|
+
end
|
15
|
+
|
16
|
+
Then(/^I trigger an unhandled exception/) do
|
17
|
+
raise "you can't handle me"
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'capybara/cucumber'
|
2
|
+
require 'capybara-screenshot'
|
3
|
+
require 'capybara-screenshot/cucumber'
|
4
|
+
require 'aruba/cucumber'
|
5
|
+
require 'aruba/jruby'
|
6
|
+
|
7
|
+
Capybara::Screenshot.register_filename_prefix_formatter(:cucumber) do |fault|
|
8
|
+
'my_screenshot'
|
9
|
+
end
|
10
|
+
|
11
|
+
Before do
|
12
|
+
@aruba_timeout_seconds = 60
|
13
|
+
end if RUBY_PLATFORM == 'java'
|
14
|
+
|
15
|
+
After('@restore-capybara-default-session') do
|
16
|
+
Capybara.session_name = nil
|
17
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Using Capybara::Screenshot with MiniTest" do
|
4
|
+
include Aruba::Api
|
5
|
+
include CommonSetup
|
6
|
+
|
7
|
+
before do
|
8
|
+
clean_current_dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def run_failing_case(code)
|
12
|
+
write_file('test_failure.rb', <<-RUBY)
|
13
|
+
#{ensure_load_paths_valid}
|
14
|
+
require 'minitest/autorun'
|
15
|
+
require 'capybara'
|
16
|
+
require 'capybara-screenshot'
|
17
|
+
require 'capybara-screenshot/minitest'
|
18
|
+
|
19
|
+
#{setup_test_app}
|
20
|
+
Capybara::Screenshot.register_filename_prefix_formatter(:minitest) do |test_case|
|
21
|
+
test_name = test_case.respond_to?(:name) ? test_case.name : test_case.__name__
|
22
|
+
raise "expected fault" unless test_name.include? 'test_failure'
|
23
|
+
'my_screenshot'
|
24
|
+
end
|
25
|
+
|
26
|
+
#{code}
|
27
|
+
RUBY
|
28
|
+
|
29
|
+
cmd = 'bundle exec ruby test_failure.rb'
|
30
|
+
run_simple cmd, false
|
31
|
+
expect(output_from(cmd)).to include %q{Unable to find link or button "you'll never find me"}
|
32
|
+
end
|
33
|
+
|
34
|
+
it "saves a screenshot on failure" do
|
35
|
+
run_failing_case <<-RUBY
|
36
|
+
module ActionDispatch
|
37
|
+
class IntegrationTest < Minitest::Unit::TestCase; end
|
38
|
+
end
|
39
|
+
|
40
|
+
class TestFailure < ActionDispatch::IntegrationTest
|
41
|
+
include Capybara::DSL
|
42
|
+
|
43
|
+
def test_failure
|
44
|
+
visit '/'
|
45
|
+
assert(page.body.include?('This is the root page'))
|
46
|
+
click_on "you'll never find me"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
RUBY
|
50
|
+
check_file_content('tmp/my_screenshot.html', 'This is the root page', true)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "does not save a screenshot for tests that don't inherit from ActionDispatch::IntegrationTest" do
|
54
|
+
run_failing_case <<-RUBY
|
55
|
+
class TestFailure < MiniTest::Unit::TestCase
|
56
|
+
include Capybara::DSL
|
57
|
+
|
58
|
+
def test_failure
|
59
|
+
visit '/'
|
60
|
+
assert(page.body.include?('This is the root page'))
|
61
|
+
click_on "you'll never find me"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
RUBY
|
65
|
+
check_file_presence(%w{tmp/my_screenshot.html}, false)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "saves a screenshot for the correct session for failures using_session" do
|
69
|
+
run_failing_case <<-RUBY
|
70
|
+
module ActionDispatch
|
71
|
+
class IntegrationTest < Minitest::Unit::TestCase; end
|
72
|
+
end
|
73
|
+
|
74
|
+
class TestFailure < ActionDispatch::IntegrationTest
|
75
|
+
include Capybara::DSL
|
76
|
+
|
77
|
+
def test_failure
|
78
|
+
visit '/'
|
79
|
+
assert(page.body.include?('This is the root page'))
|
80
|
+
using_session :different_session do
|
81
|
+
visit '/different_page'
|
82
|
+
assert(page.body.include?('This is a different page'))
|
83
|
+
click_on "you'll never find me"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
RUBY
|
88
|
+
check_file_content('tmp/my_screenshot.html', 'This is a different page', true)
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Using Capybara::Screenshot with Test::Unit" do
|
4
|
+
include Aruba::Api
|
5
|
+
include CommonSetup
|
6
|
+
|
7
|
+
before do
|
8
|
+
clean_current_dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def run_failing_case(code, integration_path = '.')
|
12
|
+
write_file("#{integration_path}/test_failure.rb", <<-RUBY)
|
13
|
+
#{ensure_load_paths_valid}
|
14
|
+
require 'test/unit'
|
15
|
+
require 'capybara'
|
16
|
+
require 'capybara/rspec'
|
17
|
+
require 'capybara-screenshot'
|
18
|
+
require 'capybara-screenshot/testunit'
|
19
|
+
|
20
|
+
#{setup_test_app}
|
21
|
+
Capybara::Screenshot.register_filename_prefix_formatter(:testunit) do | fault |
|
22
|
+
raise "expected fault" unless fault.exception.message.include? %q{Unable to find link or button "you'll never find me"}
|
23
|
+
'my_screenshot'
|
24
|
+
end
|
25
|
+
|
26
|
+
class TestFailure < Test::Unit::TestCase
|
27
|
+
include Capybara::DSL
|
28
|
+
|
29
|
+
def test_failure
|
30
|
+
#{code}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
RUBY
|
34
|
+
|
35
|
+
cmd = "bundle exec ruby #{integration_path}/test_failure.rb"
|
36
|
+
run_simple cmd, false
|
37
|
+
expect(output_from(cmd)).to include %q{Unable to find link or button "you'll never find me"}
|
38
|
+
end
|
39
|
+
|
40
|
+
it "saves a screenshot on failure for any test in path 'test/integration'" do
|
41
|
+
run_failing_case(<<-RUBY, 'test/integration')
|
42
|
+
visit '/'
|
43
|
+
assert(page.body.include?('This is the root page'))
|
44
|
+
click_on "you'll never find me"
|
45
|
+
RUBY
|
46
|
+
check_file_content('tmp/my_screenshot.html', 'This is the root page', true)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "does not generate a screenshot for tests that are not in 'test/integration'" do
|
50
|
+
run_failing_case(<<-RUBY, 'test/something-else')
|
51
|
+
visit '/'
|
52
|
+
assert(page.body.include?('This is the root page'))
|
53
|
+
click_on "you'll never find me"
|
54
|
+
RUBY
|
55
|
+
|
56
|
+
check_file_presence(%w{tmp/my_screenshot.html}, false)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "saves a screenshot for the correct session for failures using_session" do
|
60
|
+
run_failing_case(<<-RUBY, 'test/integration')
|
61
|
+
visit '/'
|
62
|
+
assert(page.body.include?('This is the root page'))
|
63
|
+
using_session :different_session do
|
64
|
+
visit '/different_page'
|
65
|
+
assert(page.body.include?('This is a different page'))
|
66
|
+
click_on "you'll never find me"
|
67
|
+
end
|
68
|
+
RUBY
|
69
|
+
check_file_content('tmp/my_screenshot.html', 'This is a different page', true)
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Capybara::Screenshot::RSpec do
|
4
|
+
describe "used with RSpec" do
|
5
|
+
include Aruba::Api
|
6
|
+
include CommonSetup
|
7
|
+
|
8
|
+
before do
|
9
|
+
clean_current_dir
|
10
|
+
end
|
11
|
+
|
12
|
+
def run_failing_case(code, error_message, format=nil)
|
13
|
+
write_file('spec/test_failure.rb', <<-RUBY)
|
14
|
+
#{ensure_load_paths_valid}
|
15
|
+
require 'rspec'
|
16
|
+
require 'capybara'
|
17
|
+
require 'capybara/rspec'
|
18
|
+
require 'capybara-screenshot'
|
19
|
+
require 'capybara-screenshot/rspec'
|
20
|
+
|
21
|
+
#{setup_test_app}
|
22
|
+
#{code}
|
23
|
+
RUBY
|
24
|
+
|
25
|
+
cmd = "bundle exec rspec #{"--format #{format} " if format}spec/test_failure.rb"
|
26
|
+
run_simple cmd, false
|
27
|
+
|
28
|
+
if error_message.kind_of?(Regexp)
|
29
|
+
expect(output_from(cmd)).to match(error_message)
|
30
|
+
else
|
31
|
+
expect(output_from(cmd)).to include(error_message)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "saves a screenshot on failure" do
|
36
|
+
run_failing_case <<-RUBY, %q{Unable to find link or button "you'll never find me"}
|
37
|
+
feature 'screenshot with failure' do
|
38
|
+
scenario 'click on a missing link' do
|
39
|
+
visit '/'
|
40
|
+
expect(page.body).to include('This is the root page')
|
41
|
+
click_on "you'll never find me"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
RUBY
|
45
|
+
check_file_content('tmp/screenshot.html', 'This is the root page', true)
|
46
|
+
end
|
47
|
+
|
48
|
+
formatters = {
|
49
|
+
progress: "HTML screenshot:",
|
50
|
+
documentation: "HTML screenshot:",
|
51
|
+
html: %r{<a href="file://\./tmp/screenshot\.html"[^>]*>HTML page</a>}
|
52
|
+
}
|
53
|
+
|
54
|
+
# Textmate formatter is only included in RSpec 2
|
55
|
+
if RSpec::Version::STRING.to_i == 2
|
56
|
+
formatters[:textmate] = %r{TextMate\.system\(.*open file://\./tmp/screenshot.html}
|
57
|
+
end
|
58
|
+
|
59
|
+
formatters.each do |formatter, error_message|
|
60
|
+
it "uses the associated #{formatter} formatter" do
|
61
|
+
run_failing_case <<-RUBY, error_message, formatter
|
62
|
+
feature 'screenshot with failure' do
|
63
|
+
scenario 'click on a missing link' do
|
64
|
+
visit '/'
|
65
|
+
click_on "you'll never find me"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
RUBY
|
69
|
+
check_file_content('tmp/screenshot.html', 'This is the root page', true)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it "does not save a screenshot for tests that don't use Capybara" do
|
74
|
+
run_failing_case <<-RUBY, %q{expected: false}
|
75
|
+
describe 'failing test' do
|
76
|
+
it 'fails intentionally' do
|
77
|
+
expect(true).to eql(false)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
RUBY
|
81
|
+
check_file_presence(%w{tmp/screenshot.html}, false)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "saves a screenshot for the correct session for failures using_session" do
|
85
|
+
run_failing_case <<-RUBY, %q{Unable to find link or button "you'll never find me"}
|
86
|
+
feature 'screenshot with failure' do
|
87
|
+
scenario 'click on a missing link' do
|
88
|
+
visit '/'
|
89
|
+
expect(page.body).to include('This is the root page')
|
90
|
+
using_session :different_session do
|
91
|
+
visit '/different_page'
|
92
|
+
expect(page.body).to include('This is a different page')
|
93
|
+
click_on "you'll never find me"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
RUBY
|
98
|
+
check_file_content('tmp/screenshot.html', 'This is a different page', true)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -6,16 +6,24 @@
|
|
6
6
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
7
|
|
8
8
|
$: << '../lib'
|
9
|
+
require 'rspec'
|
9
10
|
require 'capybara-screenshot'
|
10
11
|
require 'capybara-screenshot/rspec'
|
11
12
|
require 'timecop'
|
13
|
+
require 'aruba/api'
|
14
|
+
require 'aruba/jruby'
|
12
15
|
|
13
16
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
14
17
|
|
15
18
|
RSpec.configure do |config|
|
16
|
-
|
19
|
+
if RSpec::Version::STRING.to_i == 2
|
20
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
21
|
+
end
|
17
22
|
config.run_all_when_everything_filtered = true
|
18
23
|
config.filter_run :focus
|
24
|
+
config.before do
|
25
|
+
@aruba_timeout_seconds = 60
|
26
|
+
end if RUBY_PLATFORM == 'java'
|
19
27
|
end
|
20
28
|
|
21
29
|
Capybara.app = lambda { |env| [200, {}, ["OK"]] }
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Using Capybara::Screenshot with Spinach" do
|
4
|
+
include Aruba::Api
|
5
|
+
include CommonSetup
|
6
|
+
|
7
|
+
before do
|
8
|
+
clean_current_dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def run_failing_case(failure_message, code)
|
12
|
+
write_file('steps/failure.rb', <<-RUBY)
|
13
|
+
#{ensure_load_paths_valid}
|
14
|
+
require 'spinach/support/spinach_failure.rb'
|
15
|
+
#{setup_test_app}
|
16
|
+
RUBY
|
17
|
+
|
18
|
+
write_file('spinach.feature', code)
|
19
|
+
cmd = 'bundle exec spinach -f .'
|
20
|
+
run_simple cmd, false
|
21
|
+
expect(output_from(cmd)).to match failure_message
|
22
|
+
end
|
23
|
+
|
24
|
+
it "saves a screenshot on failure" do
|
25
|
+
run_failing_case(%q{Unable to find link or button "you'll never find me"}, <<-GHERKIN)
|
26
|
+
Feature: Failure
|
27
|
+
Scenario: Failure
|
28
|
+
Given I visit "/"
|
29
|
+
And I click on a missing link
|
30
|
+
GHERKIN
|
31
|
+
check_file_content('tmp/my_screenshot.html', 'This is the root page', true)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "saves a screenshot on an error" do
|
35
|
+
run_failing_case(%q{you can't handle me}, <<-GHERKIN)
|
36
|
+
Feature: Failure
|
37
|
+
Scenario: Failure
|
38
|
+
Given I visit "/"
|
39
|
+
And I trigger an unhandled exception
|
40
|
+
GHERKIN
|
41
|
+
check_file_content('tmp/my_screenshot.html', 'This is the root page', true)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "saves a screenshot for the correct session for failures using_session" do
|
45
|
+
run_failing_case(%q{Unable to find link or button "you'll never find me"}, <<-GHERKIN)
|
46
|
+
Feature: Failure
|
47
|
+
Scenario: Failure in different session
|
48
|
+
Given I visit "/"
|
49
|
+
And I click on a missing link on a different page in a different session
|
50
|
+
GHERKIN
|
51
|
+
check_file_content('tmp/my_screenshot.html', 'This is a different page', true)
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'capybara'
|
2
|
+
require 'capybara/rspec'
|
3
|
+
require 'capybara-screenshot'
|
4
|
+
require 'capybara-screenshot/spinach'
|
5
|
+
require 'spinach/capybara'
|
6
|
+
require 'support/test_app'
|
7
|
+
|
8
|
+
Spinach.config.failure_exceptions = [Capybara::ElementNotFound]
|
9
|
+
|
10
|
+
class Spinach::Features::Failure < Spinach::FeatureSteps
|
11
|
+
include ::Capybara::DSL
|
12
|
+
|
13
|
+
before do
|
14
|
+
::Capybara::Screenshot.register_filename_prefix_formatter(:spinach) do |failed_step|
|
15
|
+
raise 'expected failing step' if !@expected_failed_step.nil? && !failed_step.name.include?(@expected_failed_step)
|
16
|
+
'my_screenshot'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
step 'I visit "/"' do
|
21
|
+
visit '/'
|
22
|
+
end
|
23
|
+
|
24
|
+
step 'I click on a missing link' do
|
25
|
+
@expected_failed_step = 'I click on a missing link'
|
26
|
+
click_on "you'll never find me"
|
27
|
+
end
|
28
|
+
|
29
|
+
step 'I trigger an unhandled exception' do
|
30
|
+
@expected_failed_step = "I trigger an unhandled exception"
|
31
|
+
raise "you can't handle me"
|
32
|
+
end
|
33
|
+
|
34
|
+
step 'I click on a missing link on a different page in a different session' do
|
35
|
+
using_session :different_session do
|
36
|
+
visit '/different_page'
|
37
|
+
@expected_failed_step = 'I click on a missing link on a different page in a different session'
|
38
|
+
click_on "you'll never find me"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module CommonSetup
|
2
|
+
def self.included(target)
|
3
|
+
target.let(:gem_root) { File.expand_path('../..', File.dirname(__FILE__)) }
|
4
|
+
|
5
|
+
target.let(:ensure_load_paths_valid) do
|
6
|
+
<<-RUBY
|
7
|
+
%w(lib spec).each do |include_folder|
|
8
|
+
$LOAD_PATH.unshift(File.join('#{gem_root}', include_folder))
|
9
|
+
end
|
10
|
+
RUBY
|
11
|
+
end
|
12
|
+
|
13
|
+
target.let(:setup_test_app) do
|
14
|
+
<<-RUBY
|
15
|
+
require 'support/test_app'
|
16
|
+
Capybara.save_and_open_page_path = 'tmp'
|
17
|
+
Capybara.app = TestApp
|
18
|
+
Capybara::Screenshot.append_timestamp = false
|
19
|
+
RUBY
|
20
|
+
end
|
21
|
+
|
22
|
+
target.before do
|
23
|
+
if ENV['BUNDLE_GEMFILE'] && ENV['BUNDLE_GEMFILE'].match(/^\./)
|
24
|
+
ENV['BUNDLE_GEMFILE'] = File.expand_path(ENV['BUNDLE_GEMFILE'].gsub(/^\./, gem_root))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|