cucumber-rails 1.7.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +197 -19
  3. data/CONTRIBUTING.md +16 -14
  4. data/LICENSE +1 -1
  5. data/README.md +27 -21
  6. data/lib/cucumber/rails.rb +23 -17
  7. data/lib/cucumber/rails/action_dispatch.rb +21 -0
  8. data/lib/cucumber/rails/application.rb +16 -7
  9. data/lib/cucumber/rails/capybara.rb +2 -0
  10. data/lib/cucumber/rails/capybara/javascript_emulation.rb +42 -21
  11. data/lib/cucumber/rails/capybara/select_dates_and_times.rb +3 -1
  12. data/lib/cucumber/rails/database.rb +24 -56
  13. data/lib/cucumber/rails/database/deletion_strategy.rb +13 -0
  14. data/lib/cucumber/rails/database/shared_connection_strategy.rb +27 -0
  15. data/lib/cucumber/rails/database/strategy.rb +33 -0
  16. data/lib/cucumber/rails/database/truncation_strategy.rb +13 -0
  17. data/lib/cucumber/rails/hooks.rb +2 -0
  18. data/lib/cucumber/rails/hooks/active_record.rb +8 -4
  19. data/lib/cucumber/rails/hooks/allow_rescue.rb +2 -0
  20. data/lib/cucumber/rails/hooks/database_cleaner.rb +11 -3
  21. data/lib/cucumber/rails/hooks/mail.rb +3 -1
  22. data/lib/cucumber/rails/rspec.rb +3 -1
  23. data/lib/cucumber/rails/world.rb +24 -7
  24. data/lib/generators/cucumber/{install/USAGE → USAGE} +0 -0
  25. data/lib/generators/cucumber/{install/install_generator.rb → install_generator.rb} +18 -9
  26. data/lib/generators/cucumber/{install/templates → templates}/config/cucumber.yml.erb +0 -0
  27. data/lib/generators/cucumber/{install/templates → templates}/script/cucumber +1 -0
  28. data/lib/generators/cucumber/{install/templates → templates}/support/_rails_each_run.rb.erb +0 -0
  29. data/lib/generators/cucumber/{install/templates → templates}/support/_rails_prefork.rb.erb +0 -0
  30. data/lib/generators/cucumber/{install/templates → templates}/support/capybara.rb +2 -0
  31. data/lib/generators/cucumber/{install/templates → templates}/support/edit_warning.txt +0 -0
  32. data/lib/generators/cucumber/{install/templates → templates}/support/rails.rb.erb +0 -0
  33. data/lib/generators/cucumber/{install/templates → templates}/support/rails_spork.rb.erb +0 -0
  34. data/lib/generators/cucumber/{install/templates → templates}/tasks/cucumber.rake.erb +0 -0
  35. metadata +100 -169
  36. data/.github/ISSUE_TEMPLATE.md +0 -52
  37. data/.github/PULL_REQUEST_TEMPLATE.md +0 -42
  38. data/.gitignore +0 -13
  39. data/.rspec +0 -1
  40. data/.rubocop.yml +0 -1
  41. data/.rubocop_todo.yml +0 -443
  42. data/.travis.yml +0 -45
  43. data/Appraisals +0 -32
  44. data/Gemfile +0 -7
  45. data/Rakefile +0 -49
  46. data/bin/install_geckodriver.sh +0 -19
  47. data/bin/install_webpacker.sh +0 -9
  48. data/config/.gitignore +0 -1
  49. data/config/cucumber.yml +0 -17
  50. data/cucumber-rails.gemspec +0 -44
  51. data/dev_tasks/cucumber.rake +0 -3
  52. data/dev_tasks/rspec.rake +0 -3
  53. data/dev_tasks/yard.rake +0 -35
  54. data/dev_tasks/yard/default/layout/html/bubble_32x32.png +0 -0
  55. data/dev_tasks/yard/default/layout/html/footer.erb +0 -5
  56. data/dev_tasks/yard/default/layout/html/index.erb +0 -1
  57. data/dev_tasks/yard/default/layout/html/layout.erb +0 -25
  58. data/dev_tasks/yard/default/layout/html/logo.erb +0 -1
  59. data/dev_tasks/yard/default/layout/html/setup.rb +0 -4
  60. data/features/allow_rescue.feature +0 -65
  61. data/features/annotations.feature +0 -22
  62. data/features/capybara_javascript_drivers.feature +0 -73
  63. data/features/choose_javascript_database_strategy.feature +0 -145
  64. data/features/database_cleaner.feature +0 -44
  65. data/features/disable_automatic_database_cleaning.feature +0 -55
  66. data/features/emulate_javascript.feature +0 -94
  67. data/features/install_cucumber_rails.feature +0 -14
  68. data/features/no_database.feature +0 -61
  69. data/features/raising_errors.feature +0 -16
  70. data/features/rerun_profile.feature +0 -46
  71. data/features/rest_api.feature +0 -47
  72. data/features/step_definitions/cucumber_rails_steps.rb +0 -153
  73. data/features/support/aruba.rb +0 -3
  74. data/features/support/bundler_pre_support.rb +0 -28
  75. data/features/support/env.rb +0 -40
  76. data/features/support/fixtures/bundler-1.0.21.gem +0 -0
  77. data/features/support/fixtures/bundler-1.1.rc.gem +0 -0
  78. data/features/support/legacy_web_steps_support.rb +0 -289
  79. data/gemfiles/rails_4_2.gemfile +0 -10
  80. data/gemfiles/rails_5_0.gemfile +0 -10
  81. data/gemfiles/rails_5_1.gemfile +0 -10
  82. data/gemfiles/rails_5_2.gemfile +0 -10
  83. data/gemfiles/rails_6_0.gemfile +0 -8
  84. data/lib/cucumber/rails/action_controller.rb +0 -13
  85. data/spec/cucumber/rails/database_spec.rb +0 -57
  86. data/spec/generators/cucumber/install/install_generator_spec.rb +0 -47
  87. data/spec/spec_helper.rb +0 -14
