Remove hardcoded user profile fields (#14079)

* Remove duplicated work display from header

* Update work profile field handling

* Update DUS + spec

* Delegate more carefully

* Update delegation guard

* Adapt for removed delegation

* Undo accidental schema changes

* Fix seeds

* Remove accidentaly change

* Fix User#processed_website_url

* Update guard clause

* Update profile card content

* Add Organization#profile

* Be more conservative with profile fields

* Spec fixes round 1

* Fix typo

* Update spec

* Limit number of header fields and update card content

* Decorate correct model

* Update factory

* Update schema.rb

* Fix validation

* How bad could this possibly be?

* Pretty bad, nevermind

* Remove obsolete code

* Reset profile fields during test runs

* Move profile fields back to before(:suite)

* Spec fixes

* Remove accidentally re-added files

* More spec fixes

* Specs

* Change User#tag_keywords_for_search

* More spec fixes

* Add comment

* Undo accidental schema changes

* Attempt spec fix

* Remove fix attempt

* Fix e2e test

* Update spec

* Remove guard clause

* Remove hardcoded instances of education field

* WIP Access display_email_on_profile via User Settings

* Remove unused profile column

* WIP fix issues revealed by systems specs

* WIP fix issues revealed by services specs

* WIP Fix failing tests

* WIP Fix spec failures

* wip

* Move two attributes from controller to decorator

* Update comment

* More consistently use user.tag_line

Even before the profile changes we sometimes used the user.summary
attribute directly but used the user.tag_line method in other places.

* Remove education

* Add comment

* WIP

* Clean up mostly_work_with

* WIP

* Update work profile field handling

* More work-related changes

* Remove settings_only from display_area enum

* Remove quickfix from _metadata partial

* Remove special attributes

* Remove leftover spec

* Retrieve location from profile, not user

* Profile.special_attributes no longer exists

* Update specs

* More spec fixes

* Update UsersController

* Update UsersController and spec

* Fix e2e seeds

* Minor cleanup

* More e2e seed fixes

* Fix profile field CSV

* Fix e2e seeds

* Move one more attribute in e2e seeds

* Remove duplicate line

* Clear inputs before typing in them

Co-authored-by: Jacob Herrington <jacobherringtondeveloper@gmail.com>
This commit is contained in:
Michael Kohl 2021-08-18 11:44:53 +07:00 committed by GitHub
parent 9b7a0e1036
commit 1ebec4b67f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 157 additions and 185 deletions

View file

@ -333,6 +333,7 @@ class StoriesController < ApplicationController
def set_user_json_ld
# For more info on structuring data with JSON-LD,
# please refer to this link: https://moz.com/blog/json-ld-for-beginners
decorated_user = @user.decorate
@user_json_ld = {
"@context": "http://schema.org",
"@type": "Person",
@ -344,9 +345,8 @@ class StoriesController < ApplicationController
sameAs: user_same_as,
image: Images::Profile.call(@user.profile_image_url, length: 320),
name: @user.name,
email: @user.setting.display_email_on_profile ? @user.email : nil,
description: @user.profile.summary.presence || "404 bio not found",
alumniOf: @user.education.presence
email: decorated_user.profile_email,
description: decorated_user.profile_summary
}.reject { |_, v| v.blank? }
end

View file

@ -46,9 +46,7 @@ class UsersController < ApplicationController
set_current_tab(params["user"]["tab"])
set_users_setting_and_notification_setting
@user.assign_attributes(permitted_attributes(@user))
if @user.save
if @user.update(permitted_attributes(@user))
# NOTE: [@rhymes] this queues a job to fetch the feed each time the profile is updated, regardless if the user
# explicitly requested "Feed fetch now" or simply updated any other field
import_articles_from_feed(@user)
@ -170,6 +168,7 @@ class UsersController < ApplicationController
def onboarding_update
authorize User
user_params = { saw_onboarding: true }
if params[:user]
@ -181,7 +180,7 @@ class UsersController < ApplicationController
user_params.merge!(params[:user].permit(ALLOWED_USER_PARAMS))
end
update_result = Profiles::Update.call(current_user, { user: user_params, profile: profile_params })
update_result = Profiles::Update.call(current_user, user: user_params, profile: profile_params)
render_update_response(update_result.success?, update_result.errors_as_sentence)
end
@ -381,7 +380,7 @@ class UsersController < ApplicationController
end
def profile_params
params[:profile] ? params[:profile].permit(Profile.attributes) : nil
params[:profile] ? params[:profile].permit(Profile.static_fields + Profile.attributes) : nil
end
def password_params

View file

@ -18,6 +18,8 @@ class UserDecorator < ApplicationDecorator
},
].freeze
DEFAULT_PROFILE_SUMMARY = "404 bio not found".freeze
def cached_followed_tags
follows_map = Rails.cache.fetch("user-#{id}-#{following_tags_count}-#{last_followed_at&.rfc3339}/followed_tags",
expires_in: 20.hours) do
@ -110,6 +112,19 @@ class UserDecorator < ApplicationDecorator
created_at.after?(min_days.days.ago)
end
# Returns the user's public email if it is set and the display_email_on_profile
# settings is set to true.
def profile_email
return unless setting.display_email_on_profile?
email
end
# Returns the users profile summary or a placeholder text
def profile_summary
profile.summary.presence || DEFAULT_PROFILE_SUMMARY
end
delegate :display_sponsors, to: :setting
delegate :display_announcements, to: :setting

