Split Settings::UserExperience (#13495)

* Add Settings::UserExperience

* Add DUS

* Update usage

* Add controller

* Add e2e test

* Fix specs
This commit is contained in:
Michael Kohl 2021-04-28 13:29:25 +07:00 committed by GitHub
parent 02037b1e99
commit 1c7de11bef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 327 additions and 123 deletions

View file

@ -0,0 +1,34 @@
module Admin
module Settings
class UserExperiencesController < Admin::ApplicationController
def create
errors = upsert_config(settings_params)
if errors.none?
Audit::Logger.log(:internal, current_user, params.dup)
redirect_to admin_config_path, notice: "Site configuration was successfully updated."
else
redirect_to admin_config_path, alert: "😭 #{errors.to_sentence}"
end
end
def settings_params
params
.require(:settings_user_experience)
.permit(*::Settings::UserExperience.keys)
end
def upsert_config(configs)
errors = []
configs.each do |key, value|
::Settings::UserExperience.public_send("#{key}=", value) if value.present?
rescue ActiveRecord::RecordInvalid => e
errors << e.message
next
end
errors
end
end
end
end

View file

@ -51,7 +51,7 @@ class ApplicationController < ActionController::Base
def verify_private_forem
return if controller_name.in?(PUBLIC_CONTROLLERS)
return if self.class.module_parent.to_s == "Admin"
return if user_signed_in? || SiteConfig.public
return if user_signed_in? || Settings::UserExperience.public
if api_action?
authenticate!
@ -139,7 +139,7 @@ class ApplicationController < ActionController::Base
def feed_style_preference
# TODO: Future functionality will let current_user override this value with UX preferences
# if current_user exists and has a different preference.
SiteConfig.feed_style
Settings::UserExperience.feed_style
end
helper_method :feed_style_preference

View file

@ -32,7 +32,8 @@ class ArticlesController < ApplicationController
.includes(:user)
else
@articles
.where(featured: true).or(@articles.where(score: SiteConfig.home_feed_minimum_score..))
.where(featured: true)
.or(@articles.where(score: Settings::UserExperience.home_feed_minimum_score..))
.includes(:user)
end

View file

@ -18,7 +18,8 @@ module CachingHeaders
stale_while_revalidate: nil,
stale_if_error: 26_400
)
return unless SiteConfig.public # Only public forems should be edge-cached based on current functionality.
# Only public forems should be edge-cached based on current functionality.
return unless Settings::UserExperience.public
request.session_options[:skip] = true # no cookies

View file

@ -22,7 +22,7 @@ module Stories
end
def signed_in_base_feed
if SiteConfig.feed_strategy == "basic"
if Settings::UserExperience.feed_strategy == "basic"
Articles::Feeds::Basic.new(user: current_user, page: @page, tag: params[:tag]).feed
else
optimized_signed_in_feed
@ -30,7 +30,7 @@ module Stories
end
def signed_out_base_feed
if SiteConfig.feed_strategy == "basic"
if Settings::UserExperience.feed_strategy == "basic"
Articles::Feeds::Basic.new(user: nil, page: @page, tag: params[:tag]).feed
else
Articles::Feeds::LargeForemExperimental.new(user: current_user, page: @page, tag: params[:tag])

View file

@ -135,7 +135,7 @@ class StoriesController < ApplicationController
@num_published_articles = if @tag_model.requires_approval?
@tag_model.articles.published.where(approved: true).count
elsif SiteConfig.feed_strategy == "basic"
elsif Settings::UserExperience.feed_strategy == "basic"
tagged_count
else
Rails.cache.fetch("article-cached-tagged-count-#{@tag}", expires_in: 2.hours) do
@ -353,7 +353,7 @@ class StoriesController < ApplicationController
elsif params[:timeframe] == "latest"
@stories.where("score > ?", -20).order(published_at: :desc)
else
@stories.order(hotness_score: :desc).where("score >= ?", SiteConfig.home_feed_minimum_score)
@stories.order(hotness_score: :desc).where("score >= ?", Settings::UserExperience.home_feed_minimum_score)
end
end
@ -485,6 +485,6 @@ class StoriesController < ApplicationController
end
def tagged_count
@tag_model.articles.published.where("score >= ?", SiteConfig.tag_feed_minimum_score).count
@tag_model.articles.published.where("score >= ?", Settings::UserExperience.tag_feed_minimum_score).count
end
end

View file

@ -50,7 +50,7 @@ class UserDecorator < ApplicationDecorator
end
def config_font_name
config_font.gsub("default", SiteConfig.default_font)
config_font.gsub("default", Settings::UserExperience.default_font)
end
def config_body_class

