mirror of
https://github.com/discourse/discourse.git
synced 2025-06-22 12:21:54 +08:00

This commit improves said method to ensure that user is redirected to the right page before returning. ### Reviewer notes Example of test flakiness: https://github.com/discourse/discourse/actions/runs/14081653020/job/39435797236 ``` Failure/Error: raise capybara_timeout_error CapybaraTimeoutExtension::CapybaraTimedOut: This spec passed, but capybara waited for the full wait duration (10s) at least once. This will slow down the test suite. Beware of negating the result of selenium's RSpec matchers. [Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_user_resetting_password_when_desktop_when_user_has_multi_factor_authentication_configured_when_user_has_security_key_and_backup_codes_configured_should_allow_a_user_to_reset_pass_261.png ~~~~~~~ JS LOGS ~~~~~~~ ~~~~~ END JS LOGS ~~~~~ Shared Example Group: "forgot password scenarios" called from ./spec/system/forgot_password_spec.rb:213 ./spec/rails_helper.rb:426:in `block (3 levels) in <top (required)>' ./spec/rails_helper.rb:619:in `block (3 levels) in <top (required)>' /var/www/discourse/vendor/bundle/ruby/3.3.0/gems/benchmark-0.4.0/lib/benchmark.rb:304:in `measure' ./spec/rails_helper.rb:619:in `block (2 levels) in <top (required)>' ./spec/rails_helper.rb:580:in `block (3 levels) in <top (required)>' /var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:185:in `block in timeout' /var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:192:in `timeout' ./spec/rails_helper.rb:570:in `block (2 levels) in <top (required)>' ./spec/rails_helper.rb:527:in `block (2 levels) in <top (required)>' /var/www/discourse/vendor/bundle/ruby/3.3.0/gems/webmock-3.25.1/lib/webmock/rspec.rb:39:in `block (2 levels) in <top (required)>' ```
190 lines
5.7 KiB
Ruby
190 lines
5.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
describe "Changing email", type: :system do
|
|
fab!(:password) { "mysupersecurepassword" }
|
|
fab!(:user) { Fabricate(:user, active: true, password: password) }
|
|
let(:new_email) { "newemail@example.com" }
|
|
let(:user_preferences_security_page) { PageObjects::Pages::UserPreferencesSecurity.new }
|
|
let(:user_preferences_page) { PageObjects::Pages::UserPreferences.new }
|
|
|
|
before { Jobs.run_immediately! }
|
|
|
|
def generate_confirm_link
|
|
visit "/my/preferences/account"
|
|
|
|
email_dropdown = PageObjects::Components::SelectKit.new(".email-dropdown")
|
|
expect(email_dropdown.visible?).to eq(true)
|
|
email_dropdown.select_row_by_value("updateEmail")
|
|
|
|
find("#change-email").fill_in with: "newemail@example.com"
|
|
|
|
find(".save-button button").click
|
|
|
|
wait_for(timeout: Capybara.default_max_wait_time * 2) do
|
|
ActionMailer::Base.deliveries.count === 1
|
|
end
|
|
|
|
if user.admin?
|
|
get_link_from_email(:old)
|
|
else
|
|
get_link_from_email(:new)
|
|
end
|
|
end
|
|
|
|
def get_link_from_email(type)
|
|
mail = ActionMailer::Base.deliveries.last
|
|
expect(mail.to).to contain_exactly(type == :new ? new_email : user.email)
|
|
|
|
mail.body.to_s[%r{/u/confirm-#{type}-email/\S+}, 0]
|
|
end
|
|
|
|
it "allows regular user to change their email" do
|
|
sign_in user
|
|
|
|
visit generate_confirm_link
|
|
|
|
find(".confirm-new-email .btn-primary").click
|
|
|
|
expect(page).to have_css(".dialog-body", text: I18n.t("js.user.change_email.confirm_success"))
|
|
find(".dialog-footer .btn-primary").click
|
|
|
|
try_until_success { expect(user.reload.primary_email.email).to eq(new_email) }
|
|
end
|
|
|
|
it "works when user has totp 2fa" do
|
|
SiteSetting.hide_email_address_taken = false
|
|
|
|
second_factor = Fabricate(:user_second_factor_totp, user: user)
|
|
sign_in user
|
|
|
|
visit generate_confirm_link
|
|
|
|
find(".confirm-new-email .btn-primary").click
|
|
find(".second-factor-token-input").fill_in with: second_factor.totp_object.now
|
|
find("button[type=submit]").click
|
|
|
|
try_until_success { expect(user.reload.primary_email.email).to eq(new_email) }
|
|
end
|
|
|
|
it "works when user has webauthn 2fa" do
|
|
# enforced 2FA flow needs a user created > 5 minutes ago
|
|
user.created_at = 6.minutes.ago
|
|
user.save!
|
|
|
|
sign_in user
|
|
|
|
DiscourseWebauthn.stubs(:origin).returns(current_host + ":" + Capybara.server_port.to_s)
|
|
options =
|
|
::Selenium::WebDriver::VirtualAuthenticatorOptions.new(
|
|
user_verification: true,
|
|
user_verified: true,
|
|
resident_key: true,
|
|
)
|
|
authenticator = page.driver.browser.add_virtual_authenticator(options)
|
|
|
|
user_preferences_security_page.visit(user)
|
|
user_preferences_security_page.visit_second_factor(user, password)
|
|
|
|
find(".security-key .new-security-key").click
|
|
expect(user_preferences_security_page).to have_css("input#security-key-name")
|
|
|
|
find(".d-modal__body input#security-key-name").fill_in(with: "First Key")
|
|
find(".add-security-key").click
|
|
|
|
expect(user_preferences_security_page).to have_css(".security-key .second-factor-item")
|
|
|
|
visit generate_confirm_link
|
|
|
|
find(".confirm-new-email .btn-primary").click
|
|
find("#security-key-authenticate-button").click
|
|
|
|
try_until_success { expect(user.reload.primary_email.email).to eq(new_email) }
|
|
ensure
|
|
authenticator&.remove!
|
|
end
|
|
|
|
it "does not require login to confirm email change" do
|
|
SiteSetting.full_page_login = false
|
|
second_factor = Fabricate(:user_second_factor_totp, user: user)
|
|
sign_in user
|
|
|
|
confirm_link = generate_confirm_link
|
|
|
|
Capybara.reset_sessions! # log out
|
|
|
|
visit confirm_link
|
|
|
|
find(".confirm-new-email .btn-primary").click
|
|
find(".second-factor-token-input").fill_in with: second_factor.totp_object.now
|
|
find("button[type=submit]:not([disabled])").click
|
|
|
|
try_until_success { expect(user.reload.primary_email.email).to eq(new_email) }
|
|
end
|
|
|
|
it "makes admins verify old email" do
|
|
user.update!(admin: true)
|
|
sign_in user
|
|
|
|
confirm_old_link = generate_confirm_link
|
|
|
|
# Confirm old email
|
|
visit confirm_old_link
|
|
find(".confirm-old-email .btn-primary").click
|
|
expect(page).to have_css(
|
|
".dialog-body",
|
|
text: I18n.t("js.user.change_email.authorizing_old.confirm_success"),
|
|
)
|
|
find(".dialog-footer .btn-primary").click
|
|
|
|
# Confirm new email
|
|
wait_for(timeout: Capybara.default_max_wait_time * 2) do
|
|
ActionMailer::Base.deliveries.count === 2
|
|
end
|
|
|
|
confirm_new_link = get_link_from_email(:new)
|
|
|
|
visit confirm_new_link
|
|
|
|
find(".confirm-new-email .btn-primary").click
|
|
|
|
expect(page).to have_css(".dialog-body", text: I18n.t("js.user.change_email.confirm_success"))
|
|
find(".dialog-footer .btn-primary").click
|
|
|
|
try_until_success { expect(user.reload.primary_email.email).to eq(new_email) }
|
|
end
|
|
|
|
it "allows admin to verify old email while logged out" do
|
|
user.update!(admin: true)
|
|
sign_in user
|
|
|
|
confirm_old_link = generate_confirm_link
|
|
|
|
Capybara.reset_sessions! # log out
|
|
|
|
# Confirm old email
|
|
visit confirm_old_link
|
|
find(".confirm-old-email .btn-primary").click
|
|
expect(page).to have_css(
|
|
".dialog-body",
|
|
text: I18n.t("js.user.change_email.authorizing_old.confirm_success"),
|
|
)
|
|
find(".dialog-footer .btn-primary").click
|
|
|
|
# Confirm new email
|
|
wait_for(timeout: Capybara.default_max_wait_time * 2) do
|
|
ActionMailer::Base.deliveries.count === 2
|
|
end
|
|
|
|
confirm_new_link = get_link_from_email(:new)
|
|
|
|
visit confirm_new_link
|
|
|
|
find(".confirm-new-email .btn-primary").click
|
|
|
|
expect(page).to have_css(".dialog-body", text: I18n.t("js.user.change_email.confirm_success"))
|
|
find(".dialog-footer .btn-primary").click
|
|
|
|
try_until_success { expect(user.reload.primary_email.email).to eq(new_email) }
|
|
end
|
|
end
|