View file

@ -11,15 +11,6 @@ class Profile < ApplicationRecord
# any profile on a given Forem.
STATIC_FIELDS = %w[summary location website_url].freeze
SPECIAL_DISPLAY_ATTRIBUTES = %w[summary location].freeze
# NOTE: @citizen428 This is a temporary mapping so we don't break DEV during
# profile migration/generalization work.
MAPPED_ATTRIBUTES = {
education: :education,
skills_languages: :mostly_work_with
}.with_indifferent_access.freeze
# Generates typed accessors for all currently defined profile fields.
def self.refresh_attributes!
return if ENV["ENV_AVAILABLE"] == "false"
@ -30,7 +21,7 @@ class Profile < ApplicationRecord
# TODO: [@jacobherrington] Remove this when ProfileFields for the static
# fields are dropped from production and the associated data is removed.
# https://github.com/forem/forem/pull/13641#discussion_r637641185
next if STATIC_FIELDS.any?(field.attribute_name)
next if field.attribute_name.in?(STATIC_FIELDS)
store_attribute :data, field.attribute_name.to_sym, field.type
end
@ -45,10 +36,6 @@ class Profile < ApplicationRecord
(stored_attributes[:data] || []).map(&:to_s)
end
def self.special_attributes
SPECIAL_DISPLAY_ATTRIBUTES
end
def self.static_fields
STATIC_FIELDS
end

View file

@ -13,8 +13,7 @@ class ProfileField < ApplicationRecord
enum display_area: {
header: 0,
left_sidebar: 1,
settings_only: 2
left_sidebar: 1
}
belongs_to :profile_field_group, optional: true

View file

@ -12,7 +12,6 @@ class User < ApplicationRecord
bg_color_hex
currently_hacking_on
currently_learning
currently_streaming_on
dribbble_url
education
email_public
@ -58,19 +57,12 @@ class User < ApplicationRecord
after_create_commit -> { Profile.create(user: self) }, unless: :_skip_creating_profile
# Getters and setters for unmapped profile attributes
(PROFILE_COLUMNS - Profile::MAPPED_ATTRIBUTES.values).each do |column|
PROFILE_COLUMNS.each do |column|
next if INACTIVE_PROFILE_COLUMNS.include?(column)
next unless column.in?(Profile.attributes)
delegate column, "#{column}=", to: :profile, allow_nil: true
end
# Getters and setters for mapped profile attributes
Profile::MAPPED_ATTRIBUTES.each do |profile_attribute, user_attribute|
define_method(user_attribute) { profile&.public_send(profile_attribute) }
define_method("#{user_attribute}=") do |value|
profile&.public_send("#{profile_attribute}=", value)
end
end
end
end
@ -289,7 +281,7 @@ class User < ApplicationRecord
end
def tag_line
summary
profile.summary
end
def twitter_url

View file

@ -20,7 +20,8 @@ class UserPolicy < ApplicationPolicy
email_follower_notifications
email_membership_newsletter
email_mention_notifications
email_newsletter email_public
email_newsletter
email_public
email_tag_mod_newsletter
email_unread_notifications
employer_name
@ -33,21 +34,17 @@ class UserPolicy < ApplicationPolicy
feed_url
inbox_guidelines
inbox_type
location
mobile_comment_notifications
mod_roundrobin_notifications
welcome_notifications
mostly_work_with
name
password
password_confirmation
payment_pointer
permit_adjacent_sponsors
profile_image
summary
text_color_hex
username
website_url
].freeze
def edit?

View file

