Ensure spam doesn't hit logged-out /latest (#19110)
* Ensure spam doesn't hit logged-out /latest * Restore load-bearing constant * Narrow selector avoids the new 'latest' banner * Test scenario for signed-in low-score content * Remove false-negative test * Actual test for the logged-in scenario * Signed-in users get poor quality content * Feeds (RSS) use an entirely different query * Pass @stories * Update tests with new minimum score
This commit is contained in:
parent
f14b0868f8
commit
1374ab98e6
10 changed files with 83 additions and 10 deletions
|
|
@ -231,7 +231,7 @@ class StoriesController < ApplicationController
|
|||
if params[:timeframe].in?(Timeframe::FILTER_TIMEFRAMES)
|
||||
@stories = Articles::Feeds::Timeframe.call(params[:timeframe])
|
||||
elsif params[:timeframe] == Timeframe::LATEST_TIMEFRAME
|
||||
@stories = Articles::Feeds::Latest.call
|
||||
@stories = Articles::Feeds::Latest.call(minimum_score: Settings::UserExperience.home_feed_minimum_score)
|
||||
else
|
||||
@default_home_feed = true
|
||||
feed = Articles::Feeds::LargeForemExperimental.new(page: @page, tag: params[:tag])
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
module ArticlesHelper
|
||||
def should_show_latest_spam_suppression?(stories)
|
||||
return false if user_signed_in?
|
||||
return false unless stories.size > 1
|
||||
|
||||
params[:timeframe] == Timeframe::LATEST_TIMEFRAME
|
||||
end
|
||||
|
||||
def sort_options
|
||||
[
|
||||
[I18n.t("helpers.articles_helper.recently_created"), "creation-desc"],
|
||||
|
|
|
|||
|
|
@ -3,10 +3,13 @@ module Articles
|
|||
module Latest
|
||||
MINIMUM_SCORE = -20
|
||||
|
||||
def self.call(tag: nil, number_of_articles: Article::DEFAULT_FEED_PAGINATION_WINDOW_SIZE, page: 1)
|
||||
def self.call(tag: nil, number_of_articles: nil, page: 1, minimum_score: nil)
|
||||
number_of_articles ||= Article::DEFAULT_FEED_PAGINATION_WINDOW_SIZE
|
||||
minimum_score ||= MINIMUM_SCORE
|
||||
|
||||
Articles::Feeds::Tag.call(tag)
|
||||
.order(published_at: :desc)
|
||||
.where("score > ?", MINIMUM_SCORE)
|
||||
.where("score > ?", minimum_score)
|
||||
.page(page)
|
||||
.per(number_of_articles)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,12 @@
|
|||
<% if should_show_latest_spam_suppression?(@stories) %>
|
||||
<div class="crayons-story">
|
||||
<p class="crayons-story__body">
|
||||
<%= t "views.articles.sign_in_for_full_latest_html",
|
||||
link: sign_up_path(state: "new-user") %>
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if @pinned_article %>
|
||||
<%= render partial: "articles/single_story", locals: { story: @pinned_article, pinned: true, featured: @pinned_article.id == @featured_story.id } %>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ en:
|
|||
missing: Article No Longer Available
|
||||
published_html: Posted on %{date}
|
||||
scheduled_html: Scheduled for %{date}
|
||||
sign_in_for_full_latest_html: Some latest posts are only visible for members. <a href="%{link}">Sign in</a> to see all latest.
|
||||
read_next: Read next
|
||||
reading_time:
|
||||
one: "1 min read"
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ fr:
|
|||
missing: Article No Longer Available
|
||||
published_html: Posted on %{date}
|
||||
scheduled_html: Scheduled for %{date}
|
||||
sign_in_for_full_latest_html: Some latest posts are only visible for members. <a href="%{link}">Sign in</a> to see all latest.
|
||||
read_next: Lire ensuite
|
||||
reading_time:
|
||||
one: "1 min read"
|
||||
|
|
|
|||
|
|
@ -191,12 +191,19 @@ RSpec.describe "Articles" do
|
|||
let!(:article_with_low_score) do
|
||||
create(:article, score: Articles::Feeds::Latest::MINIMUM_SCORE)
|
||||
end
|
||||
let!(:article_with_mid_score) do
|
||||
minimum = Articles::Feeds::Latest::MINIMUM_SCORE
|
||||
maximum = Settings::UserExperience.home_feed_minimum_score
|
||||
score = (minimum..maximum).to_a.sample
|
||||
create(:article, score: score)
|
||||
end
|
||||
|
||||
before { get "/feed/latest" }
|
||||
|
||||
it "contains latest articles" do
|
||||
expect(response.body).to include(last_article.title)
|
||||
expect(response.body).to include(not_featured_article.title)
|
||||
expect(response.body).to include(article_with_mid_score.title)
|
||||
expect(response.body).not_to include(article_with_low_score.title)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -199,5 +199,25 @@ RSpec.describe "Stories::Feeds" do
|
|||
expect(response_article["top_comments"].first["username"]).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when there are low-scoring articles" do
|
||||
let!(:article) { create(:article, featured: false) }
|
||||
let!(:article_with_mid_score) do
|
||||
minimum = Articles::Feeds::Latest::MINIMUM_SCORE
|
||||
maximum = Settings::UserExperience.home_feed_minimum_score
|
||||
score = (minimum..maximum).to_a.sample
|
||||
create(:article, score: score)
|
||||
end
|
||||
let!(:article_with_low_score) do
|
||||
create(:article, score: Articles::Feeds::Latest::MINIMUM_SCORE)
|
||||
end
|
||||
|
||||
it "excludes low-score article but not mid-score" do
|
||||
get timeframe_stories_feed_path(:latest)
|
||||
expect(response.body).to include(article.title)
|
||||
expect(response.body).to include(article_with_mid_score.title)
|
||||
expect(response.body).not_to include(article_with_low_score.title)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -289,6 +289,13 @@ RSpec.describe "StoriesIndex" do
|
|||
|
||||
describe "GET stories index with timeframe" do
|
||||
describe "/latest" do
|
||||
let(:user) { create(:user) }
|
||||
let!(:low_score) { create(:article, score: -10) }
|
||||
|
||||
before do
|
||||
create_list(:article, 3, score: Settings::UserExperience.home_feed_minimum_score + 1)
|
||||
end
|
||||
|
||||
it "includes a link to Relevant", :aggregate_failures do
|
||||
get "/latest"
|
||||
|
||||
|
|
@ -296,6 +303,17 @@ RSpec.describe "StoriesIndex" do
|
|||
expected_tag = "<a data-text=\"Relevant\" href=\"/\""
|
||||
expect(response.body).to include(expected_tag)
|
||||
end
|
||||
|
||||
it "includes message and a link to sign in for signed-out" do
|
||||
get "/latest"
|
||||
expect(response.body).to include("Some latest posts are only visible for members")
|
||||
expect(response.body).to match(/Sign in.*to see all latest/)
|
||||
end
|
||||
|
||||
it "excludes low-score content for signed-out" do
|
||||
get "/latest"
|
||||
expect(response.body).not_to include(low_score.title)
|
||||
end
|
||||
end
|
||||
|
||||
describe "/top/week" do
|
||||
|
|
|
|||
|
|
@ -1,15 +1,22 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe "User visits articles by timeframe", type: :system do
|
||||
RSpec.describe "User visits articles by timeframe" do
|
||||
let(:author) { create(:user) }
|
||||
let!(:article) { create(:article, user: author, published_at: Time.current) }
|
||||
let!(:days_old_article) { create(:article, :past, past_published_at: 2.days.ago, user: author) }
|
||||
let!(:weeks_old_article) { create(:article, :past, past_published_at: 2.weeks.ago, user: author) }
|
||||
let!(:months_old_article) { create(:article, :past, past_published_at: 2.months.ago, user: author) }
|
||||
let!(:years_old_article) { create(:article, :past, past_published_at: 2.years.ago, user: author) }
|
||||
let(:minimum_score) { Settings::UserExperience.home_feed_minimum_score + 1 }
|
||||
let!(:article) { create(:article, score: minimum_score, user: author, published_at: Time.current) }
|
||||
let!(:days_old_article) { create(:article, :past, past_published_at: 2.days.ago, score: minimum_score, user: author) }
|
||||
let!(:weeks_old_article) do
|
||||
create(:article, :past, past_published_at: 2.weeks.ago, score: minimum_score, user: author)
|
||||
end
|
||||
let!(:months_old_article) do
|
||||
create(:article, :past, past_published_at: 2.months.ago, score: minimum_score, user: author)
|
||||
end
|
||||
let!(:years_old_article) do
|
||||
create(:article, :past, past_published_at: 2.years.ago, score: minimum_score, user: author)
|
||||
end
|
||||
|
||||
def shows_correct_articles_count(count)
|
||||
expect(page).to have_selector(".crayons-story", visible: :visible, count: count)
|
||||
expect(page).to have_selector(".crayons-story__title", visible: :visible, count: count)
|
||||
end
|
||||
|
||||
def shows_correct_articles_count_via_xpath(count)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue