* chore: update the profile to be more dynamic. Maps out the group + field name + value for field * feat: add the correct input type to the form tying it to the correct method to update * feat: update the value and the label * feat: update the profile with a submit * refactor: ensure we dont show empty groups on the settings/profile * fix: this shoudl be dribbble * fix: use attributes! to refresh the cache to ensure that we font run into the case where Profile.attributes is [] * feat: show group description only when it exists * chore: add some classes * feat: use the profile update service to update the relevant user information * chore: rename variable * chore: use a scope to not return empty groups * fix: only update user if the profile sync is update is successful. Also capture errors * feat: style the crayons field checkbox * refactor: rename the attribute to user instead of profile * refactor: service object * Fix CodeClimate warning and rearrange code * Fix service object, add missing attribute * Fix flash and add redirect * Fix service object + spec * Fix user edits profile spec * Disable spec * Fix more specs * Undo vim fail * Add data update scripts * Update where clause in data update script * Rename method * Make sync_to_user conditional * Add after update actions and Honeycomb * feat: handle checkbox allow chcekbox to check * Don't set experience cookie in ProfilesController * Clean up Profiles::Update object * Fix spec * Remove Twitch integration (#10732) * Remove Twitch integration * Ignore columns * Change admin password in docs (#10735) Update default admin password in documentation * Do not require meta_keywords to be set (#10721) * Remove extra character from meta_keywords in /listings/index.html.erb * Remove meta_keywords from MANDATORY_CONFIGS * Add and use meta_keywords_tag helper * Use modern tag syntax instead of deprecated syntax * Add and use meta_keywords_default helper * Add and use meta_keywords_article helper * Remove * from meta_keywords_field.label * Update meta_keyword specs to account for no keywords being set * Generalizes Connect welcome message and settings nav tab and refactors tests (#10741) [deploy] * [deploy] fix: Clear chat input after Enter key submit (#10487) * fix: Clear chat input after Enter key submit * fix: Rename Composer component * test: Add integration tests for input value * After pressing Enter * After clicking Send * After clicking Save edit * After clicking Close edit * Remove tabindex of -1 on upload image input in the write a post editor (#10543) * Remove tabindex of -1 on upload image input in the write a post editor * Remove negative tabindex on image uploader's outer button * Give tabindex of -1 to outer button on image uploader * Update buttons.scss * [deploy] Add recaptcha keys to as site-configurable keys (#10736) * Add recaptcha keys to as site-configurable keys * Add recaptcha to site config view * Use site config key * Make ReCAPTCHA tag optional if keys are blank * Use proper capitalization of recaptcha Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com> * Refactor logic for readability * Use proper capitalization of recaptcha Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com> * Use proper capitalization of recaptcha Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com> * Use proper capitalization of recaptcha Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com> Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com> * Add Section for Series/Collections (#10719) * Add Section for Series/Collections I added a small section in this area of the docs to share some information about collections/ series. There is not much information in the documentation about collections or series and I felt like this would be beneficial to others looking to help with the Series feature of the website. * Updated wording of collections section. * Articles API: filtering by tags (#3654) (#10614) * Articles API: filtering by tags (#3654) * Address CR comments (#3654) * Add test case * Move tests * Update docs * Use _any_ option for querying by tags (#3654) * Move Imgproxy endpoint config to Images::Optimizer (#10742) * Link Email Account creation UI to existing Create Account UI (#10746) [deploy] * Conditional button to link to email accout creation * Rename email signup state param * Test for email account creation button scenarios * Add forem meta tags (#10747) * Add forem meta tags * Add docs * Update docs/technical-overview/compatibility.md Co-authored-by: Jacob Herrington <jacobherringtondeveloper@gmail.com> * Update docs/technical-overview/compatibility.md Co-authored-by: Jacob Herrington <jacobherringtondeveloper@gmail.com> * Update docs/technical-overview/compatibility.md Co-authored-by: Jacob Herrington <jacobherringtondeveloper@gmail.com> * Update docs/technical-overview/compatibility.md Co-authored-by: Jacob Herrington <jacobherringtondeveloper@gmail.com> * Remove Twitch-related columns from users table (#10750) * [deploy] Long text goes outside cards (#10740) * Add word-break to crayons-card Add word-break to crayons-card this will allow words to be broken if the word is too long but will wrap normally otherwise. * Use overflow-wrap: anywhere; `break-word` is deprecated though `anywhere` does not work in IE * Removes superfluous sign_in from users_onboarding_spec.rb (#10757) [deploy] * [deploy] Allow glitch id starting with ~ (#10544) * [deploy] crayons prep (#10737) * Add DEV special case for following hiring tag * Move image validation to Profiles::Update object * spec: move over the tests for validating images * Move follow_hiring_tag into the service object Co-authored-by: Ridhwana <ridhwana.khan16@gmail.com> Co-authored-by: Andrew Bone <AndrewB05@gmail.com> Co-authored-by: Vaidehi Joshi <vaidehi.sj@gmail.com> Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com> Co-authored-by: Robin Gagnon <me@reobin.dev> Co-authored-by: Manaswini <58681405+Manaswini1832@users.noreply.github.com> Co-authored-by: Andy Zhao <17884966+Zhao-Andy@users.noreply.github.com> Co-authored-by: Christopher Wray <53663762+cwray-tech@users.noreply.github.com> Co-authored-by: Rafal Trojanowski <rt.trojanowski@gmail.com> Co-authored-by: Mac Siri <mac@dev.to> Co-authored-by: Josh Puetz <josh@dev.to> Co-authored-by: Ben Halpern <bendhalpern@gmail.com> Co-authored-by: Jacob Herrington <jacobherringtondeveloper@gmail.com> Co-authored-by: irmela <irmela@berlin-coding.de> Co-authored-by: ludwiczakpawel <ludwiczakpawel@gmail.com>
373 lines
14 KiB
Ruby
373 lines
14 KiB
Ruby
require "rails_helper"
|
|
|
|
RSpec.describe "UserSettings", type: :request do
|
|
let(:user) { create(:user) }
|
|
|
|
describe "GET /settings/:tab" do
|
|
context "when not signed-in" do
|
|
it "redirects them to login" do
|
|
get "/settings"
|
|
expect(response).to redirect_to("/enter")
|
|
end
|
|
end
|
|
|
|
context "when signed-in" do
|
|
before { sign_in user }
|
|
|
|
it "renders various settings tabs properly" do
|
|
%w[organization misc account ux].each do |tab|
|
|
get "/settings/#{tab}"
|
|
expect(response.body).to include("Settings for")
|
|
end
|
|
end
|
|
|
|
it "handles unknown settings tab properly" do
|
|
expect { get "/settings/does-not-exist" }
|
|
.to raise_error(ActiveRecord::RecordNotFound)
|
|
end
|
|
|
|
it "displays content on ux tab properly" do
|
|
get "/settings/ux"
|
|
expect(response.body).to include("Style Customization")
|
|
end
|
|
|
|
it "displays content on misc tab properly" do
|
|
get "/settings/misc"
|
|
expect(response.body).to include("Connect", "Languages", "Sponsors", "Announcements", "Export Content")
|
|
end
|
|
|
|
it "displays content on RSS tab properly" do
|
|
get "/settings/publishing-from-rss"
|
|
title = "Publishing to #{SiteConfig.community_name} from RSS"
|
|
expect(response.body).to include(title)
|
|
end
|
|
|
|
it "renders heads up dupe account message with proper param" do
|
|
get "/settings?state=previous-registration"
|
|
error_message = "There is an existing account authorized with that social account"
|
|
expect(response.body).to include error_message
|
|
end
|
|
|
|
it "renders the proper response template" do
|
|
response_template = create(:response_template, user: user)
|
|
get user_settings_path(tab: "response-templates", id: response_template.id)
|
|
expect(response.body).to include "Editing a response template"
|
|
end
|
|
end
|
|
|
|
describe ":account" do
|
|
let(:remove_oauth_section) { "Remove OAuth Associations" }
|
|
let(:user) { create(:user, :with_identity) }
|
|
|
|
before do
|
|
omniauth_mock_providers_payload
|
|
sign_in user
|
|
end
|
|
|
|
it "allows users to visit the account page" do
|
|
get user_settings_path(tab: "account")
|
|
expect(response).to have_http_status(:ok)
|
|
end
|
|
|
|
it "shows the 'Remove OAuth' section if a user has multiple enabled identities" do
|
|
allow(Authentication::Providers).to receive(:enabled).and_return(Authentication::Providers.available)
|
|
providers = Authentication::Providers.available.first(2)
|
|
allow(user).to receive(:identities).and_return(user.identities.where(provider: providers))
|
|
|
|
get user_settings_path(tab: "account")
|
|
expect(response.body).to include(remove_oauth_section)
|
|
end
|
|
|
|
it "hides the 'Remove OAuth' section if a user has one enabled identity" do
|
|
provider = Authentication::Providers.available.first
|
|
allow(Authentication::Providers).to receive(:enabled).and_return([provider])
|
|
allow(user).to receive(:identities).and_return(user.identities.where(provider: provider))
|
|
|
|
get user_settings_path(tab: "account")
|
|
expect(response.body).not_to include(remove_oauth_section)
|
|
end
|
|
|
|
it "hides the 'Remove OAuth' section if a user has one enabled identity and one disabled" do
|
|
provider = Authentication::Providers.available.first
|
|
allow(Authentication::Providers).to receive(:enabled).and_return([provider])
|
|
|
|
get user_settings_path(tab: "account")
|
|
expect(response.body).not_to include(remove_oauth_section)
|
|
end
|
|
end
|
|
|
|
describe "connect providers accounts" do
|
|
before do
|
|
omniauth_mock_providers_payload
|
|
end
|
|
|
|
it "does not render the text for the enabled provider the user has an identity for" do
|
|
allow(Authentication::Providers).to receive(:enabled).and_return(Authentication::Providers.available)
|
|
user = create(:user, :with_identity, identities: [:github])
|
|
|
|
sign_in user
|
|
get "/settings"
|
|
|
|
expect(response.body).not_to include("Connect GitHub Account")
|
|
end
|
|
|
|
it "does not render the text for the disabled provider the user has an identity for" do
|
|
providers = Authentication::Providers.available - %i[github]
|
|
allow(Authentication::Providers).to receive(:enabled).and_return(providers)
|
|
user = create(:user, :with_identity, identities: [:github])
|
|
|
|
sign_in user
|
|
get "/settings"
|
|
|
|
expect(response.body).not_to include("Connect GitHub Account")
|
|
end
|
|
|
|
it "renders the text for the enabled provider the user has no identity for" do
|
|
allow(Authentication::Providers).to receive(:enabled).and_return(Authentication::Providers.available)
|
|
user = create(:user, :with_identity, identities: [:twitter])
|
|
|
|
sign_in user
|
|
get "/settings"
|
|
|
|
expect(response.body).to include("Connect GitHub Account")
|
|
end
|
|
end
|
|
|
|
describe ":integrations" do
|
|
it "renders the repositories container if the user has authenticated through GitHub" do
|
|
user = create(:user, :with_identity, identities: [:github])
|
|
sign_in user
|
|
|
|
get user_settings_path(tab: :integrations)
|
|
expect(response.body).to include("github-repos-container")
|
|
end
|
|
|
|
it "does not render anything if the user has not authenticated through GitHub" do
|
|
sign_in user
|
|
|
|
get user_settings_path(tab: :integrations)
|
|
expect(response.body).not_to include("github-repos-container")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "PUT /update/:id" do
|
|
before { sign_in user }
|
|
|
|
it "updates summary" do
|
|
put "/users/#{user.id}", params: { user: { tab: "profile", summary: "Hello new summary" } }
|
|
expect(user.summary).to eq("Hello new summary")
|
|
end
|
|
|
|
it "updates profile_updated_at" do
|
|
user.update_column(:profile_updated_at, 2.weeks.ago)
|
|
put "/users/#{user.id}", params: { user: { tab: "profile", summary: "Hello new summary" } }
|
|
expect(user.reload.profile_updated_at).to be > 2.minutes.ago
|
|
end
|
|
|
|
it "disables reaction notifications" do
|
|
put "/users/#{user.id}", params: { user: { tab: "notifications", reaction_notifications: 0 } }
|
|
expect(user.reload.reaction_notifications).to be(false)
|
|
end
|
|
|
|
it "enables community-success notifications" do
|
|
put "/users/#{user.id}", params: { user: { tab: "notifications", mod_roundrobin_notifications: 1 } }
|
|
expect(user.reload.mod_roundrobin_notifications).to be(true)
|
|
end
|
|
|
|
it "updates the users announcement display preferences" do
|
|
expect do
|
|
put "/users/#{user.id}", params: { user: { tab: "misc", display_announcements: 0 } }
|
|
end.to change { user.reload.display_announcements }.from(true).to(false)
|
|
end
|
|
|
|
it "disables community-success notifications" do
|
|
put "/users/#{user.id}", params: { user: { tab: "notifications", mod_roundrobin_notifications: 0 } }
|
|
expect(user.reload.mod_roundrobin_notifications).to be(false)
|
|
end
|
|
|
|
it "can toggle welcome notifications" do
|
|
put "/users/#{user.id}", params: { user: { tab: "notifications", welcome_notifications: 0 } }
|
|
expect(user.reload.subscribed_to_welcome_notifications?).to be(false)
|
|
|
|
put "/users/#{user.id}", params: { user: { tab: "notifications", welcome_notifications: 1 } }
|
|
expect(user.reload.subscribed_to_welcome_notifications?).to be(true)
|
|
end
|
|
|
|
it "updates username to too short username" do
|
|
put "/users/#{user.id}", params: { user: { tab: "profile", username: "h" } }
|
|
expect(response.body).to include("Username is too short")
|
|
end
|
|
|
|
it "returns error message if user can't be saved" do
|
|
put "/users/#{user.id}", params: { user: { password: "1", password_confirmation: "1" } }
|
|
|
|
expect(flash[:error]).to include("Password is too short")
|
|
end
|
|
|
|
it "returns an error message if the passwords do not match" do
|
|
put "/users/#{user.id}", params: { user: { password: "asdfghjk", password_confirmation: "qwertyui" } }
|
|
|
|
expect(flash[:error]).to include("Password doesn't match password confirmation")
|
|
end
|
|
|
|
context "when requesting an export of the articles" do
|
|
def send_request(export_requested: true)
|
|
put "/users/#{user.id}", params: {
|
|
user: { tab: "misc", export_requested: export_requested }
|
|
}
|
|
end
|
|
|
|
it "updates export_requested flag" do
|
|
send_request
|
|
expect(user.reload.export_requested).to be(true)
|
|
end
|
|
|
|
it "displays a flash with a reminder for the user to expect an email" do
|
|
send_request
|
|
expect(flash[:settings_notice]).to include("The export will be emailed to you shortly.")
|
|
end
|
|
|
|
it "hides the checkbox" do
|
|
send_request
|
|
follow_redirect!
|
|
expect(response.body).not_to include("Request an export of your posts")
|
|
end
|
|
|
|
it "tells the user they recently requested an export" do
|
|
send_request
|
|
follow_redirect!
|
|
expect(response.body).to include("You have recently requested an export")
|
|
end
|
|
|
|
it "sends an email" do
|
|
expect do
|
|
sidekiq_perform_enqueued_jobs do
|
|
send_request
|
|
end
|
|
end.to change { ActionMailer::Base.deliveries.count }.by(1)
|
|
end
|
|
|
|
it "does not send an email if there was no request" do
|
|
sidekiq_perform_enqueued_jobs do
|
|
expect { send_request(export_requested: false) }.not_to(change { ActionMailer::Base.deliveries.count })
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "POST /users/update_language_settings" do
|
|
before { sign_in user }
|
|
|
|
it "updates language settings" do
|
|
post "/users/update_language_settings", params: { user: { preferred_languages: %w[ja es] } }
|
|
user.reload
|
|
expect(user.language_settings["preferred_languages"]).to eq(%w[ja es])
|
|
end
|
|
|
|
it "keeps the estimated_default_language" do
|
|
user.update_column(:language_settings, estimated_default_language: "ru", preferred_languages: %w[en es])
|
|
post "/users/update_language_settings", params: { user: { preferred_languages: %w[it en] } }
|
|
user.reload
|
|
expect(user.language_settings["estimated_default_language"]).to eq("ru")
|
|
end
|
|
|
|
it "doesn't set non-existent languages" do
|
|
user.update_column(:language_settings, estimated_default_language: "ru", preferred_languages: %w[en es])
|
|
post "/users/update_language_settings", params: { user: { preferred_languages: %w[it en blah] } }
|
|
user.reload
|
|
expect(user.language_settings["preferred_languages"].sort).to eq(%w[en it])
|
|
end
|
|
end
|
|
|
|
describe "DELETE /users/remove_identity" do
|
|
let(:provider) { Authentication::Providers.available.first }
|
|
|
|
context "when user has multiple identities" do
|
|
let(:user) { create(:user, :with_identity) }
|
|
|
|
before do
|
|
omniauth_mock_providers_payload
|
|
sign_in user
|
|
end
|
|
|
|
it "removes the correct identity" do
|
|
expect do
|
|
delete "/users/remove_identity", params: { provider: provider }
|
|
end.to change(user.identities, :count).by(-1)
|
|
|
|
expect(user.identities.map(&:provider)).not_to include(provider)
|
|
end
|
|
|
|
it "empties their associated username" do
|
|
delete "/users/remove_identity", params: { provider: provider }
|
|
|
|
expect(user.public_send("#{provider}_username")).to be(nil)
|
|
end
|
|
|
|
it "updates the profile_updated_at timestamp" do
|
|
original_profile_updated_at = user.profile_updated_at
|
|
delete "/users/remove_identity", params: { provider: provider }
|
|
expect(user.profile_updated_at.to_i).to be > original_profile_updated_at.to_i
|
|
end
|
|
|
|
it "redirects successfully to /settings/account" do
|
|
delete "/users/remove_identity", params: { provider: provider }
|
|
expect(response).to redirect_to("/settings/account")
|
|
end
|
|
|
|
it "renders a successful response message" do
|
|
delete "/users/remove_identity", params: { provider: provider }
|
|
auth_provider = Authentication::Providers.get!(provider)
|
|
|
|
expected_notice = "Your #{auth_provider.official_name} account was successfully removed."
|
|
expect(flash[:settings_notice]).to eq(expected_notice)
|
|
end
|
|
|
|
it "redirects the user with an error if the corresponding provider has been since disabled" do
|
|
providers = Authentication::Providers.available - [provider]
|
|
allow(Authentication::Providers).to receive(:enabled).and_return(providers)
|
|
delete "/users/remove_identity", params: { provider: provider }
|
|
expect(response).to redirect_to("/settings/account")
|
|
|
|
error = "An error occurred. Please try again or send an email to: #{SiteConfig.email_addresses[:default]}"
|
|
expect(flash[:error]).to eq(error)
|
|
end
|
|
|
|
it "does not show the 'Remove OAuth' section afterwards if only one identity remains" do
|
|
providers = Authentication::Providers.available.first(2)
|
|
allow(user).to receive(:identities).and_return(user.identities.where(provider: providers))
|
|
|
|
delete "/users/remove_identity", params: { provider: providers.first }
|
|
expect(response.body).not_to include("Remove OAuth Associations")
|
|
end
|
|
end
|
|
|
|
# Users won't be able to do this via the view, but in case they hit the route somehow...
|
|
context "when user has only one identity" do
|
|
let(:user) { create(:user, :with_identity, identities: [provider]) }
|
|
|
|
before do
|
|
sign_in user
|
|
end
|
|
|
|
it "sets the proper flash error message" do
|
|
delete "/users/remove_identity", params: { provider: provider }
|
|
|
|
error = "An error occurred. Please try again or send an email to: #{SiteConfig.email_addresses[:default]}"
|
|
expect(flash[:error]).to eq(error)
|
|
end
|
|
|
|
it "does not delete any identities" do
|
|
expect do
|
|
delete "/users/remove_identity", params: { provider: provider }
|
|
end.not_to change(user.identities, :count)
|
|
end
|
|
|
|
it "redirects successfully to /settings/account" do
|
|
delete "/users/remove_identity", params: { provider: provider }
|
|
expect(response).to redirect_to("/settings/account")
|
|
end
|
|
end
|
|
end
|
|
end
|