@ -1,3 +1,5 @@
# TODO: This should probably become `Users::Update` as we're dealing with core
# user attributes, profile attributes and user settings.
module Profiles
# Deals with updates that affect fields on +Profile+ and/or +User+ in a transparent way.
class Update

View file

@ -16,7 +16,7 @@ module Users
suggested_user_ids = Rails.cache.fetch(generate_cache_name, expires_in: 120.hours) do
(reputable_user_ids + random_user_ids).uniq
end
User.select(:id, :name, :username, :profile_image, :summary).where(id: suggested_user_ids)
User.select(:id, :name, :username, :profile_image).where(id: suggested_user_ids)
end
private

View file

@ -5,12 +5,13 @@ json.extract!(
:id,
:username,
:name,
:summary,
:twitter_username,
:github_username,
:website_url,
:location,
)
Profile.static_fields.each do |attr|
json.set! attr, user.profile.public_send(attr)
end
json.joined_at user.created_at.strftime("%b %e, %Y")
json.profile_image Images::Profile.call(user.profile_image_url, length: 320)

View file

@ -7,7 +7,7 @@ xml.rss version: "2.0" do
xml.channel do
xml.title user ? user.name : community_name
xml.author user ? user.name : community_name
xml.description user ? user.summary : Settings::Community.community_description
xml.description user ? user.tag_line : Settings::Community.community_description
xml.link user ? app_url(user.path) : app_url
xml.language "en"
if user

View file

@ -53,8 +53,8 @@
<%= inline_svg_tag("github.svg", class: "crayons-icon", aria: true, title: "GitHub logo") %>
</a>
<% end %>
<% if @user.website_url.present? %>
<a href="<%= @user.website_url %>" target="_blank" rel="noopener nofollow me" class="px-1 align-middle inline-block">
<% if @user.profile.website_url.present? %>
<a href="<%= @user.profile.website_url %>" target="_blank" rel="noopener nofollow me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("external.svg", class: "crayons-icon", aria: true, title: "External link icon") %>
</a>
<% end %>

View file

@ -29,19 +29,18 @@
</div>
</li>
<% end %>
<% if actor.location.present? %>
<% if actor.profile.location.present? %>
<li>
<div class="key">
Location
</div>
<div class="value">
<%= actor.location %>
<%= actor.profile.location %>
</div>
</li>
<% end %>
<% if (header_fields = actor.profile.decorate.ui_attributes_for(area: :header)).present? %>
<% header_fields.sort.each do |title, value| %>
<% next if Profile.special_attributes.include?(title) %>
<li>
<div class="key">
<%= title %>

View file

@ -20,7 +20,7 @@
<div class="ltag__user__content">
<h2><%= link_to_if user_path.present?, user.name, user_path, class: "ltag__user__link" %><%= follow_button %></h2>
<div class="ltag__user__summary">
<%= link_to_if user_path.present?, user.summary, user_path, class: "ltag__user__link" %>
<%= link_to_if user_path.present?, user.tag_line, user_path, class: "ltag__user__link" %>
</div>
</div>
</div>

View file

@ -1,19 +1,18 @@
<link rel="canonical" href="<%= user_url(@user) %>" />
<meta name="description" content="<%= @user.profile.summary %>">
<meta name="description" content="<%= @user.tag_line %>">
<%= meta_keywords_default %>
<meta property="og:type" content="website" />
<meta property="og:url" content="<%= user_url(@user) %>" />
<meta property="og:title" content="<%= @user.name %> — <%= community_name %> Profile" />
<meta property="og:image" content="<%= user_social_image_url(@user) %>">
<meta property="og:description" content="<%= @user.profile.summary %>" />
<meta property="og:description" content="<%= @user.tag_line %>" />
<meta property="og:site_name" content="<%= community_name %>" />
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@<%= Settings::General.social_media_handles["twitter"] %>">
<meta name="twitter:creator" content="@<%= @user.twitter_username %>">
<meta name="twitter:title" content="<%= @user.name %> — <%= community_name %> Profile">
<meta name="twitter:description" content="<%= @user.profile.summary %>">
<meta name="twitter:description" content="<%= @user.tag_line %>">
<meta name="twitter:image:src" content="<%= user_social_image_url(@user) %>">
<% if @stories.any? %>
<%= auto_discovery_link_tag(:rss, app_url("/feed/#{@user.username}"), title: "#{community_name} RSS Feed") %>

View file