View file

@ -0,0 +1,32 @@
module Constants
module Settings
module UserExperience
DETAILS = {
default_font: {
description: "Determines the default reading font (registered users can change this in their UX settings)"
},
feed_strategy: {
description: "Determines the main feed algorithm approach the app takes: basic or large_forem_experimental
(which should only be used for 10k+ member communities)",
placeholder: "basic"
},
feed_style: {
description: "Determines which default feed the users sees (rich content, more minimal, etc.)",
placeholder: "basic, rich, or compact"
},
home_feed_minimum_score: {
description: "Minimum score needed for a post to show up on the unauthenticated home page.",
placeholder: "0"
},
primary_brand_color_hex: {
description: "Determines background/border of buttons etc. Must be dark enough to contrast with white text.",
placeholder: "#0a0a0a"
},
tag_feed_minimum_score: {
description: "Minimum score needed for a post to show up on default tag page.",
placeholder: "0"
}
}.freeze
end
end
end

View file

@ -22,9 +22,6 @@ module Constants
placeholder: ""
}
},
default_font: {
description: "Determines the default Base Reading Font (registered users can change this in their UX settings)"
},
email_addresses: {
description: "Email address",
placeholder: ""
@ -33,19 +30,6 @@ module Constants
description: "Used as the site favicon",
placeholder: IMAGE_PLACEHOLDER
},
feed_strategy: {
description: "Determines the main feed algorithm approach the app takes: basic or large_forem_experimental
(which should only be used for 10k+ member communities)",
placeholder: "basic"
},
feed_style: {
description: "Determines which default feed the users sees (rich content, more minimal, etc.)",
placeholder: "basic, rich, or compact"
},
primary_brand_color_hex: {
description: "Determines background/border of buttons etc. Must be dark enough to contrast with white text.",
placeholder: "#0a0a0a"
},
ga_tracking_id: {
description: "Google Analytics Tracking ID, e.g. UA-71991000-1",
placeholder: ""
@ -54,10 +38,6 @@ module Constants
description: "Used to authenticate with your health check endpoints.",
placeholder: "a secure token"
},
home_feed_minimum_score: {
description: "Minimum score needed for a post to show up on the unauthenticated home page.",
placeholder: "0"
},
logo_png: {
description: "Used as a fallback to the SVG. Recommended minimum of 512x512px for PWA support",
placeholder: IMAGE_PLACEHOLDER
@ -148,10 +128,6 @@ module Constants
description: "Always show suggested users as suggested people to follow even when " \
"auto-suggestion is available"
},
tag_feed_minimum_score: {
description: "Minimum score needed for a post to show up on default tag page.",
placeholder: "0"
},
twitter_hashtag: {
description: "Used as the twitter hashtag of the community",
placeholder: "#DEVCommunity"

View file

@ -0,0 +1,30 @@
module Settings
# Basic UX settings that can be overriden by individual user preferences.
class UserExperience < RailsSettings::Base
self.table_name = :settings_user_experiences
HEX_COLOR_REGEX = /\A#(\h{6}|\h{3})\z/.freeze
# The configuration is cached, change this if you want to force update
# the cache, or call Settings::UserExperience.clear_cache
cache_prefix { "v1" }
# The default font for all users that have not chosen a custom font yet
field :default_font, type: :string, default: "sans_serif"
field :feed_strategy, type: :string, default: "basic"
# basic (current default), rich (cover image on all posts), compact (more minimal)
field :feed_style, type: :string, default: "basic"
field :home_feed_minimum_score, type: :integer, default: 0
field :primary_brand_color_hex, type: :string, default: "#3b49df", validates: {
format: {
with: HEX_COLOR_REGEX,
message: "must be be a 3 or 6 character hex (starting with #)"
},
color_contrast: true
}
# a non-public forem will redirect all unauthenticated pages to the registration page.
# a public forem could have more fine-grained authentication (listings ar private etc.) in future
field :public, type: :boolean, default: 0
field :tag_feed_minimum_score, type: :integer, default: 0
end
end

View file

@ -178,6 +178,8 @@ class SiteConfig < RailsSettings::Base
# Tags
field :sidebar_tags, type: :array, default: %w[]
# NOTE: @citizen428 - These will be removed once we migrated to Settings::UserExperience
# across the whole fleet.
# User Experience
# These are the default UX settings, which can be overridded by individual user preferences.
# basic (current default), rich (cover image on all posts), compact (more minimal)

View file

@ -124,7 +124,7 @@ module Articles
else
hot_stories = Article.published.limited_column_select
.page(@page).per(@number_of_articles)
.where("score >= ? OR featured = ?", SiteConfig.home_feed_minimum_score, true)
.where("score >= ? OR featured = ?", Settings::UserExperience.home_feed_minimum_score, true)
.order(hotness_score: :desc)
featured_story = hot_stories.where.not(main_image: nil).first
end

View file

@ -1162,7 +1162,7 @@
</div>
<% end %>
<%= form_for(SiteConfig.new, url: admin_config_path) do |f| %>
<%= form_for(Settings::UserExperience.new, url: admin_settings_user_experiences_path) do |f| %>
<div class=" card mt-3">
<%= render partial: "admin/shared/card_header",
locals: {
@ -1174,61 +1174,61 @@
<div id="uxBodyContainer" class="card-body collapse hide" aria-labelledby="uxBodyContainer">
<fieldset class="grid gap-4">
<div class="crayons-fieldx">
<%= admin_config_label :feed_style %>
<%= admin_config_description Constants::SiteConfig::DETAILS[:feed_style][:description] %>
<%= admin_config_label :feed_style, model: Settings::UserExperience %>
<%= admin_config_description Constants::Settings::UserExperience::DETAILS[:feed_style][:description] %>
<%= f.text_field :feed_style,
class: "crayons-textfield",
value: SiteConfig.feed_style,
placeholder: Constants::SiteConfig::DETAILS[:feed_style][:placeholder] %>
value: Settings::UserExperience.feed_style,
placeholder: Constants::Settings::UserExperience::DETAILS[:feed_style][:placeholder] %>
</div>
<div class="crayons-field">
<%= admin_config_label :feed_strategy %>
<%= admin_config_description Constants::SiteConfig::DETAILS[:feed_strategy][:description] %>
<%= admin_config_label :feed_strategy, model: Settings::UserExperience %>
<%= admin_config_description Constants::Settings::UserExperience::DETAILS[:feed_strategy][:description] %>
<%= f.text_field :feed_strategy,
class: "crayons-textfield",
value: SiteConfig.feed_strategy,
placeholder: Constants::SiteConfig::DETAILS[:feed_strategy][:placeholder] %>
value: Settings::UserExperience.feed_strategy,
placeholder: Constants::Settings::UserExperience::DETAILS[:feed_strategy][:placeholder] %>
</div>
<div class="crayons-field">
<%= admin_config_label :tag_feed_minimum_score %>
<%= admin_config_description Constants::SiteConfig::DETAILS[:tag_feed_minimum_score][:description] %>
<%= admin_config_label :tag_feed_minimum_score, model: Settings::UserExperience %>
<%= admin_config_description Constants::Settings::UserExperience::DETAILS[:tag_feed_minimum_score][:description] %>
<%= f.number_field :tag_feed_minimum_score,
class: "crayons-textfield",
value: SiteConfig.tag_feed_minimum_score,
placeholder: Constants::SiteConfig::DETAILS[:tag_feed_minimum_score][:placeholder] %>
value: Settings::UserExperience.tag_feed_minimum_score,
placeholder: Constants::Settings::UserExperience::DETAILS[:tag_feed_minimum_score][:placeholder] %>
</div>
<div class="crayons-field">
<%= admin_config_label :home_feed_minimum_score %>
<%= admin_config_description Constants::SiteConfig::DETAILS[:home_feed_minimum_score][:description] %>
<%= admin_config_label :home_feed_minimum_score, model: Settings::UserExperience %>
<%= admin_config_description Constants::Settings::UserExperience::DETAILS[:home_feed_minimum_score][:description] %>
<%= f.number_field :home_feed_minimum_score,
class: "crayons-textfield",
value: SiteConfig.home_feed_minimum_score,
placeholder: Constants::SiteConfig::DETAILS[:home_feed_minimum_score][:placeholder] %>
value: Settings::UserExperience.home_feed_minimum_score,
placeholder: Constants::Settings::UserExperience::DETAILS[:home_feed_minimum_score][:placeholder] %>
</div>
<div class="crayons-field">
<%= admin_config_label :default_font %>
<%= admin_config_description Constants::SiteConfig::DETAILS[:default_font][:description] %>
<%= select_tag "site_config[default_font]",
<%= admin_config_label :default_font, model: Settings::UserExperience %>
<%= admin_config_description Constants::Settings::UserExperience::DETAILS[:default_font][:description] %>
<%= select_tag "settings_user_experience[default_font]",
options_for_select(
[%w[sans-serif sans_serif], %w[serif serif], %w[open-dyslexic open_dyslexic]],
SiteConfig.default_font,
Settings::UserExperience.default_font,
),
multiple: false,
class: "selectpicker" %>
</div>
<div class="crayons-field">
<%= admin_config_label :primary_brand_color_hex %>
<%= admin_config_description Constants::SiteConfig::DETAILS[:primary_brand_color_hex][:description] %>
<%= admin_config_label :primary_brand_color_hex, model: Settings::UserExperience %>
<%= admin_config_description Constants::Settings::UserExperience::DETAILS[:primary_brand_color_hex][:description] %>
<%= f.color_field :primary_brand_color_hex,
class: "crayons-color-selector",
value: SiteConfig.primary_brand_color_hex,
value: Settings::UserExperience.primary_brand_color_hex,
pattern: "^#+([a-fA-F0-9]{6})$",
placeholder: Constants::SiteConfig::DETAILS[:primary_brand_color_hex][:placeholder] %>
placeholder: Constants::Settings::UserExperience::DETAILS[:primary_brand_color_hex][:placeholder] %>
</div>
<div class="crayons-field crayons-field--checkbox">
<%= f.check_box :public, checked: SiteConfig.public, class: "crayons-checkbox" %>
<%= f.check_box :public, checked: Settings::UserExperience.public, class: "crayons-checkbox" %>
<div class="mt-0">
<%= admin_config_label :public %>
<%= admin_config_label :public, model: Settings::UserExperience %>
<p class="crayons-field__description">
Are most of the site pages (e.g. posts, profiles) public? <strong>Please take precaution when changing this setting.</strong>
</p>

View file

@ -48,7 +48,7 @@
<% cache(release_adjusted_cache_key("top-html-and-config--#{user_signed_in?}")) do %>
<body
data-user-status="<%= user_logged_in_status %>"
class="<%= SiteConfig.default_font.tr("_", "-") %>-article-body default-header"
class="<%= Settings::UserExperience.default_font.tr("_", "-") %>-article-body default-header"
data-pusher-key="<%= ApplicationConfig["PUSHER_KEY"] %>"
data-app-domain="<%= ChatChannel.urlsafe_encoded_app_domain %>"
data-honeybadger-key="<%= ApplicationConfig["HONEYBADGER_JS_API_KEY"] %>"
@ -60,10 +60,10 @@
<div id="body-styles">
<style>
:root {
--accent-brand: <%= SiteConfig.primary_brand_color_hex %>;
--accent-brand-darker: <%= Color::CompareHex.new([SiteConfig.primary_brand_color_hex]).brightness(0.85) %>;
--accent-brand-lighter: <%= Color::CompareHex.new([SiteConfig.primary_brand_color_hex]).brightness(1.1) %>;
--accent-brand-a10: <%= Color::CompareHex.new([SiteConfig.primary_brand_color_hex]).opacity(0.1) %>;
--accent-brand: <%= Settings::UserExperience.primary_brand_color_hex %>;
--accent-brand-darker: <%= Color::CompareHex.new([Settings::UserExperience.primary_brand_color_hex]).brightness(0.85) %>;
--accent-brand-lighter: <%= Color::CompareHex.new([Settings::UserExperience.primary_brand_color_hex]).brightness(1.1) %>;
--accent-brand-a10: <%= Color::CompareHex.new([Settings::UserExperience.primary_brand_color_hex]).opacity(0.1) %>;
}
</style>
</div>

View file

@ -64,6 +64,7 @@ Rails.application.routes.draw do
resources :communities, only: [:create]
resources :mascots, only: [:create]
resources :rate_limits, only: [:create]
resources :user_experiences, only: [:create]
end
namespace :users do
resources :gdpr_delete_requests, only: %i[index destroy]

View file

@ -0,0 +1,53 @@
describe('User experience Section', () => {
beforeEach(() => {
cy.testSetup();
cy.fixture('users/adminUser.json').as('user');
cy.get('@user').then((user) => {
cy.loginUser(user);
});
});
describe('default font', () => {
it('can change the default font', () => {
cy.get('@user').then(({ username }) => {
cy.visit('/admin/config');
cy.get('#new_settings_user_experience').as('userExperienceSectionForm');
cy.get('@userExperienceSectionForm')
.findByText('User Experience and Brand')
.click();
cy.get('@userExperienceSectionForm')
.get('#settings_user_experience_default_font')
// We need to use force here because the select is covered by another element.
.select('open-dyslexic', { force: true });
cy.get('@userExperienceSectionForm')
.findByPlaceholderText('Confirmation text')
.type(
`My username is @${username} and this action is 100% safe and appropriate.`,
);
cy.get('@userExperienceSectionForm')
.findByText('Update Site Configuration')
.click();
cy.url().should('contains', '/admin/config');
cy.findByText('Site configuration was successfully updated.').should(
'be.visible',
);
// Page reloaded so need to get a new reference to the form.
cy.get('#new_settings_user_experience').as('userExperienceSectionForm');
cy.get('@userExperienceSectionForm')
.findByText('User Experience and Brand')
.click();
cy.get('#settings_user_experience_default_font').should(
'have.value',
'open_dyslexic',
);
});
});
});
});

View file

@ -0,0 +1,16 @@
class CreateSettingsUserExperiences < ActiveRecord::Migration[6.1]
def self.up
create_table :settings_user_experiences do |t|
t.string :var, null: false
t.text :value, null: true
t.timestamps
end
add_index :settings_user_experiences, :var, unique: true
end
def self.down
drop_table :settings_user_experiences
end
end

View file

@ -1111,6 +1111,14 @@ ActiveRecord::Schema.define(version: 2021_04_26_165234) do
t.index ["var"], name: "index_settings_rate_limits_on_var", unique: true
end
create_table "settings_user_experiences", force: :cascade do |t|
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.text "value"
t.string "var", null: false
t.index ["var"], name: "index_settings_user_experiences_on_var", unique: true
end
create_table "site_configs", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false

View file

@ -15,7 +15,7 @@ puts "Seeding with multiplication factor: #{SEEDS_MULTIPLIER}\n\n"
##############################################################################
# Default development site config if different from production scenario
SiteConfig.public = true
Settings::UserExperience.public = true
SiteConfig.waiting_on_first_user = false
Settings::Authentication.providers = Authentication::Providers.available
@ -24,7 +24,7 @@ Settings::Authentication.providers = Authentication::Providers.available
# Put forem into "starter mode"
if ENV["MODE"] == "STARTER"
SiteConfig.public = false
Settings::UserExperience.public = false
SiteConfig.waiting_on_first_user = true
puts "Seeding forem in starter mode to replicate new creator experience"
exit # We don't need any models if we're launching things from startup.

View file

@ -0,0 +1,11 @@
module DataUpdateScripts
class MoveUserExperienceSettings
def run
return if Settings::UserExperience.any?
Settings::UserExperience.editable_keys.each do |field|
Settings::UserExperience.public_send("#{field}=", SiteConfig.public_send(field))
end
end
end
end

View file

@ -109,14 +109,14 @@ RSpec.describe UserDecorator, type: :decorator do
it "replaces 'default' with font configured for the site in SiteConfig" do
expect(user.config_font).to eq("default")
%w[sans_serif serif open_dyslexic].each do |font|
allow(SiteConfig).to receive(:default_font).and_return(font)
allow(Settings::UserExperience).to receive(:default_font).and_return(font)
expect(user.decorate.config_font_name).to eq(font)
end
end
it "doesn't replace the user's custom selected font" do
user_comic_sans = create(:user, config_font: "comic_sans")
allow(SiteConfig).to receive(:default_font).and_return("open_dyslexic")
allow(Settings::UserExperience).to receive(:default_font).and_return("open_dyslexic")
expect(user_comic_sans.decorate.config_font_name).to eq("comic_sans")
end
end

View file

@ -0,0 +1,17 @@
require "rails_helper"
require Rails.root.join(
"lib/data_update_scripts/20210426023014_move_user_experience_settings.rb",
)
describe DataUpdateScripts::MoveUserExperienceSettings do
it "moves existing settings" do
allow(SiteConfig).to receive(:default_font).and_return("Comic Sans")
allow(SiteConfig).to receive(:primary_brand_color_hex).and_return("#000")
expect do
described_class.new.run
end
.to change(Settings::UserExperience, :default_font).to("Comic Sans")
.and change(Settings::UserExperience, :primary_brand_color_hex).to("#000")
end
end

View file

@ -685,6 +685,10 @@ RSpec.describe User, type: :model do
end
describe "theming properties" do
before do
allow(Settings::UserExperience).to receive(:default_font).and_return("sans-serif")
end
it "creates proper body class with defaults" do
classes = "default sans-serif-article-body trusted-status-#{user.trusted} #{user.config_navbar}-header"
expect(user.decorate.config_body_class).to eq(classes)

View file

@ -185,7 +185,7 @@ RSpec.configure do |config|
}).to_return(status: 200, body: "", headers: {})
allow(Settings::Community).to receive(:community_description).and_return("Some description")
allow(SiteConfig).to receive(:public).and_return(true)
allow(Settings::UserExperience).to receive(:public).and_return(true)
allow(SiteConfig).to receive(:waiting_on_first_user).and_return(false)
# Default to have field a field test available.

View file

@ -851,66 +851,82 @@ RSpec.describe "/admin/config", type: :request do
describe "User Experience" do
it "updates the feed_style" do
feed_style = "basic"
post "/admin/config", params: { site_config: { feed_style: feed_style },
confirmation: confirmation_message }
expect(SiteConfig.feed_style).to eq(feed_style)
post admin_settings_user_experiences_path, params: {
settings_user_experience: { feed_style: feed_style },
confirmation: confirmation_message
}
expect(Settings::UserExperience.feed_style).to eq(feed_style)
end
it "updates the feed_strategy" do
feed_strategy = "optimized"
post "/admin/config", params: { site_config: { feed_strategy: feed_strategy },
confirmation: confirmation_message }
expect(SiteConfig.feed_strategy).to eq(feed_strategy)
post admin_settings_user_experiences_path, params: {
settings_user_experience: { feed_strategy: feed_strategy },
confirmation: confirmation_message
}
expect(Settings::UserExperience.feed_strategy).to eq(feed_strategy)
end
it "updates the tag_feed_minimum_score" do
tag_feed_minimum_score = 3
post "/admin/config", params: { site_config: { tag_feed_minimum_score: tag_feed_minimum_score },
confirmation: confirmation_message }
expect(SiteConfig.tag_feed_minimum_score).to eq(tag_feed_minimum_score)
post admin_settings_user_experiences_path, params: {
settings_user_experience: { tag_feed_minimum_score: tag_feed_minimum_score },
confirmation: confirmation_message
}
expect(Settings::UserExperience.tag_feed_minimum_score).to eq(tag_feed_minimum_score)
end
it "updates the home_feed_minimum_score" do
home_feed_minimum_score = 5
post "/admin/config", params: { site_config: { home_feed_minimum_score: home_feed_minimum_score },
confirmation: confirmation_message }
expect(SiteConfig.home_feed_minimum_score).to eq(home_feed_minimum_score)
post admin_settings_user_experiences_path, params: {
settings_user_experience: { home_feed_minimum_score: home_feed_minimum_score },
confirmation: confirmation_message
}
expect(Settings::UserExperience.home_feed_minimum_score).to eq(home_feed_minimum_score)
end
it "updates the brand color if proper hex" do
hex = "#0a0a0a" # dark enough
post "/admin/config", params: { site_config: { primary_brand_color_hex: hex },
confirmation: confirmation_message }
expect(SiteConfig.primary_brand_color_hex).to eq(hex)
post admin_settings_user_experiences_path, params: {
settings_user_experience: { primary_brand_color_hex: hex },
on: confirmation_message
}
expect(Settings::UserExperience.primary_brand_color_hex).to eq(hex)
end
it "does not update brand color if hex not contrasting enough" do
hex = "#bd746f" # not dark enough
post "/admin/config", params: { site_config: { primary_brand_color_hex: hex },
confirmation: confirmation_message }
expect(SiteConfig.primary_brand_color_hex).not_to eq(hex)
expect(Settings::UserExperience.primary_brand_color_hex).not_to eq(hex)
end
it "does not update brand color if hex not a hex with proper format" do
hex = "0a0a0a" # dark enough, but not proper format
post "/admin/config", params: { site_config: { primary_brand_color_hex: hex },
confirmation: confirmation_message }
expect(SiteConfig.primary_brand_color_hex).not_to eq(hex)
post admin_settings_user_experiences_path, params: {
settings_user_experience: { primary_brand_color_hex: hex },
confirmation: confirmation_message
}
expect(Settings::UserExperience.primary_brand_color_hex).not_to eq(hex)
end
it "updates public to true" do
is_public = true
post "/admin/config", params: { site_config: { public: is_public },
confirmation: confirmation_message }
expect(SiteConfig.public).to eq(is_public)
post admin_settings_user_experiences_path, params: {
settings_user_experience: { public: is_public },
confirmation: confirmation_message
}
expect(Settings::UserExperience.public).to eq(is_public)
end
it "updates public to false" do
allow(SiteConfig).to receive(:public).and_return(false)
allow(Settings::UserExperience).to receive(:public).and_return(false)
is_public = false
post "/admin/config", params: { site_config: { public: is_public },
confirmation: confirmation_message }
expect(SiteConfig.public).to eq(is_public)
post admin_settings_user_experiences_path, params: {
settings_user_experience: { public: is_public },
confirmation: confirmation_message
}
expect(Settings::UserExperience.public).to eq(is_public)
end
end

