mirror of
https://github.com/discourse/discourse.git
synced 2026-08-08 15:55:38 +08:00
Similar to c6d8fcf, this suppresses the "new feature" notifications for
admins on brand new sites <1hr old.
This change sets `last_viewed_feature_date` to the newest item on new
sites, suppressing the initial changes while keeping notifications for
new features that come later.
358 lines
11 KiB
Ruby
Vendored
358 lines
11 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
RSpec.describe Jobs::CheckNewFeatures do
|
|
def build_feature_hash(id:, created_at:, discourse_version: "2.9.0.beta10")
|
|
{
|
|
id: id,
|
|
user_id: 89_432,
|
|
emoji: "👤",
|
|
title: "New fancy feature!",
|
|
description: "",
|
|
link: "https://meta.discourse.org/t/-/238821",
|
|
created_at: created_at.iso8601,
|
|
updated_at: (created_at + 1.minute).iso8601,
|
|
discourse_version: discourse_version,
|
|
}
|
|
end
|
|
|
|
def stub_new_features_endpoint(*features)
|
|
stub_request(:get, DiscourseUpdates.new_features_full_endpoint_url).to_return(
|
|
status: 200,
|
|
body: JSON.dump(features),
|
|
headers: {
|
|
"Content-Type" => "application/json",
|
|
},
|
|
)
|
|
end
|
|
|
|
let(:permanent_upcoming_changes) do
|
|
[
|
|
{
|
|
setting: :enable_upload_debug_mode,
|
|
humanized_name: SiteSetting.humanized_names(:enable_upload_debug_mode),
|
|
description: SiteSetting.description(:enable_upload_debug_mode),
|
|
upcoming_change: {
|
|
learn_more_url: "https://meta.discourse.org/t/-/1234",
|
|
image: {
|
|
url: "#{Discourse.base_url}/images/upcoming_changes/enable_upload_debug_mode.png",
|
|
},
|
|
},
|
|
},
|
|
]
|
|
end
|
|
|
|
def stub_permanent_upcoming_changes!(changes)
|
|
UpcomingChanges.stubs(:permanent_upcoming_changes).returns(changes)
|
|
end
|
|
|
|
fab!(:admin1, :admin)
|
|
fab!(:admin2, :admin)
|
|
|
|
let(:feature1) do
|
|
build_feature_hash(id: 35, created_at: 3.days.ago, discourse_version: "2.8.1.beta12")
|
|
end
|
|
|
|
let(:feature2) do
|
|
build_feature_hash(id: 34, created_at: 2.days.ago, discourse_version: "2.8.1.beta13")
|
|
end
|
|
|
|
let(:pending_feature) do
|
|
build_feature_hash(id: 37, created_at: 1.day.ago, discourse_version: "2.8.1.beta14")
|
|
end
|
|
|
|
before do
|
|
Migration::Helpers.stubs(:new_site?).returns(false)
|
|
DiscourseUpdates.stubs(:current_version).returns("2.8.1.beta13")
|
|
freeze_time
|
|
stub_new_features_endpoint(feature1, feature2, pending_feature)
|
|
UpcomingChanges.stubs(:permanent_upcoming_changes).returns([])
|
|
end
|
|
|
|
after { DiscourseUpdates.clean_state }
|
|
|
|
it "backfills last viewed feature for admins who don't have last viewed feature" do
|
|
DiscourseUpdates.stubs(:current_version).returns("2.8.1.beta12")
|
|
DiscourseUpdates.update_new_features([feature1].to_json)
|
|
DiscourseUpdates.bump_last_viewed_feature_date(admin1.id, Time.zone.now.iso8601)
|
|
|
|
described_class.new.execute({})
|
|
|
|
expect(DiscourseUpdates.get_last_viewed_feature_date(admin2.id).iso8601).to eq(
|
|
feature1[:created_at],
|
|
)
|
|
expect(DiscourseUpdates.get_last_viewed_feature_date(admin1.id).iso8601).to eq(
|
|
Time.zone.now.iso8601,
|
|
)
|
|
end
|
|
|
|
it "notifies admins about new features that are available in the site's version" do
|
|
Notification.destroy_all
|
|
|
|
described_class.new.execute({})
|
|
|
|
expect(
|
|
admin1
|
|
.notifications
|
|
.where(notification_type: Notification.types[:new_features], read: false)
|
|
.count,
|
|
).to eq(1)
|
|
expect(
|
|
admin2
|
|
.notifications
|
|
.where(notification_type: Notification.types[:new_features], read: false)
|
|
.count,
|
|
).to eq(1)
|
|
end
|
|
|
|
context "when the site is brand new (< 1 hour old)" do
|
|
before { Migration::Helpers.stubs(:new_site?).returns(true) }
|
|
|
|
it "suppresses the back-catalog notification and seeds last_viewed_feature_date" do
|
|
Notification.destroy_all
|
|
|
|
described_class.new.execute({})
|
|
|
|
expect(
|
|
admin1.notifications.where(notification_type: Notification.types[:new_features]),
|
|
).to be_empty
|
|
expect(
|
|
admin2.notifications.where(notification_type: Notification.types[:new_features]),
|
|
).to be_empty
|
|
|
|
# pending_feature is filtered out by version (beta14 > current beta13), so
|
|
# feature2 is the newest visible item.
|
|
newest_at = Time.zone.parse(feature2[:created_at])
|
|
expect(DiscourseUpdates.get_last_viewed_feature_date(admin1.id)).to be_within_one_second_of(
|
|
newest_at,
|
|
)
|
|
expect(DiscourseUpdates.get_last_viewed_feature_date(admin2.id)).to be_within_one_second_of(
|
|
newest_at,
|
|
)
|
|
end
|
|
|
|
it "still notifies once a newer feature lands after the site is established" do
|
|
Notification.destroy_all
|
|
|
|
described_class.new.execute({})
|
|
|
|
Migration::Helpers.stubs(:new_site?).returns(false)
|
|
newer_feature =
|
|
build_feature_hash(id: 99, created_at: 1.hour.from_now, discourse_version: "2.8.1.beta13")
|
|
stub_new_features_endpoint(newer_feature, feature1, feature2, pending_feature)
|
|
|
|
described_class.new.execute({})
|
|
|
|
expect(
|
|
admin1
|
|
.notifications
|
|
.where(notification_type: Notification.types[:new_features], read: false)
|
|
.count,
|
|
).to eq(1)
|
|
expect(
|
|
admin2
|
|
.notifications
|
|
.where(notification_type: Notification.types[:new_features], read: false)
|
|
.count,
|
|
).to eq(1)
|
|
end
|
|
end
|
|
|
|
context "when a permanent upcoming change is merged into an empty new-features feed" do
|
|
before do
|
|
stub_permanent_upcoming_changes!(permanent_upcoming_changes)
|
|
UpcomingChanges.stubs(:image_exists?).returns(true)
|
|
UpcomingChanges.stubs(:image_data).returns(
|
|
{
|
|
url: "#{Discourse.base_url}/images/upcoming_changes/enable_upload_debug_mode.png",
|
|
width: 244,
|
|
height: 66,
|
|
file_path: file_from_fixtures("logo.png", "images").path,
|
|
},
|
|
)
|
|
stub_new_features_endpoint(feature1)
|
|
end
|
|
|
|
it "notifies admins and bumps last_viewed_feature_date from the status_changed time" do
|
|
Notification.destroy_all
|
|
|
|
status_changed_at = 1.day.ago
|
|
event =
|
|
UpcomingChangeEvent.create!(
|
|
event_type: :status_changed,
|
|
upcoming_change_name: "enable_upload_debug_mode",
|
|
event_data: {
|
|
"previous_value" => "stable",
|
|
"new_value" => "permanent",
|
|
},
|
|
created_at: status_changed_at,
|
|
)
|
|
Discourse.cache.delete(UpcomingChanges.current_statuses_cache_key)
|
|
|
|
described_class.new.execute({})
|
|
|
|
expect(
|
|
admin1
|
|
.notifications
|
|
.where(notification_type: Notification.types[:new_features], read: false)
|
|
.count,
|
|
).to eq(1)
|
|
expect(
|
|
admin2
|
|
.notifications
|
|
.where(notification_type: Notification.types[:new_features], read: false)
|
|
.count,
|
|
).to eq(1)
|
|
|
|
status_changed_at_db = event.reload.created_at
|
|
expect(DiscourseUpdates.get_last_viewed_feature_date(admin1.id)).to be_within_one_second_of(
|
|
status_changed_at_db,
|
|
)
|
|
expect(DiscourseUpdates.get_last_viewed_feature_date(admin2.id)).to be_within_one_second_of(
|
|
status_changed_at_db,
|
|
)
|
|
end
|
|
end
|
|
|
|
context "when persisted feed is older than a permanent upcoming change" do
|
|
let(:feature_stale) do
|
|
build_feature_hash(id: 99, created_at: 3.days.ago, discourse_version: "2.8.1.beta12")
|
|
end
|
|
|
|
let(:feature_newer_than_uc) do
|
|
build_feature_hash(id: 100, created_at: 1.day.ago, discourse_version: "2.8.1.beta13")
|
|
end
|
|
|
|
before do
|
|
stub_permanent_upcoming_changes!(permanent_upcoming_changes)
|
|
UpcomingChanges.stubs(:image_exists?).returns(true)
|
|
UpcomingChanges.stubs(:image_data).returns(
|
|
{
|
|
url: "#{Discourse.base_url}/images/upcoming_changes/enable_upload_debug_mode.png",
|
|
width: 244,
|
|
height: 66,
|
|
file_path: file_from_fixtures("logo.png", "images").path,
|
|
},
|
|
)
|
|
stub_new_features_endpoint(feature_stale)
|
|
end
|
|
|
|
it "seeds last_viewed to the UC when the fetch adds nothing newer, without notifying" do
|
|
Notification.destroy_all
|
|
|
|
uc_became_permanent_at = 2.days.ago
|
|
UpcomingChangeEvent.create!(
|
|
event_type: :status_changed,
|
|
upcoming_change_name: "enable_upload_debug_mode",
|
|
event_data: {
|
|
"previous_value" => "stable",
|
|
"new_value" => "permanent",
|
|
},
|
|
created_at: uc_became_permanent_at,
|
|
)
|
|
Discourse.cache.delete(UpcomingChanges.current_statuses_cache_key)
|
|
|
|
DiscourseUpdates.update_new_features([feature_stale].to_json)
|
|
|
|
described_class.new.execute({})
|
|
|
|
expect(
|
|
admin1.notifications.where(
|
|
notification_type: Notification.types[:new_features],
|
|
read: false,
|
|
),
|
|
).to be_empty
|
|
expect(
|
|
admin2.notifications.where(
|
|
notification_type: Notification.types[:new_features],
|
|
read: false,
|
|
),
|
|
).to be_empty
|
|
expect(DiscourseUpdates.get_last_viewed_feature_date(admin1.id)).to be_within_one_second_of(
|
|
uc_became_permanent_at,
|
|
)
|
|
expect(DiscourseUpdates.get_last_viewed_feature_date(admin2.id)).to be_within_one_second_of(
|
|
uc_became_permanent_at,
|
|
)
|
|
end
|
|
|
|
it "notifies and bumps last_viewed to a new feed item newer than the UC" do
|
|
Notification.destroy_all
|
|
|
|
uc_became_permanent_at = 2.days.ago
|
|
UpcomingChangeEvent.create!(
|
|
event_type: :status_changed,
|
|
upcoming_change_name: "enable_upload_debug_mode",
|
|
event_data: {
|
|
"previous_value" => "stable",
|
|
"new_value" => "permanent",
|
|
},
|
|
created_at: uc_became_permanent_at,
|
|
)
|
|
Discourse.cache.delete(UpcomingChanges.current_statuses_cache_key)
|
|
|
|
DiscourseUpdates.update_new_features([feature_stale].to_json)
|
|
stub_new_features_endpoint(feature_newer_than_uc, feature_stale)
|
|
|
|
described_class.new.execute({})
|
|
|
|
expect(
|
|
admin1
|
|
.notifications
|
|
.where(notification_type: Notification.types[:new_features], read: false)
|
|
.count,
|
|
).to eq(1)
|
|
expect(
|
|
admin2
|
|
.notifications
|
|
.where(notification_type: Notification.types[:new_features], read: false)
|
|
.count,
|
|
).to eq(1)
|
|
newer_time = Time.zone.parse(feature_newer_than_uc[:created_at])
|
|
expect(DiscourseUpdates.get_last_viewed_feature_date(admin1.id)).to be_within_one_second_of(
|
|
newer_time,
|
|
)
|
|
expect(DiscourseUpdates.get_last_viewed_feature_date(admin2.id)).to be_within_one_second_of(
|
|
newer_time,
|
|
)
|
|
end
|
|
end
|
|
|
|
it "consolidates new features notifications" do
|
|
Notification.destroy_all
|
|
|
|
described_class.new.execute({})
|
|
|
|
notification =
|
|
admin1
|
|
.notifications
|
|
.where(notification_type: Notification.types[:new_features], read: false)
|
|
.first
|
|
expect(notification).to be_present
|
|
|
|
DiscourseUpdates.stubs(:current_version).returns("2.8.1.beta14")
|
|
described_class.new.execute({})
|
|
|
|
# old notification is destroyed
|
|
expect(Notification.find_by(id: notification.id)).to eq(nil)
|
|
|
|
notification =
|
|
admin1
|
|
.notifications
|
|
.where(notification_type: Notification.types[:new_features], read: false)
|
|
.first
|
|
# new notification is created
|
|
expect(notification).to be_present
|
|
end
|
|
|
|
it "doesn't notify admins about features they've already seen" do
|
|
Notification.destroy_all
|
|
DiscourseUpdates.bump_last_viewed_feature_date(admin1.id, feature2[:created_at])
|
|
|
|
described_class.new.execute({})
|
|
|
|
expect(admin1.notifications.count).to eq(0)
|
|
expect(
|
|
admin2.notifications.where(notification_type: Notification.types[:new_features]).count,
|
|
).to eq(1)
|
|
end
|
|
end
|