@ -1,7 +1,6 @@
<% if (header_fields = @profile.ui_attributes_for(area: :header)).present? %>
<div class="profile-header__bottom fs-base">
<% header_fields.sort.each do |title, value| %>
<% next if Profile.special_attributes.include?(title) %>
<div class="crayons-definition">
<strong class="crayons-definition__title">
<%= sanitized_sidebar title.titleize %>

View file

@ -1,7 +1,7 @@
json.array! @users.each do |user|
json.extract!(user, :id, :name, :username)
json.summary truncate(user.summary.presence || "Active #{community_name} author", length: 100)
json.summary truncate(user.tag_line || "Active #{community_name} author", length: 100)
json.profile_image_url Images::Profile.call(user.profile_image_url, length: 90)
json.following false
end

View file

@ -61,13 +61,13 @@
<div class="profile-header__details">
<h1 class="crayons-title fw-heavy mb-2"><%= @user.name %></h1>
<p class="fs-base m:fs-l color-base-90 mb-4 mx-auto max-w-100 m:max-w-75"><%= @user.profile.summary.presence || ["404 bio not found"].sample %></p>
<p class="fs-base m:fs-l color-base-90 mb-4 mx-auto max-w-100 m:max-w-75"><%= @user.tag_line.presence || ["404 bio not found"].sample %></p>
<div class="profile-header__meta">
<% if @user.profile.location.present? %>
<span class="profile-header__meta__item">
<%= inline_svg_tag("location.svg", aria: true, class: "crayons-icon mr-2 shrink-0", title: "Location") %>
<%= @user.location %>
<%= @user.profile.location %>
</span>
<% end %>
@ -80,14 +80,14 @@
<% if @user.setting.display_email_on_profile %>
<a href="mailto:<%= @user.email %>" class="profile-header__meta__item">
<%= inline_svg_tag("email.svg", class: "crayons-icon mr-2 shrink-0", aria: true, title: "Email address") %>
<%= @user.email %>
<%= @user.setting.display_email_on_profile %>
</a>
<% end %>
<% if @user.profile.website_url.present? %>
<a href="<%= @user.website_url %>" target="_blank" rel="noopener me" class="profile-header__meta__item">
<a href="<%= @user.profile.website_url %>" target="_blank" rel="noopener me" class="profile-header__meta__item">
<%= inline_svg_tag("link-external.svg", class: "crayons-icon mr-2 shrink-0", aria: true, title: "Personal website") %>
<%= @user.website_url %>
<%= @user.profile.website_url %>
</a>
<% end %>

View file

@ -81,9 +81,11 @@ Rails.application.configure do
Bullet.add_safelist(type: :unused_eager_loading, class_name: "Article", association: :top_comments)
Bullet.add_safelist(type: :unused_eager_loading, class_name: "Comment", association: :user)
# NOTE: @citizen428 Temporarily ignoring this while working out user - profile relationship
Bullet.add_safelist(type: :n_plus_one_query, class_name: "User", association: :profile)
Bullet.add_safelist(type: :n_plus_one_query, class_name: "User", association: :setting)
Bullet.add_safelist(type: :n_plus_one_query, class_name: "User", association: :notification_setting)
Bullet.add_whitelist(type: :n_plus_one_query, class_name: "User", association: :profile)
Bullet.add_whitelist(type: :n_plus_one_query, class_name: "User", association: :setting)
Bullet.add_whitelist(type: :n_plus_one_query, class_name: "User", association: :notification_setting)
# NOTE: @citizen428 Let's ignore this for now, we have to revisit the user - profile relationship anyway
Bullet.add_whitelist(type: :n_plus_one_query, class_name: "Profile", association: :user)
# @mstruve: These occur during setting updates, not sure how since we are only dealing with single setting records
Bullet.add_safelist(type: :n_plus_one_query, class_name: "Users::Setting", association: :user)
Bullet.add_safelist(type: :n_plus_one_query, class_name: "Users::NotificationSetting", association: :user)

View file

