diff --git a/.codeclimate.yml b/.codeclimate.yml index 90f2ff87a0c..d8b71800205 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -32,11 +32,6 @@ checks: plugins: brakeman: enabled: true - exclude_patterns: - # Excluding User Flows tools since these are not loaded - # except when explicitly called from the User Flow rake tasks - - 'lib/user_flow_exporter.rb' - - 'lib/rspec/formatters/user_flow_formatter.rb' bundler-audit: enabled: true coffeelint: @@ -53,8 +48,6 @@ plugins: - 'node_modules/**/*' - 'db/schema.rb' - 'app/forms/password_form.rb' - - 'lib/user_flow_exporter.rb' - - 'lib/rspec/formatters/user_flow_formatter.rb' fixme: enabled: true exclude_patterns: @@ -81,9 +74,7 @@ exclude_patterns: - 'db/schema.rb' - 'node_modules/' - 'lib/proofer_mocks/' - - 'lib/rspec/formatters/user_flow_formatter.rb' - 'lib/tasks/create_test_accounts.rb' - - 'lib/user_flow_exporter.rb' - 'scripts/load_testing/' - 'tmp/' - 'config/initializers/jwt.rb' diff --git a/.rubocop.yml b/.rubocop.yml index 47654b1455d..bec67867ac5 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -10,9 +10,7 @@ AllCops: - 'bin/**/*' - 'db/migrate/*' - 'db/schema.rb' - - 'lib/rspec/user_flow_formatter.rb' - 'lib/tasks/create_test_accounts.rb' - - 'lib/user_flow_exporter.rb' - 'node_modules/**/*' - 'tmp/**/*' - 'vendor/**/*' diff --git a/README.md b/README.md index 9f28b384c08..c6bd7905ee9 100644 --- a/README.md +++ b/README.md @@ -268,30 +268,6 @@ The Geolite2-City database can be downloaded from MaxMind's site at [https://dev Download the GeoIP2 Binary and save it at `geo_data/GeoLite2-City.mmdb`. The app will start using that Geolite2 file for geolocation after restart. -### User flows - -We have an automated tool for generating user flows using real views generated from the application. These specs are excluded from our typical spec run because of the overhead of generating screenshots for each view. - -The local instance of the application must be running in order to serve up the assets (eg. `make run`). Then, you can specify where the assets are hosted from and generate the views with: - -``` -$ RAILS_ASSET_HOST=localhost:3000 rake spec:user_flows -``` - -Then, visit http://localhost:3000/user_flows in your browser! - -##### Exporting - -The user flows tool also has an export feature which allows you to export everything for the web. You may host these assets with someting like [`simplehttpserver`](https://www.npmjs.com/package/simplehttpserver) or publish to [Federalist](https://federalist.18f.gov/). To publish user flows for Federalist, first make sure the application is running locally (eg. localhost:3000) and run: - -``` -$ RAILS_ASSET_HOST=localhost:3000 FEDERALIST_PATH=/site/user/repository rake spec:user_flows:web -``` - -This will output your site to `public/site/user/repository` for quick publishing to [Federalist](https://federalist-docs.18f.gov/pages/using-federalist/). To test compatibility, run `simplehttpserver` from the app's `public` folder and visit `http://localhost:8000//user_flows` in your browser. - -[laptop script]: https://github.com/18F/laptop - ### Proofing vendors Some proofing vendor code is located in private Github repositories because of NDAs. You can still use it diff --git a/lib/rspec/formatters/user_flow_formatter.rb b/lib/rspec/formatters/user_flow_formatter.rb deleted file mode 100644 index 0c54939a13d..00000000000 --- a/lib/rspec/formatters/user_flow_formatter.rb +++ /dev/null @@ -1,78 +0,0 @@ -require 'rails_helper' - -Capybara.save_path = Rails.root.join('public', 'user_flows') -Capybara::Screenshot.prune_strategy = :keep_last_run - -# Capybara's `save_page` does not return anything. This is a hack to -# capture the path of the screenshot so that it can be embedded in -# the HTML output of this formatter -# rubocop:disable Style/GlobalVars -Capybara::Screenshot.after_save_html do |path| - filename = path.split('/').last - uri = Capybara.save_path.to_s.split('public').last + '/' + filename - $cur_screenshot_link = uri -end - -class UserFlowFormatter < RSpec::Core::Formatters::DocumentationFormatter - # This registers the notifications this formatter supports, and tells - # us that this was written against the RSpec 3.x formatter API. - RSpec::Core::Formatters.register self, :initialize, - :example_passed, - :example_group_finished, - :example_group_started, - :stop - - def initialize(output) - @html = '' \ - '' \ - '

' \ - '' \ - '/ user flows

' \ - '
'
-    @user_flows_html_file = Capybara.save_path.join('index.html').to_s
-    super
-  end
-
-  def example_passed(notification)
-    example = notification.example
-    indent = '       ' * example.metadata[:scoped_id].split(':').size
-    @html << "
#{indent}" \ - "#{example.description} »" - - super - end - - def example_group_started(notification) - indent = super - - @html << '
' - @html << ' ' * indent - @html << notification.group.description - end - - def example_group_finished(notification) - @html << '
' - super - end - - def stop(_notification) - Kernel.puts "Complete!\n" - @html += '
' - File.open(@user_flows_html_file, 'wb') do |file| - file.write(@html) - end - - Kernel.puts 'User flows output to:' - Kernel.puts flows_output_url - end - - private - - def flows_output_url - host = Rails.application.config.action_controller.asset_host || 'localhost:3000' - protocol = host.match?(/localhost/) ? 'http://' : 'https://' - - "#{protocol}#{host}/user_flows" - end -end -# rubocop:enable Style/GlobalVars diff --git a/lib/tasks/user_flows.rake b/lib/tasks/user_flows.rake deleted file mode 100644 index c540d9d79da..00000000000 --- a/lib/tasks/user_flows.rake +++ /dev/null @@ -1,19 +0,0 @@ -unless Rails.env.production? - namespace :spec do - desc 'Executes user flow specs' - RSpec::Core::RakeTask.new('user_flows') do |t| - t.rspec_opts = %w[--tag user_flow - --order defined - --require ./lib/rspec/formatters/user_flow_formatter.rb - --format UserFlowFormatter] - end - - desc 'Exports user flows for the web' - task 'user_flows:web' => :environment do - ENV['RAILS_DISABLE_ASSET_DIGEST'] = 'true' - require './lib/user_flow_exporter' - Rake::Task['spec:user_flows'].invoke - UserFlowExporter.run - end - end -end diff --git a/lib/user_flow_exporter.rb b/lib/user_flow_exporter.rb deleted file mode 100644 index afb702d16c5..00000000000 --- a/lib/user_flow_exporter.rb +++ /dev/null @@ -1,93 +0,0 @@ -# This module is part of the User Flows toolchest -# -# UserFlowExporter.run - scrapes user flows for use on the web -# -# Dependencies: -# - Must be running the application locally eg (localhost:3000) -# - Must have wget installed and available on your PATH -# -# Executing: -# Start the application with: -# $ make run -# Export flows with: -# $ RAILS_ASSET_HOST=localhost:3000 FEDERALIST_PATH=/site/user/repo rake spec:user_flows:web -# Use the files output to public/ in a Github repo connected to Federalist -# $ cp -r ./public/site/user/repo ~/code/login-user-flows -# And commit the changes in the Federalist repo! - -module UserFlowExporter - ASSET_HOST = ENV['RAILS_ASSET_HOST'] || 'localhost:3000' - # Coming soon: signal testing for different devices - # USER_AGENT = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092416 Firefox/3.0.3" - FEDERALIST_PATH = ENV['FEDERALIST_PATH'] || '/flows_export/' - - def self.run - Kernel.puts "Preparing to scrape user flows...\n" - url = "http://#{ASSET_HOST}/user_flows/" - # The web-friendly flows are still output to the public directory - # in order to quickly test the content by visiting your locally - # hosted application (eg. localhost:3000/site/18f/identity-ux/user_flows) - - if FEDERALIST_PATH[0] != '/' - raise 'Federalist path must start with a slash (eg. /site/18f/identity-ux)' - end - - output_dir = "public#{FEDERALIST_PATH}" - - scrape_cmd = [ - 'wget', - '-r', # -r = recursively mirrors site - '-H', # -H = span hosts (e.g. include assets from other domains) - '-p', # -p = download all assets associated with the page - '--no-host-directories', # --no-host-directories = removes domain prefix from output path - '-P', output_dir, # -P = output prefix (a.k.a the directory to dump the assets) - '--domains', 'localhost', # --domains = whitelist of domains to include when following links - url - ] - - system(*scrape_cmd) - - massage_html(output_dir) - massage_assets(output_dir) - - Kernel.puts 'Done! The user flows are now prepared for use on the interwebs!' - end - - private - - def self.massage_html(dir) - Dir.glob("#{dir}/**/*.html") do |html| - File.open(html) do |file| - path = file.path - contents = File.read(path) - contents.gsub!("http://#{ASSET_HOST}/", "#{FEDERALIST_PATH}/") - contents.gsub!('.css?body=1', '.css') - contents.gsub!('.js?body=1', '.js') - contents.gsub!('href="/assets/', "href=\"#{FEDERALIST_PATH}/assets/") - contents.gsub!('src="/assets/', "src=\"#{FEDERALIST_PATH}/assets/") - contents.gsub!("href='/user_flows/", "href='#{FEDERALIST_PATH}/user_flows/") - - contents.gsub!("", "") - - File.open(path, "w") {|file| file.puts contents } - Kernel.puts "Updated #{path} references" - end - end - end - - def self.massage_assets(dir) - Dir.glob("#{dir}/assets/**/**") do |file| - if file[-11..-1] == '.css?body=1' - new_filename = file.gsub('.css?body=1', '.css') - `mv #{file} #{new_filename}` - Kernel.puts "Moved #{file} to #{new_filename}" - end - - if file[-10..-1] == '.js?body=1' - new_filename = file.gsub('.js?body=1', '.js') - `mv #{file} #{new_filename}` - Kernel.puts "Moved #{file} to #{new_filename}" - end - end - end -end diff --git a/spec/features/flows/sp_authentication_flows_spec.rb b/spec/features/flows/sp_authentication_flows_spec.rb deleted file mode 100644 index b3138bcb1cc..00000000000 --- a/spec/features/flows/sp_authentication_flows_spec.rb +++ /dev/null @@ -1,335 +0,0 @@ -require 'rails_helper' - -feature 'SP-initiated authentication with login.gov', :user_flow do - include IdvHelper - include SamlAuthHelper - - I18n.available_locales.each do |locale| - context "with locale=#{locale}" do - context 'with a valid SP' do - context 'when IAL2' do - before do - visit "#{ial2_authnrequest}&locale=#{locale}" - end - - it 'prompts the user to create an account or sign in' do - screenshot_and_save_page - end - - context 'when choosing Create Account' do - before do - click_link t('links.create_account') - end - - it 'prompts for email address' do - screenshot_and_save_page - end - - context 'with a valid email address submitted' do - before do - @email = Faker::Internet.safe_email - fill_in 'user_email', with: @email - click_button t('forms.buttons.submit.default') - @user = User.find_with_email(@email) - end - - it 'informs the user to check email' do - screenshot_and_save_page - end - - context 'with a confirmed email address' do - before do - confirm_last_user - end - - it 'prompts the user for a password' do - screenshot_and_save_page - end - - context 'with a valid password' do - before do - fill_in 'password_form_password', with: Features::SessionHelper::VALID_PASSWORD - click_button t('forms.buttons.continue') - end - - it 'prompts the user to configure 2FA' do - screenshot_and_save_page - end - - context 'with a valid phone number' do - before do - complete_phone_form_with_valid_phone - end - - context 'with SMS delivery' do - before do - choose t('two_factor_authentication.otp_delivery_preference.sms') - click_send_security_code - end - - it 'prompts for OTP' do - screenshot_and_save_page - end - - context 'with valid OTP confirmation' do - before do - fill_in 'code', with: @user.reload.direct_otp - click_button t('forms.buttons.submit.default') - end - - it 'prompts the user to verify oneself' do - screenshot_and_save_page - end - - context 'when choosing Yes, continue' do - before do - select 'Virginia', from: 'jurisdiction_state' - click_idv_continue - end - - it 'prompts for personal information' do - screenshot_and_save_page - end - - context 'with valid personal information entered' do - before do - fill_in 'profile_first_name', with: Faker::Name.first_name - fill_in 'profile_last_name', with: Faker::Name.last_name - fill_in 'profile_address1', with: '123 Main St' - fill_in 'profile_city', with: Faker::Address.city - find('#profile_state'). - find(:xpath, "option[#{(1..50).to_a.sample}]"). - select_option - fill_in 'profile_zipcode', with: Faker::Address.zip_code - fill_in 'profile_dob', with: "09/09/#{(1900..2000).to_a.sample}" - fill_in 'profile_ssn', with: "999-99-#{(1000..9999).to_a.sample}" - click_button t('forms.buttons.continue') - end - - it 'prompts the user to confirm or enter phone number' do - screenshot_and_save_page - end - - context 'when activating by mail' do - before do - click_on t('idv.form.no_alternate_phone_html') - end - - it 'prompts the user to confirm' do - screenshot_and_save_page - end - - context 'when confirming to mail' do - before do - click_on t('idv.buttons.mail.send') - end - - it 'prompts user for password to encrypt profile' do - screenshot_and_save_page - end - - context 'when confirming password' do - before do - fill_in 'user_password', - with: Features::SessionHelper::VALID_PASSWORD - click_button t('forms.buttons.submit.default') - end - - it 'provides a new personal key and prompts to verify' do - screenshot_and_save_page - end - - context 'when clicking Continue' do - before do - click_acknowledge_personal_key - end - - it 'displays the user profile' do - screenshot_and_save_page - end - end - end - - # Disabling this spec because of js: true issue - # Will re-enable this once resolved - # context 'when choosing to cancel' do - # before do - # click_button t('links.cancel') - # end - - # it 'prompts to continue verification or visit profile' do - # screenshot_and_save_page - # end - # end - end - end - end - - context 'with invalid personal information entered' do - before do - fill_out_idv_form_fail - click_button t('forms.buttons.continue') - end - - it 'presents a modal with current retries remaining' do - screenshot_and_save_page - end - end - end - end - end - end - end - end - end - end - end - - context 'when IAL1' do - before do - visit "#{authnrequest_get}&locale=#{locale}" - end - - it 'prompts the user to create an account or sign in' do - screenshot_and_save_page - end - - context 'when choosing Create Account' do - before do - click_link t('links.create_account') - end - - it 'prompts for email address' do - screenshot_and_save_page - end - - context 'with a valid email address submitted' do - before do - @email = Faker::Internet.safe_email - fill_in 'user_email', with: @email - click_button t('forms.buttons.submit.default') - @user = User.find_with_email(@email) - end - - it 'informs the user to check email' do - screenshot_and_save_page - end - - context 'with a confirmed email address' do - before do - confirm_last_user - end - - it 'prompts the user for a password' do - screenshot_and_save_page - end - - context 'with a valid password' do - before do - fill_in 'password_form_password', with: Features::SessionHelper::VALID_PASSWORD - click_button t('forms.buttons.continue') - end - - it 'prompts the user to configure 2FA' do - screenshot_and_save_page - end - - context 'with a valid phone number' do - before do - complete_phone_form_with_valid_phone - end - - context 'with SMS delivery' do - before do - choose t('two_factor_authentication.otp_delivery_preference.sms') - click_send_security_code - end - - it 'prompts for OTP' do - screenshot_and_save_page - end - end - - context 'with Voice delivery' do - before do - choose t('two_factor_authentication.otp_delivery_preference.voice') - click_send_security_code - end - - it 'prompts for OTP' do - screenshot_and_save_page - end - end - end - end - end - end - end - - context 'when choosing to sign in' do - before do - @user = create(:user, :signed_up) - end - - context 'with valid credentials entered' do - before do - fill_in_credentials_and_submit(@user.email, @user.password) - end - - it 'prompts for 2FA delivery method' do - screenshot_and_save_page - end - - context 'with SMS OTP selected (default)' do - it 'prompts for OTP verification' do - screenshot_and_save_page - end - - context 'with valid OTP confirmation' do - before do - fill_in 'code', with: @user.reload.direct_otp - click_button t('forms.buttons.submit.default') - end - - # Skipping since we have nothing to show: this occurs on the SP - xit 'redirects back to SP' do - screenshot_and_save_page - end - end - end - end - - context 'without a valid username and password' do - context 'when choosing "Forgot your password?"' do - before do - click_link t('links.passwords.forgot') - end - - it 'prompts for my email address' do - screenshot_and_save_page - end - - context 'with not_a_real_email_dot.com submitted' do - before do - fill_in 'password_reset_email_form_email', with: 'not_a_real_email_dot.com' - click_button t('forms.buttons.continue') - end - - it 'displays a useful error' do - screenshot_and_save_page - end - end - end - end - end - end - end - end - end - - def complete_phone_form_with_valid_phone - phone = Faker::PhoneNumber.cell_phone - phone = Faker::PhoneNumber.cell_phone until Phonelib.valid_for_country?(phone, 'US') - fill_in 'new_phone_form_phone', with: phone - end -end diff --git a/spec/features/flows/visitor_flows_spec.rb b/spec/features/flows/visitor_flows_spec.rb deleted file mode 100644 index 7bae6f6f0d6..00000000000 --- a/spec/features/flows/visitor_flows_spec.rb +++ /dev/null @@ -1,191 +0,0 @@ -require 'rails_helper' - -feature 'Visitors requesting login.gov directly', devise: true, user_flow: true do - I18n.available_locales.each do |locale| - context "with locale=#{locale}" do - context 'when visiting the homepage' do - before do - visit root_path(locale: locale) - end - - it 'loads the home page' do - screenshot_and_save_page - end - - describe 'showing the password' do - it 'allows me to see my password', js: true do - visit new_user_session_path(locale: locale) - fill_in 'user_password', with: 'my password' - screenshot_and_save_page - end - end - - context 'when attempting to sign in' do - context 'with a valid account' do - before do - @user = create(:user, :signed_up) - fill_in 'user_email', with: @user.email - fill_in 'user_password', with: @user.password - page.find('.btn-primary').click - end - - it 'sends OTP via previously chosen method' do - screenshot_and_save_page - end - - context 'with a valid OTP' do - before do - fill_in 'code', with: @user.reload.direct_otp - click_button t('forms.buttons.submit.default') - end - - it 'redirects to profile' do - screenshot_and_save_page - end - end - - context 'with an invalid OTP submitted' do - before do - fill_in 'code', with: '123abc' - click_button t('forms.buttons.submit.default') - end - - it 'displays a useful error' do - screenshot_and_save_page - end - - context 'with a second invalid OTP submitted' do - before do - fill_in 'code', with: '123abc' - click_button t('forms.buttons.submit.default') - end - - it 'displays a useful error' do - screenshot_and_save_page - end - - context 'with a third invalid OTP submitted' do - before do - fill_in 'code', with: '123abc' - click_button t('forms.buttons.submit.default') - end - - it 'displays a useful error and locks the user account' do - screenshot_and_save_page - end - end - end - end - end - - context 'without valid credentials' do - before do - fill_in 'user_email', with: Faker::Internet.safe_email - fill_in 'user_password', with: 'my password' - page.find('.btn-primary').click - end - - it 'displays a useful error message' do - screenshot_and_save_page - end - end - end - - context 'when choosing create account' do - before do - click_link t('links.create_account') - end - - it 'informs the user about login.gov' do - screenshot_and_save_page - end - - context 'when creating account with valid email' do - before do - sign_up_with(Faker::Internet.safe_email) - end - - it 'notifies user to check email' do - screenshot_and_save_page - end - - context 'when confirming email' do - before do - confirm_last_user - end - - it 'prompts user to set password' do - screenshot_and_save_page - end - end - end - - context 'when attempting with an invalid email' do - before do - sign_up_with('kevin@kevin') - end - - it 'informs the user to try again' do - screenshot_and_save_page - end - end - end - end - - context 'when choosing \'Forgot your password?' do - before do - visit new_user_password_path(locale: locale) - end - - it 'prompts for email address' do - screenshot_and_save_page - end - - context 'when submitting email for an existing account' do - before do - @user = create(:user, :signed_up) - fill_in 'password_reset_email_form_email', with: @user.email - click_button t('forms.buttons.continue') - end - - it 'informs the user to check their email' do - screenshot_and_save_page - end - - context 'when following link in email', email: true do - before do - open_last_email - click_email_link_matching(/reset_password_token/) - end - - it 'prompts the user to enter a new password' do - screenshot_and_save_page - end - - context 'when submitting a valid password' do - before do - fill_in t('forms.passwords.edit.labels.password'), with: 'NewVal!dPassw0rd' - click_button t('forms.passwords.edit.buttons.submit') - end - - it 'redirects to the homepage with a helpful message' do - screenshot_and_save_page - end - end - end - end - - context 'when submitting email not associated with an account' do - before do - fill_in 'password_reset_email_form_email', with: 'non-existent-email@example.com' - click_button t('forms.buttons.continue') - end - - it 'informs the user to check their email' do - screenshot_and_save_page - end - end - end - end - end -end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 4624b3be3a9..c8102bea954 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -3,8 +3,6 @@ require 'simplecov' SimpleCov.start 'rails' do add_filter '/config/' - add_filter '/lib/rspec/formatters/user_flow_formatter.rb' - add_filter '/lib/user_flow_exporter.rb' add_filter '/lib/deploy/migration_statement_timeout.rb' add_filter '/lib/tasks/create_test_accounts.rb' @@ -87,12 +85,6 @@ Telephony::Test::Call.clear_calls end - config.around(:each, user_flow: true) do |example| - Capybara.current_driver = :rack_test - example.run - Capybara.use_default_driver - end - config.before(:each) do IdentityDocAuth::Mock::DocAuthMockClient.reset! end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f06cc962590..d2e7aaf7644 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -27,9 +27,6 @@ # show the n slowest tests at the end of the test run config.profile_examples = RSPEC_RUNNING_IN_PARALLEL ? 10 : 0 - - # Skip user_flow specs in default tasks - config.filter_run_excluding user_flow: true end require 'webmock/rspec'