View file

@ -27,7 +27,7 @@ RSpec.describe "Api::V0::Users", type: :request do
end
it "returns unauthenticated if no authentication and site config is set to private" do
allow(SiteConfig).to receive(:public).and_return(false)
allow(Settings::UserExperience).to receive(:public).and_return(false)
get api_user_path("by_username"), params: { url: user.username }
expect(response).to have_http_status(:unauthorized)
end
@ -78,7 +78,7 @@ RSpec.describe "Api::V0::Users", type: :request do
end
it "returns 200 if no authentication and site config is set to private but user is authenticated" do
allow(SiteConfig).to receive(:public).and_return(false)
allow(Settings::UserExperience).to receive(:public).and_return(false)
get me_api_users_path, params: { access_token: access_token.token }
response_user = response.parsed_body

View file

@ -65,7 +65,9 @@ RSpec.describe "Articles", type: :request do
context "when :username param is not given" do
let!(:featured_article) { create(:article, featured: true) }
let!(:not_featured_article) { create(:article, featured: false, score: SiteConfig.home_feed_minimum_score - 1) }
let!(:not_featured_article) do
create(:article, featured: false, score: Settings::UserExperience.home_feed_minimum_score - 1)
end
before { get feed_path }
@ -157,25 +159,25 @@ RSpec.describe "Articles", type: :request do
context "with scored articles" do
before do
allow(SiteConfig).to receive(:home_feed_minimum_score).and_return(10)
allow(Settings::UserExperience).to receive(:home_feed_minimum_score).and_return(10)
end
it "does not contain non featured articles with a score below SiteConfig.home_feed_minimum_score" do
create(:article, featured: false, score: SiteConfig.home_feed_minimum_score - 1)
it "does not contain non featured articles with a score below Settings::UserExperience.home_feed_minimum_score" do
create(:article, featured: false, score: Settings::UserExperience.home_feed_minimum_score - 1)
expect { get feed_path }.to raise_error(ActiveRecord::RecordNotFound)
end
it "contains non featured articles with a score equal to SiteConfig.home_feed_minimum_score" do
create(:article, featured: false, score: SiteConfig.home_feed_minimum_score)
it "contains non featured articles with a score equal to Settings::UserExperience.home_feed_minimum_score" do
create(:article, featured: false, score: Settings::UserExperience.home_feed_minimum_score)
get feed_path
expect(response).to have_http_status(:ok)
end
it "contains non featured articles with a score above SiteConfig.home_feed_minimum_score" do
create(:article, featured: false, score: SiteConfig.home_feed_minimum_score + 1)
it "contains non featured articles with a score above Settings::UserExperience.home_feed_minimum_score" do
create(:article, featured: false, score: Settings::UserExperience.home_feed_minimum_score + 1)
get feed_path