@ -13,9 +13,13 @@ describe('User Update Settings Profile', () => {
const summary = 'This is my story...';
const location = 'New York City';
cy.findByLabelText(/^Website URL$/i).type(websiteURL);
cy.findByLabelText(/^Bio$/i).type(summary);
cy.findByLabelText(/^Location$/i).type(location);
cy.findByLabelText(/^Website URL$/i)
.clear()
.type(websiteURL);
cy.findByLabelText(/^Bio$/i).clear().type(summary);
cy.findByLabelText(/^Location$/i)
.clear()
.type(location);
cy.findByText(/^Save Profile Information$/i).click();

View file

@ -1,9 +0,0 @@
module DataUpdateScripts
class BackfillProfileSkillsLanguages
def run
User.where.not(mostly_work_with: [nil, ""]).find_each do |user|
user.profile.update(skills_languages: user.mostly_work_with)
end
end
end
end

View file

@ -1,28 +0,0 @@
module DataUpdateScripts
class SetProperDisplayAreaForProfileFields
def run
ProfileField.where(attribute_name: %w[brand_color1
brand_color2
recruiters_can_contact_me_about_job_opportunities
display_email_on_profile
twitter_url
github_url
facebook_url
linkedin_url
youtube_url
instagram_url
behance_url
medium_url
stackoverflow_url
gitlab_url
twitch_url
mastodon_url
website_url])
.update_all(display_area: "settings_only")
ProfileField.where.not(display_area: "settings_only").update_all(display_area: "header")
ProfileField.where(attribute_name:
%w[currently_hacking_on currently_learning mostly_work_with available_for])
.update_all(display_area: "left_sidebar")
end
end
end

View file

@ -14,7 +14,7 @@ RSpec.describe ApplicationHelper, type: :helper do
expect(user.darker_color).to eq Color::CompareHex.new(described_class::USER_COLORS).brightness
expect(user.username).to eq "[deleted user]"
expect(user.name).to eq "[Deleted User]"
expect(user.summary).to be_nil
expect(user.tag_line).to be_nil
expect(user.twitter_username).to be_nil
expect(user.github_username).to be_nil
end

View file

@ -1,13 +0,0 @@
require "rails_helper"
require Rails.root.join(
"lib/data_update_scripts/20201223013903_set_proper_display_area_for_profile_fields.rb",
)
describe DataUpdateScripts::SetProperDisplayAreaForProfileFields do
it "migrates profile fields to proper areas" do
# Run the script
described_class.new.run
expect(ProfileField.find_by(attribute_name: "education").display_area).to eq("header")
expect(ProfileField.find_by(attribute_name: "currently_learning").display_area).to eq("left_sidebar")
end
end

View file

@ -48,5 +48,17 @@ RSpec.describe ProfileField, type: :model do
field = create(:profile_field, label: "Test? Test! 1")
expect(field.attribute_name).to eq "test_test1"
end
describe "#maximum_header_field_count" do
it "limits the number of header fields" do
count = [0, described_class::HEADER_FIELD_LIMIT - described_class.header.count].max
create_list(:profile_field, count, :header)
expected_message = /#{Regexp.quote(ProfileField::HEADER_LIMIT_MESSAGE)}/
expect { create(:profile_field, :header) }
.to raise_error(ActiveRecord::RecordInvalid, expected_message)
end
end
end
end

View file

@ -413,7 +413,7 @@ RSpec.describe User, type: :model do
user = described_class.find(create(:user, :ignore_mailchimp_subscribe_callback).id)
sidekiq_assert_no_enqueued_jobs(only: Users::SubscribeToMailchimpNewsletterWorker) do
user.update(website_url: "http://example.com")
user.update(credits_count: 100)
end
end
end
@ -827,14 +827,5 @@ RSpec.describe User, type: :model do
expect(user.profile).to be_present
expect(user.profile).to respond_to(:available_for)
end
it "propagates changes of unmapped attributes to the profile model", :aggregate_failures do
expect do
user.update(available_for: "profile migrations")
end.to change { user.profile.reload.available_for }.from(nil).to("profile migrations")
# Changes were also persisted in the users table
expect(user.reload.available_for).to eq "profile migrations"
end
end
end

View file

@ -58,12 +58,15 @@ RSpec.describe "Api::V0::Organizations", type: :request do
expect(response_org_users["type_of"]).to eq("user")
%w[
id username name summary twitter_username github_username website_url location
].each do |attr|
%w[id username name twitter_username github_username].each do |attr|
expect(response_org_users[attr]).to eq(org_user.public_send(attr))
end
org_user_profile = org_user.profile
%w[summary location website_url].each do |attr|
expect(response_org_users[attr]).to eq(org_user_profile.public_send(attr))
end
expect(response_org_users["joined_at"]).to eq(org_user.created_at.strftime("%b %e, %Y"))
expect(response_org_users["profile_image"]).to eq(Images::Profile.call(org_user.profile_image_url, length: 320))
end
@ -104,10 +107,12 @@ RSpec.describe "Api::V0::Organizations", type: :request do
expect(response_listing["tag_list"]).to eq(listing.cached_tag_list)
expect(response_listing["tags"]).to match_array(listing.tag_list)
%w[name username twitter_username github_username website_url].each do |attr|
%w[name username twitter_username github_username].each do |attr|
expect(response_listing["user"][attr]).to eq(org_user.public_send(attr))
end
expect(response_listing["organization"]["website_url"]).to eq(org_user.profile.website_url)
%w[name username slug].each do |attr|
expect(response_listing["organization"][attr]).to eq(organization.public_send(attr))
end
@ -149,10 +154,12 @@ RSpec.describe "Api::V0::Organizations", type: :request do
expect(response_article["tag_list"]).to match_array(article.tag_list)
%w[name username twitter_username github_username website_url].each do |attr|
%w[name username twitter_username github_username].each do |attr|
expect(response_article["user"][attr]).to eq(org_user.public_send(attr))
end
expect(response_article["user"]["website_url"]).to eq(org_user.profile.website_url)
%w[name username slug].each do |attr|
expect(response_article["organization"][attr]).to eq(organization.public_send(attr))
end

View file

@ -2,7 +2,12 @@ require "rails_helper"
RSpec.describe "Api::V0::Users", type: :request do
describe "GET /api/users/:id" do
let(:user) { create(:user, summary: "Something something", profile_image: "") }
let!(:user) do
create(:user,
profile_image: "",
_skip_creating_profile: true,
profile: create(:profile, summary: "Something something"))
end
it "returns 404 if the user id is not found" do
get api_user_path("invalid-id")
@ -39,12 +44,14 @@ RSpec.describe "Api::V0::Users", type: :request do
expect(response_user["type_of"]).to eq("user")
%w[
id username name summary twitter_username github_username website_url location
].each do |attr|
%w[id username name twitter_username github_username].each do |attr|
expect(response_user[attr]).to eq(user.public_send(attr))
end
%w[summary website_url location].each do |attr|
expect(response_user[attr]).to eq(user.profile.public_send(attr))
end
expect(response_user["joined_at"]).to eq(user.created_at.strftime("%b %e, %Y"))
expect(response_user["profile_image"]).to eq(Images::Profile.call(user.profile_image_url, length: 320))
end
@ -67,12 +74,14 @@ RSpec.describe "Api::V0::Users", type: :request do
expect(response_user["type_of"]).to eq("user")
%w[
id username name summary twitter_username github_username website_url location
].each do |attr|
%w[id username name twitter_username github_username].each do |attr|
expect(response_user[attr]).to eq(user.public_send(attr))
end
%w[summary website_url location].each do |attr|
expect(response_user[attr]).to eq(user.profile.public_send(attr))
end
expect(response_user["joined_at"]).to eq(user.created_at.strftime("%b %e, %Y"))
expect(response_user["profile_image"]).to eq(Images::Profile.call(user.profile_image_url, length: 320))
end
@ -85,12 +94,14 @@ RSpec.describe "Api::V0::Users", type: :request do
expect(response_user["type_of"]).to eq("user")
%w[
id username name summary twitter_username github_username website_url location
].each do |attr|
%w[id username name twitter_username github_username].each do |attr|
expect(response_user[attr]).to eq(user.public_send(attr))
end
%w[summary website_url location].each do |attr|
expect(response_user[attr]).to eq(user.profile.public_send(attr))
end
expect(response_user["joined_at"]).to eq(user.created_at.strftime("%b %e, %Y"))
expect(response_user["profile_image"]).to eq(Images::Profile.call(user.profile_image_url, length: 320))
end

View file

@ -98,19 +98,13 @@ RSpec.describe "UserProfiles", type: :request do
expect(response.body.split("Whoaaaa").first).to include "crayons-layout__sidebar-left"
end
it "does not render settings_only on page" do
create(:profile_field, label: "whoaaaa", display_area: "settings_only")
get "/#{user.username}"
expect(response.body).not_to include "Whoaaaa"
end
it "does not render special display header elements naively" do
user.location = "hawaii"
user.profile.location = "hawaii"
user.save
get "/#{user.username}"
# Does not include the word, but does include the SVG
expect(response.body).not_to include "<p>Location</p>"
expect(response.body).to include user.location
expect(response.body).to include user.profile.location
expect(response.body).to include "M18.364 17.364L12 23.728l-6.364-6.364a9 9 0 1112.728 0zM12 13a2 2 0 100-4 2 2 0"
end

View file

@ -238,14 +238,9 @@ RSpec.describe "UserSettings", type: :request do
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" } }
put "/users/#{user.id}", params: { user: { tab: "profile", username: "new_username" } }
expect(user.reload.profile_updated_at).to be > 2.minutes.ago
end

View file

@ -38,8 +38,7 @@ RSpec.describe "UserShow", type: :request do
"image" => Images::Profile.call(user.profile_image_url, length: 320),
"name" => user.name,
"email" => user.email,
"description" => user.summary,
"alumniOf" => user.education,
"description" => user.tag_line,
)
end
# rubocop:enable RSpec/ExampleLength

View file

@ -38,7 +38,7 @@ RSpec.describe "Users", type: :request do
"id" => suggested_user.id,
"name" => suggested_user.name,
"username" => suggested_user.username,
"summary" => suggested_user.summary,
"summary" => suggested_user.profile.summary,
"profile_image_url" => Images::Profile.call(suggested_user.profile_image_url, length: 90),
"following" => false,
)
@ -86,7 +86,7 @@ RSpec.describe "Users", type: :request do
"id" => suggested_user.id,
"name" => suggested_user.name,
"username" => suggested_user.username,
"summary" => suggested_user.summary,
"summary" => suggested_user.profile.summary,
"profile_image_url" => Images::Profile.call(suggested_user.profile_image_url, length: 90),
"following" => false,
)