data/.travis.yml DELETED
@@ -1,45 +0,0 @@
1
- rvm:
2
- - 2.2
3
- - 2.3
4
- - 2.4
5
- - 2.5
6
- - 2.6
7
- - ruby-head
8
-
9
- addons:
10
- firefox: latest
11
-
12
- # whitelist
13
- branches:
14
- only:
15
- - master
16
-
17
- gemfile:
18
- - gemfiles/rails_6_0.gemfile
19
- - gemfiles/rails_5_2.gemfile
20
- - gemfiles/rails_5_1.gemfile
21
- - gemfiles/rails_5_0.gemfile
22
- - gemfiles/rails_4_2.gemfile
23
-
24
- matrix:
25
- fast_finish: true
26
- allow_failures:
27
- - rvm: ruby-head
28
- exclude:
29
- - rvm: 2.2
30
- gemfile: gemfiles/rails_6_0.gemfile
31
- - rvm: 2.3
32
- gemfile: gemfiles/rails_6_0.gemfile
33
- - rvm: 2.4
34
- gemfile: gemfiles/rails_6_0.gemfile
35
- - rvm: 2.4
36
- gemfile: gemfiles/rails_4_2.gemfile
37
- - rvm: 2.5
38
- gemfile: gemfiles/rails_4_2.gemfile
39
-
40
- before_install:
41
- - gem install --remote bundler -v '~> 1.17'
42
- - bin/install_geckodriver.sh
43
- - bin/install_webpacker.sh
44
-
45
- before_script: geckodriver -V
data/Appraisals DELETED
@@ -1,32 +0,0 @@
1
- appraise "rails_4_2" do
2
- gem "railties", "~> 4.2.1"
3
- gem "i18n", "< 1.5.1", platform: :mri_22
4
- gem "capybara", "< 3.16.0", platform: [:mri_23, :mri_22]
5
- gem "nokogiri", "< 1.9.1", platform: [:mri_23, :mri_22]
6
- end
7
-
8
- appraise "rails_5_0" do
9
- gem "railties", "~> 5.0.0"
10
- gem "i18n", "< 1.5.1", platform: :mri_22
11
- gem "capybara", "< 3.16.0", platform: [:mri_23, :mri_22]
12
- gem "nokogiri", "< 1.9.1", platform: [:mri_23, :mri_22]
13
- end
14
-
15
- appraise "rails_5_1" do
16
- gem "railties", "~> 5.1.0"
17
- gem "i18n", "< 1.5.1", platform: :mri_22
18
- gem "capybara", "< 3.16.0", platform: [:mri_23, :mri_22]
19
- gem "nokogiri", "< 1.9.1", platform: [:mri_23, :mri_22]
20
- end
21
-
22
- appraise "rails_5_2" do
23
- gem "railties", "~> 5.2"
24
- gem "i18n", "< 1.5.1", platform: :mri_22
25
- gem "capybara", "< 3.16.0", platform: [:mri_23, :mri_22]
26
- gem "nokogiri", "< 1.9.1", platform: [:mri_23, :mri_22]
27
- end
28
-
29
- appraise "rails_6_0" do
30
- gem "railties", "~> 6.0.0.beta1"
31
- gem "rails", "~> 6.0.0.beta1"
32
- end
data/Gemfile DELETED
@@ -1,7 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Test dependencies for Rails in the default test task, as test-generated
4
- # apps do not run bundler for speed reasons. Kept here so they can be
5
- # excluded from Appraisal-generated gemfiles.
6
-
7
- gemspec
data/Rakefile DELETED
@@ -1,49 +0,0 @@
1
- # encoding: utf-8
2
- CUCUMBER_RAILS_VERSION = Gem::Specification.load(File.dirname(__FILE__) + '/cucumber-rails.gemspec').version.version
3
- require 'rubygems'
4
- require 'bundler'
5
- require 'bundler/setup'
6
- require 'appraisal'
7
- require 'rdoc' # https://github.com/lsegal/yard/commit/b861dcc2d7f7e1fbbed7b552ac2e4f7caf68bafa
8
- require 'rake/clean'
9
- require 'pathname'
10
- Bundler::GemHelper.install_tasks
11
-
12
- $:.unshift(File.dirname(__FILE__) + '/lib')
13
- Dir["#{File.dirname(__FILE__)}/dev_tasks/*.rake"].sort.each { |ext| load ext }
14
-
15
- CLEAN.include('doc', 'tmp')
16
-
17
- task default: :test
18
-
19
- task test: [:spec, :cucumber]
20
-
21
- namespace :test do
22
- desc "Run tests against all gemfiles"
23
- task :all do
24
- Rake::Task['appraisal'].invoke('test')
25
- end
26
-
27
- desc "Run tests against specified gemfile, e.g. rake test:gemfile[rails_3_0]"
28
- task :gemfile, :name do |task, args|
29
- unless args.name && Pathname.new("gemfiles/#{args.name}.gemfile").exist?
30
- raise ArgumentError "You must provide the name of an existing Appraisal gemfile, e.g. 'rake test:gemfile[rails_3_2]'"
31
- end
32
- Rake::Task["appraisal:#{args.name}"].invoke('test')
33
- end
34
- end
35
-
36
- namespace :gemfiles do
37
- desc "Install dependencies for all gemfiles"
38
- task :install do
39
- ENV['BUNDLE_GEMFILE'] = "Gemfile.appraisal"
40
- Rake::Task['appraisal:install'].invoke
41
- end
42
-
43
- task :clean do
44
- FileUtils.rm_rf("gemfiles/*")
45
- end
46
-
47
- desc "Rebuild generated gemfiles and install dependencies"
48
- task rebuild: [:clean, :install]
49
- end
@@ -1,19 +0,0 @@
1
- #!/bin/bash
2
-
3
- set -xeuo pipefail
4
-
5
- curl --silent \
6
- --show-error \
7
- --location \
8
- --fail \
9
- --retry 3 \
10
- --output /tmp/geckodriver_linux64.tar.gz \
11
- https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-linux64.tar.gz
12
-
13
- sudo tar -C /usr/local/bin -xvzf /tmp/geckodriver_linux64.tar.gz geckodriver
14
-
15
- rm /tmp/geckodriver_linux64.tar.gz
16
-
17
- geckodriver --version
18
-
19
- set +x
@@ -1,9 +0,0 @@
1
- #!/bin/bash
2
-
3
- set -xeuo pipefail
4
-
5
- curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
6
- echo 'deb https://dl.yarnpkg.com/debian/ stable main' | sudo tee /etc/apt/sources.list.d/yarn.list
7
- sudo apt update && sudo apt install --yes yarn
8
-
9
- set +x
data/config/.gitignore DELETED
@@ -1 +0,0 @@
1
- aruba-rvm.yml
data/config/cucumber.yml DELETED
@@ -1,17 +0,0 @@
1
- <%
2
- rerun_file = '.cucumber.rerun'
3
- failing_scenarios = IO.read(rerun_file) rescue ''
4
- path = if failing_scenarios.empty?
5
- 'features'
6
- else
7
- puts "Running failed scenarios"
8
- failing_scenarios.gsub /\s/, ' '
9
- end
10
- opts = [
11
- "--format #{path == 'features' ? 'progress' : 'pretty'}",
12
- "--format rerun -o #{rerun_file}"
13
- ]
14
- rails_version = `bundle exec rails --version`.match(/[\d.]+$/).to_s
15
- optlist = opts.join(' ')
16
- %>
17
- default: <%= path %> <%= optlist %> --tags 'not @broken'
@@ -1,44 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
3
-
4
- Gem::Specification.new do |s|
5
- s.name = 'cucumber-rails'
6
- s.version = '1.7.0'
7
- s.authors = ["Aslak Hellesøy", "Dennis Blöte", "Rob Holland"]
8
- s.description = "Cucumber Generator and Runtime for Rails"
9
- s.summary = "#{s.name}-#{s.version}"
10
- s.email = 'cukes@googlegroups.com'
11
- s.homepage = "http://cukes.info"
12
-
13
- s.license = 'MIT'
14
-
15
- s.required_ruby_version = '>= 2.2'
16
-
17
- s.add_runtime_dependency('capybara', ['>= 2.3.0', '< 4'])
18
- s.add_runtime_dependency('cucumber', ['>= 3.0.2', '< 4'])
19
- s.add_runtime_dependency('nokogiri', '~> 1.8')
20
- s.add_runtime_dependency('railties', ['>= 4.2', '< 7'])
21
- s.add_runtime_dependency('mime-types', ['>= 1.17', '< 4'])
22
-
23
- # Main development dependencies
24
- s.add_development_dependency('ammeter', ['>= 1.0.0', '!= 1.1.3'])
25
- s.add_development_dependency('appraisal', '>= 0.5.1')
26
- s.add_development_dependency('aruba', '~> 0.14.2')
27
- s.add_development_dependency('bundler', '>= 1.3.5')
28
- s.add_development_dependency('rubocop', '~> 0.66.0')
29
- s.add_development_dependency('rake', '>= 10.3')
30
- s.add_development_dependency('rspec', '~> 3.5')
31
- s.add_development_dependency('rails', ['>= 4.2', '< 7'])
32
- s.add_development_dependency('sqlite3', '~> 1.3.13')
33
-
34
- # For Documentation:
35
- s.add_development_dependency('rdiscount', '>= 2.0.7')
36
- s.add_development_dependency('rdoc', '>= 3.4')
37
- s.add_development_dependency('yard', '>= 0.8.7')
38
-
39
- s.required_ruby_version = '>= 2.2.0'
40
- s.rubygems_version = ">= 1.6.1"
41
- s.files = `git ls-files`.split("\n")
42
- s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
43
- s.require_path = "lib"
44
- end
@@ -1,3 +0,0 @@
1
- require 'cucumber/rake/task'
2
-
3
- Cucumber::Rake::Task.new
data/dev_tasks/rspec.rake DELETED
@@ -1,3 +0,0 @@
1
- require 'rspec/core/rake_task'
2
-
3
- RSpec::Core::RakeTask.new
data/dev_tasks/yard.rake DELETED
@@ -1,35 +0,0 @@
1
- require 'yard'
2
- require 'yard/rake/yardoc_task'
3
-
4
- SITE_DIR = File.expand_path(File.dirname(__FILE__) + '/../cucumber.github.com')
5
- API_DIR = File.join(SITE_DIR, 'api', 'cucumber-rails', 'ruby', 'yardoc')
6
-
7
- namespace :api do
8
- file :dir do
9
- unless File.directory?(SITE_DIR)
10
- raise "You need to git clone git@github.com:cucumber/cucumber.github.com.git #{SITE_DIR}"
11
- end
12
- sh('git pull origin master')
13
- mkdir_p API_DIR
14
- end
15
-
16
- template_path = File.expand_path(File.join(File.dirname(__FILE__), 'yard'))
17
- YARD::Templates::Engine.register_template_path(template_path)
18
- YARD::Rake::YardocTask.new(:yard) do |yard|
19
- dir = API_DIR
20
- mkdir_p dir
21
- yard.options = ["--out", dir]
22
- end
23
- task yard: :dir
24
-
25
- task :release do
26
- Dir.chdir(SITE_DIR) do
27
- sh('git add .')
28
- sh("git commit -m 'Update API docs for cucumber-rails v#{cucumber-rails::VERSION}'")
29
- sh('git push origin master')
30
- end
31
- end
32
-
33
- desc "Generate YARD docs for Cucumber-Rails' API"
34
- task doc: [:yard, :release]
35
- end
@@ -1,5 +0,0 @@
1
- <div id="footer">
2
- Generated on <%= Time.now.strftime("%c") %> by
3
- <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
4
- <%= YARD::VERSION %> (ruby-<%= RUBY_VERSION %>).
5
- </div>
@@ -1 +0,0 @@
1
- <%= yieldall %>
@@ -1,25 +0,0 @@
1
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
- <head>
5
- <%= erb(:headers) %>
6
- </head>
7
- <body>
8
- <script type="text/javascript" charset="utf-8">
9
- if (window.top.frames.main) document.body.className = 'frames';
10
- </script>
11
-
12
- <div id="header">
13
- <%= erb(:logo) %>
14
- <%= erb(:breadcrumb) %>
15
- <%= erb(:search) %>
16
- <div class="clear"></div>
17
- </div>
18
-
19
- <iframe id="search_frame"></iframe>
20
-
21
- <div id="content"><%= yieldall %></div>
22
-
23
- <%= erb(:footer) %>
24
- </body>
25
- </html>
@@ -1 +0,0 @@
1
- <h3><img src="<%= url_for('images/bubble_32x32.png') %>"></img> Cucumber-Rails <%= CUCUMBER_RAILS_VERSION %></h3>
@@ -1,4 +0,0 @@
1
- def init
2
- super
3
- options[:serializer].serialize('/images/bubble_32x32.png', IO.read(File.dirname(__FILE__) + '/bubble_32x32.png'))
4
- end
@@ -1,65 +0,0 @@
1
- Feature: Allow Cucumber to rescue exceptions
2
-
3
- Background: A controller that raises an exception
4
- Given I have created a new Rails app "rails-app" and installed cucumber-rails
5
- And I write to "app/controllers/posts_controller.rb" with:
6
- """
7
- class PostsController < ApplicationController
8
- def index
9
- raise "There is an error in index"
10
- end
11
- end
12
- """
13
- And I write to "config/routes.rb" with:
14
- """
15
- RailsApp::Application.routes.draw do
16
- resources :posts
17
- end
18
- """
19
-
20
- Scenario: Allow rescue
21
- Given I write to "features/posts.feature" with:
22
- """
23
- Feature: posts
24
- @allow-rescue
25
- Scenario: See posts
26
- When I look at the posts
27
- Then I should see the public error page
28
- """
29
- And I write to "features/step_definitions/posts_steps.rb" with:
30
- """
31
- When /^I look at the posts$/ do
32
- visit '/posts'
33
- end
34
- Then /^I should see the public error page$/ do
35
- expect(page).to have_content "We're sorry, but something went wrong."
36
- end
37
- """
38
- And I run `bundle exec rake db:migrate`
39
- And I run `bundle exec cucumber`
40
- Then the feature run should pass with:
41
- """
42
- 1 scenario (1 passed)
43
- 2 steps (2 passed)
44
- """
45
-
46
- Scenario: Don't allow rescue
47
- Given I write to "features/posts.feature" with:
48
- """
49
- Feature: posts
50
- Scenario: See them
51
- When I look at the posts
52
- """
53
- And I write to "features/step_definitions/posts_steps.rb" with:
54
- """
55
- When /^I look at the posts$/ do
56
- visit '/posts'
57
- end
58
- """
59
- And I run `bundle exec rake db:migrate`
60
- And I run `bundle exec cucumber`
61
- Then it should fail with:
62
- """
63
- 1 scenario (1 failed)
64
- 1 step (1 failed)
65
- """
@@ -1,22 +0,0 @@
1
- Feature: Annotations
2
-
3
- I order to track my development progress
4
- As a developer
5
- I should be able to list annotations in my features
6
-
7
- Background:
8
- Given I have created a new Rails app "test-app" with no database and installed cucumber-rails
9
- And I write to "features/tests.feature" with:
10
- """
11
- Feature: Tests
12
- Scenario: Tests
13
- # TODO: When I go to the products page
14
- """
15
-
16
- Scenario: See annotations in .feature file
17
- When I run `bundle exec rake notes`
18
- Then it should pass with:
19
- """
20
- features/tests.feature:
21
- * [ 3] [TODO] When I go to the products page
22
- """
@@ -1,73 +0,0 @@
1
- @focus
2
- Feature: Capybara Javascript Drivers
3
-
4
- Background: A simple calendar app
5
- Given I have created a new Rails app and installed cucumber-rails
6
- And I force selenium to run Firefox in headless mode
7
- And I run `bundle exec rails g scaffold appointment name:string when:datetime`
8
- And I write to "features/step_definitions/date_time_steps.rb" with:
9
- """
10
- When /^(?:|I )select "([^"]+)" as the "([^"]+)" time$/ do |time, selector|
11
- select_time(time, from: selector)
12
- end
13
-
14
- When /^(?:|I )select "([^"]+)" as the "([^"]+)" date$/ do |date, selector|
15
- select_date(date, from: selector)
16
- end
17
-
18
- When /^(?:|I )select "([^"]+)" as the "([^"]+)" date and time$/ do |datetime, selector|
19
- select_datetime(datetime, from: selector)
20
- end
21
- """
22
-
23
- Scenario: Use a particular driver
24
- Given I write to "features/create_appointment.feature" with:
25
- """
26
- @javascript
27
- Feature: Create appointments
28
- Scenario: Constitution on May 17
29
- Given I am on the new appointment page
30
- And I fill in "Norway's constitution" for "Name"
31
- And I select "2015-02-20 15:10:00 UTC" as the "When" date and time
32
- And I press "Create Appointment"
33
- Then I should see "Norway's constitution"
34
- And I should see "2015-02-20 15:10:00 UTC"
35
-
36
- """
37
- When I run `bundle exec rake db:migrate`
38
- And I run `bundle exec rake cucumber`
39
- Then the feature run should pass with:
40
- """
41
- 1 scenario (1 passed)
42
- 6 steps (6 passed)
43
- """
44
-
45
- Scenario: Mixed DB access
46
- Given I write to "features/create_appointment.feature" with:
47
- """
48
- @javascript
49
- Feature: Create appointments
50
- Scenario: Constitution on May 17
51
- Given a random appointment
52
- And I am viewing random appointment
53
- Then I should see "Random appointment"
54
-
55
- """
56
- And I write to "features/step_definitions/custom_steps.rb" with:
57
- """
58
- Given /^a random appointment$/ do
59
- @appointment = Appointment.create!(name: 'Random appointment', when: DateTime.now)
60
- end
61
-
62
- Given /^I am viewing random appointment$/ do
63
- visit appointment_path(@appointment)
64
- end
65
- """
66
-
67
- When I run `bundle exec rake db:migrate`
68
- And I run `bundle exec rake cucumber`
69
- Then the feature run should pass with:
70
- """
71
- 1 scenario (1 passed)
72
- 3 steps (3 passed)
73
- """