View file

@ -15,7 +15,7 @@ RSpec.describe "AsyncInfo", type: :request do
end
it "renders normal response even if site config is private" do
allow(SiteConfig).to receive(:public).and_return(false)
allow(Settings::UserExperience).to receive(:public).and_return(false)
get "/async_info/base_data"
expect(response.parsed_body.keys).to match_array(%w[broadcast param token])
end

View file

@ -15,7 +15,7 @@ RSpec.describe "GaEvents", type: :request, vcr: vcr_option do
end
it "renders normal response even if site config is private" do
allow(SiteConfig).to receive(:public).and_return(false)
allow(Settings::UserExperience).to receive(:public).and_return(false)
post "/fallback_activity_recorder", params: {
path: "/ben", user_language: "en"
}.to_json

View file

@ -5,7 +5,7 @@ RSpec.describe "Invitations", type: :request do
describe "Accept invitation" do
it "renders normal response even if site config is private" do
allow(SiteConfig).to receive(:public).and_return(false)
allow(Settings::UserExperience).to receive(:public).and_return(false)
get "/users/invitation/accept?invitation_token=blahblahblahblah"
# This is a fake token, so the only thing we're testing for here is
# that we *do not* land on the "registrations" page which shouldn't

View file

@ -31,7 +31,7 @@ RSpec.describe "Stories::Feeds", type: :request do
end
it "returns feed when feed_strategy is basic" do
allow(SiteConfig).to receive(:feed_strategy).and_return("basic")
allow(Settings::UserExperience).to receive(:feed_strategy).and_return("basic")
get "/stories/feed"
expect(response_article).to include(
"id" => article.id,
@ -45,7 +45,7 @@ RSpec.describe "Stories::Feeds", type: :request do
end
it "returns feed when feed_strategy is optimized" do
allow(SiteConfig).to receive(:feed_strategy).and_return("optimized")
allow(Settings::UserExperience).to receive(:feed_strategy).and_return("optimized")
get "/stories/feed"
expect(response_article).to include(
"id" => article.id,
@ -128,7 +128,7 @@ RSpec.describe "Stories::Feeds", type: :request do
end
it "returns feed when feed_strategy is basic" do
allow(SiteConfig).to receive(:feed_strategy).and_return("basic")
allow(Settings::UserExperience).to receive(:feed_strategy).and_return("basic")
get "/stories/feed"
expect(response_article).to include(
"id" => article.id,
@ -142,7 +142,7 @@ RSpec.describe "Stories::Feeds", type: :request do
end
it "returns feed when feed_strategy is optimized" do
allow(SiteConfig).to receive(:feed_strategy).and_return("optimized")
allow(Settings::UserExperience).to receive(:feed_strategy).and_return("optimized")
get "/stories/feed"
expect(response_article).to include(
"id" => article.id,

View file

@ -43,7 +43,7 @@ RSpec.describe "StoriesIndex", type: :request do
end
it "renders registration page if site config is private" do
allow(SiteConfig).to receive(:public).and_return(false)
allow(Settings::UserExperience).to receive(:public).and_return(false)
get root_path
expect(response.body).to include("Continue with")
@ -105,7 +105,7 @@ RSpec.describe "StoriesIndex", type: :request do
end
it "does not set cache-related headers if private" do
allow(SiteConfig).to receive(:public).and_return(false)
allow(Settings::UserExperience).to receive(:public).and_return(false)
get "/"
expect(response.status).to eq(200)
@ -148,7 +148,7 @@ RSpec.describe "StoriesIndex", type: :request do
it "shows only one cover if basic feed style" do
create_list(:article, 3, featured: true, score: 20, main_image: "https://example.com/image.jpg")
allow(SiteConfig).to receive(:feed_style).and_return("basic")
allow(Settings::UserExperience).to receive(:feed_style).and_return("basic")
get "/"
expect(response.body.scan(/(?=class="crayons-story__cover crayons-story__cover__image)/).count).to be 1
end
@ -156,7 +156,7 @@ RSpec.describe "StoriesIndex", type: :request do
it "shows multiple cover images if rich feed style" do
create_list(:article, 3, featured: true, score: 20, main_image: "https://example.com/image.jpg")
allow(SiteConfig).to receive(:feed_style).and_return("rich")
allow(Settings::UserExperience).to receive(:feed_style).and_return("rich")
get "/"
expect(response.body.scan(/(?=class="crayons-story__cover crayons-story__cover__image)/).count).to be > 1
end
@ -199,7 +199,7 @@ RSpec.describe "StoriesIndex", type: :request do
context "with campaign_sidebar" do
before do
allow(Settings::Campaign).to receive(:featured_tags).and_return("mytag,yourtag")
allow(SiteConfig).to receive(:home_feed_minimum_score).and_return(7)
allow(Settings::UserExperience).to receive(:home_feed_minimum_score).and_return(7)
a_body = "---\ntitle: Super-sheep#{rand(1000)}\npublished: true\ntags: heyheyhey,mytag\n---\n\nHello"
create(:article, approved: true, body_markdown: a_body, score: 1)
@ -402,7 +402,7 @@ RSpec.describe "StoriesIndex", type: :request do
end
it "renders properly even if site config is private" do
allow(SiteConfig).to receive(:public).and_return(false)
allow(Settings::UserExperience).to receive(:public).and_return(false)
get "/t/#{tag.name}"
expect(response.body).to include("crayons-tabs__item crayons-tabs__item--current")
end

View file

@ -59,7 +59,7 @@ RSpec.describe Articles::Feeds::LargeForemExperimental, type: :service do
before do
article.update(published_at: 1.week.ago)
allow(SiteConfig).to receive(:home_feed_minimum_score).and_return(0)
allow(Settings::UserExperience).to receive(:home_feed_minimum_score).and_return(0)
end
it "returns a featured article and correctly scored other articles", :aggregate_failures do

View file

@ -9,7 +9,7 @@ seeder = Seeder.new
##############################################################################
# Default development site config if different from production scenario
SiteConfig.public = true
Settings::UserExperience.public = true
SiteConfig.waiting_on_first_user = false
##############################################################################