View file

@ -1,7 +1,12 @@
require "rails_helper"
RSpec.describe "UsersOnboarding", type: :request do
let(:user) { create(:user, saw_onboarding: false, location: "Llama Town") }
let!(:user) do
create(:user,
saw_onboarding: false,
_skip_creating_profile: true,
profile: create(:profile, location: "Llama Town"))
end
describe "PATCH /onboarding_update" do
context "when signed in" do

View file

@ -16,9 +16,8 @@ RSpec.describe Profiles::Update, type: :service do
let(:user) { profile.user }
it "correctly typecasts new attributes", :aggregate_failures do
described_class.call(user, profile: { location: 123, education: "false" })
expect(user.location).to eq "123"
expect(profile.education).to eq "false"
described_class.call(user, profile: { location: 123 })
expect(user.profile.location).to eq "123"
end
it "removes old attributes from the profile" do
@ -87,7 +86,7 @@ RSpec.describe Profiles::Update, type: :service do
it "enqueues resave articles job when changing summary" do
sidekiq_assert_resave_article_worker(user) do
described_class.call(user, profile: { summary: "#{user.summary} changed" })
described_class.call(user, profile: { summary: "#{user.profile.summary} changed" })
end
end

View file

@ -32,7 +32,6 @@ seeder.create_if_doesnt_exist(User, "email", "admin@forem.local") do
email: "admin@forem.local",
username: "Admin_McAdmin",
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
website_url: Faker::Internet.url,
confirmed_at: Time.current,
password: "password",
password_confirmation: "password",
@ -51,6 +50,7 @@ seeder.create_if_doesnt_exist(User, "email", "admin@forem.local") do
work: "Software developer at Company",
location: "Edinburgh",
education: "University of Life",
website_url: Faker::Internet.url,
)
user.add_role(:super_admin)
@ -71,7 +71,6 @@ seeder.create_if_doesnt_exist(User, "email", "trusted-user-1@forem.local") do
email: "trusted-user-1@forem.local",
username: "trusted_user_1",
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
website_url: Faker::Internet.url,
confirmed_at: Time.current,
password: "password",
password_confirmation: "password",
@ -84,6 +83,8 @@ seeder.create_if_doesnt_exist(User, "email", "trusted-user-1@forem.local") do
email_follower_notifications: false,
)
user.profile.update(website_url: Faker::Internet.url)
user.add_role(:trusted)
end
@ -113,9 +114,7 @@ seeder.create_if_doesnt_exist(User, "email", "change-password-user@forem.com") d
name: "Change Password User",
email: "change-password-user@forem.com",
username: "changepassworduser",
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
website_url: Faker::Internet.url,
confirmed_at: Time.current,
password: "password",
password_confirmation: "password",
@ -127,6 +126,10 @@ seeder.create_if_doesnt_exist(User, "email", "change-password-user@forem.com") d
email_comment_notifications: false,
email_follower_notifications: false,
)
user.profile.update(
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
website_url: Faker::Internet.url,
)
user
end
@ -137,9 +140,7 @@ seeder.create_if_doesnt_exist(User, "email", "article-editor-v1-user@forem.com")
name: "Article Editor v1 User",
email: "article-editor-v1-user@forem.local",
username: "article_editor_v1_user",
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
website_url: Faker::Internet.url,
confirmed_at: Time.current,
password: "password",
password_confirmation: "password",
@ -152,6 +153,10 @@ seeder.create_if_doesnt_exist(User, "email", "article-editor-v1-user@forem.com")
email_comment_notifications: false,
email_follower_notifications: false,
)
user.profile.update(
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
website_url: Faker::Internet.url,
)
user
end
@ -162,9 +167,7 @@ seeder.create_if_doesnt_exist(User, "email", "article-editor-v2-user@forem.com")
name: "Article Editor v2 User",
email: "article-editor-v2-user@forem.local",
username: "article_editor_v2_user",
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
website_url: Faker::Internet.url,
confirmed_at: Time.current,
password: "password",
password_confirmation: "password",
@ -176,6 +179,10 @@ seeder.create_if_doesnt_exist(User, "email", "article-editor-v2-user@forem.com")
email_comment_notifications: false,
email_follower_notifications: false,
)
user.profile.update(
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
website_url: Faker::Internet.url,
)
user
end
@ -186,9 +193,7 @@ chat_user_1 = seeder.create_if_doesnt_exist(User, "email", "chat-user-1@forem.lo
name: "Chat user 1",
email: "chat-user-1@forem.local",
username: "chat_user_1",
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
website_url: Faker::Internet.url,
confirmed_at: Time.current,
password: "password",
password_confirmation: "password",
@ -200,6 +205,10 @@ chat_user_1 = seeder.create_if_doesnt_exist(User, "email", "chat-user-1@forem.lo
email_comment_notifications: false,
email_follower_notifications: false,
)
user.profile.update(
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
website_url: Faker::Internet.url,
)
user
end
@ -210,9 +219,7 @@ chat_user_2 = seeder.create_if_doesnt_exist(User, "email", "chat-user-2@forem.lo
name: "Chat user 2",
email: "chat-user-2@forem.local",
username: "chat_user_2",
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
website_url: Faker::Internet.url,
confirmed_at: Time.current,
password: "password",
password_confirmation: "password",
@ -224,6 +231,10 @@ chat_user_2 = seeder.create_if_doesnt_exist(User, "email", "chat-user-2@forem.lo
email_comment_notifications: false,
email_follower_notifications: false,
)
user.profile.update(
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
website_url: Faker::Internet.url,
)
user
end
@ -233,9 +244,7 @@ seeder.create_if_doesnt_exist(User, "email", "notifications-user@forem.com") do
name: "Notifications User",
email: "notifications-user@forem.local",
username: "notifications_user",
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
website_url: Faker::Internet.url,
confirmed_at: Time.current,
password: "password",
password_confirmation: "password",
@ -247,6 +256,10 @@ seeder.create_if_doesnt_exist(User, "email", "notifications-user@forem.com") do
email_comment_notifications: false,
email_follower_notifications: false,
)
user.profile.update(
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
website_url: Faker::Internet.url,
)
follow = admin_user.follows.create!(followable: user)
Notification.send_new_follower_notification_without_delay(follow)
@ -259,9 +272,7 @@ seeder.create_if_doesnt_exist(User, "email", "liquid-tags-user@forem.com") do
name: "Liquid tags User",
email: "liquid-tags-user@forem.local",
username: "liquid_tags_user",
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
website_url: Faker::Internet.url,
confirmed_at: Time.current,
password: "password",
password_confirmation: "password",
@ -273,6 +284,10 @@ seeder.create_if_doesnt_exist(User, "email", "liquid-tags-user@forem.com") do
email_comment_notifications: false,
email_follower_notifications: false,
)
liquid_tags_user.profile.update(
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
website_url: Faker::Internet.url,
)
admin_user.follows.create!(followable: liquid_tags_user)
end
@ -389,9 +404,7 @@ seeder.create_if_doesnt_exist(User, "email", "series-user@forem.com") do
name: "Series User",
email: "series-user@forem.local",
username: "series_user",
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
website_url: Faker::Internet.url,
confirmed_at: Time.current,
password: "password",
password_confirmation: "password",
@ -404,6 +417,7 @@ seeder.create_if_doesnt_exist(User, "email", "series-user@forem.com") do
work: "Software developer at Company",
location: "Edinburgh",
education: "University of Life",
website_url: Faker::Internet.url,
)
series_user.notification_setting.update(
email_comment_notifications: false,