0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-06 13:08:40 +08:00
discourse/plugins/discourse-calendar/spec/serializers/topic_view_serializer_spec.rb
Kris 80df3e2154
UX: hide timeline for livestream topics, make layout more reliable (#42209)
Livestream topics need as much space as possible because they have an
added chat sidebar. This change adds a transformer that allows the
plugin to use the progress bar instead of the timeline, which saves a
good amount of space while still allowing navigation through many posts.

This also solves a minor issue when entering 20+ posts deep into a
livestream topic would prevent you from getting the livestream layout
(hidden avatar) when viewing OP.

<img width="3012" height="1724" alt="image"
src="https://github.com/user-attachments/assets/2576ce2b-b900-4c58-bf25-9c611f9659e4"
/>
2026-07-31 10:16:52 -04:00

280 lines
8 KiB
Ruby
Vendored

# frozen_string_literal: true
RSpec.describe TopicViewSerializer do
subject(:serializer) do
described_class.new(TopicView.new(topic), scope: Guardian.new, root: false)
end
let(:topic) { Fabricate(:topic) }
let(:first_post) { Fabricate(:post, topic:) }
let(:parsed_json) { JSON.parse(serializer.to_json) }
before do
freeze_time(Time.utc(2020, 4, 24, 14, 10))
Jobs.run_immediately!
SiteSetting.calendar_enabled = true
SiteSetting.discourse_post_event_enabled = true
end
context "without timezone" do
before do
DiscoursePostEvent::Event.create!(
id: first_post.id,
original_starts_at: 1.hour.from_now,
original_ends_at: 2.hours.from_now,
)
end
describe "#event_starts_at" do
it "returns the start time of the event in proper format" do
expect(parsed_json["event_starts_at"]).to eq("2020-04-24T15:10:00.000Z")
end
end
describe "#event_ends_at" do
it "returns the end time of the event in proper format" do
expect(parsed_json["event_ends_at"]).to eq("2020-04-24T16:10:00.000Z")
end
end
describe "#event_timezone" do
it "is not included when event has no timezone" do
expect(parsed_json).not_to have_key("event_timezone")
end
end
describe "#event_show_local_time" do
it "returns false when show_local_time is not set" do
expect(parsed_json["event_show_local_time"]).to eq(false)
end
end
end
context "with timezone and show_local_time true" do
before do
DiscoursePostEvent::Event.create!(
id: first_post.id,
original_starts_at: 1.hour.from_now,
original_ends_at: 2.hours.from_now,
timezone: "Australia/Sydney",
show_local_time: true,
)
end
describe "#event_timezone" do
it "returns the timezone of the event" do
expect(parsed_json["event_timezone"]).to eq("Australia/Sydney")
end
end
describe "#event_show_local_time" do
it "returns true when show_local_time is set" do
expect(parsed_json["event_show_local_time"]).to eq(true)
end
end
end
context "with timezone and show_local_time false" do
before do
DiscoursePostEvent::Event.create!(
id: first_post.id,
original_starts_at: 1.hour.from_now,
original_ends_at: 2.hours.from_now,
timezone: "Australia/Sydney",
show_local_time: false,
)
end
describe "#event_timezone" do
it "returns the timezone of the event" do
expect(parsed_json["event_timezone"]).to eq("Australia/Sydney")
end
end
describe "#event_show_local_time" do
it "returns false when show_local_time is explicitly false" do
expect(parsed_json["event_show_local_time"]).to eq(false)
end
end
end
context "with all-day event" do
before do
SiteSetting.display_post_event_date_on_topic_title = true
DiscoursePostEvent::Event.create!(
id: first_post.id,
original_starts_at: Time.utc(2020, 4, 25),
original_ends_at: Time.utc(2020, 4, 27),
all_day: true,
)
end
describe "#event_all_day" do
it "returns true" do
expect(parsed_json["event_all_day"]).to eq(true)
end
end
end
context "without all-day event" do
before do
DiscoursePostEvent::Event.create!(
id: first_post.id,
original_starts_at: 1.hour.from_now,
original_ends_at: 2.hours.from_now,
)
end
describe "#event_all_day" do
it "is not included" do
expect(parsed_json).not_to have_key("event_all_day")
end
end
end
describe "#event_watching_invitee_status" do
fab!(:viewer, :user)
def parsed_json_for(user)
JSON.parse(
described_class.new(TopicView.new(topic), scope: Guardian.new(user), root: false).to_json,
)
end
def create_event
DiscoursePostEvent::Event.create!(
id: first_post.id,
original_starts_at: 1.hour.from_now,
original_ends_at: 2.hours.from_now,
)
end
it "returns the status the current user answered with" do
event = create_event
DiscoursePostEvent::Invitee.create_attendance!(viewer.id, event.id, :going)
expect(parsed_json_for(viewer)["event_watching_invitee_status"]).to eq("going")
end
it "is null when the current user has not answered" do
create_event
expect(parsed_json_for(viewer)["event_watching_invitee_status"]).to be_nil
end
it "is not included when the topic has no event" do
first_post
expect(parsed_json_for(viewer)).not_to have_key("event_watching_invitee_status")
end
it "is not included for anonymous users" do
create_event
expect(parsed_json_for(nil)).not_to have_key("event_watching_invitee_status")
end
it "is not affected by other users' answers" do
event = create_event
DiscoursePostEvent::Invitee.create_attendance!(Fabricate(:user).id, event.id, :going)
expect(parsed_json_for(viewer)["event_watching_invitee_status"]).to be_nil
end
end
describe "#chat_channel_id" do
fab!(:category)
fab!(:viewer, :user)
let(:topic) { Fabricate(:topic, category:) }
before do
SiteSetting.chat_enabled = true
# Don't do onebox-warming job so it doesn't make a real request
Jobs.run_later!
first_post
end
def create_event(livestream:, status: :public, raw_invitees: nil)
DiscoursePostEvent::Event.create!(
id: first_post.id,
original_starts_at: 1.hour.from_now,
original_ends_at: 2.hours.from_now,
location: "https://example.com/live",
status: DiscoursePostEvent::Event.statuses[status],
raw_invitees:,
livestream:,
)
end
def parsed_json_for(user)
JSON.parse(
described_class.new(TopicView.new(topic), scope: Guardian.new(user), root: false).to_json,
)
end
it "is included while the event is a livestream" do
create_event(livestream: true)
expect(topic.topic_chat_channel).to be_present
expect(parsed_json_for(viewer)["chat_channel_id"]).to eq(
topic.topic_chat_channel.chat_channel_id,
)
end
it "is not included once livestream is disabled, even if the channel row remains" do
event = create_event(livestream: true)
event.update!(livestream: false)
expect(topic.reload.topic_chat_channel).to be_present
expect(parsed_json_for(viewer)).not_to have_key("chat_channel_id")
end
it "is not included for users who cannot join the event" do
create_event(livestream: true, status: :private, raw_invitees: ["some_group"])
expect(parsed_json_for(viewer)).not_to have_key("chat_channel_id")
end
it "is not included for anonymous users on private events" do
create_event(livestream: true, status: :private, raw_invitees: ["some_group"])
expect(parsed_json_for(nil)).not_to have_key("chat_channel_id")
end
it "is included for anonymous users on public events" do
create_event(livestream: true)
expect(parsed_json_for(nil)["chat_channel_id"]).to eq(
topic.topic_chat_channel.chat_channel_id,
)
end
it "is still included once the event is expired" do
create_event(livestream: true)
freeze_time(3.hours.from_now)
expect(parsed_json_for(viewer)["chat_channel_id"]).to eq(
topic.topic_chat_channel.chat_channel_id,
)
end
it "is included for users in a group that can join the event" do
group = Fabricate(:group)
group.add(viewer)
create_event(livestream: true, status: :private, raw_invitees: [group.name])
expect(parsed_json_for(viewer)["chat_channel_id"]).to eq(
topic.topic_chat_channel.chat_channel_id,
)
end
it "is included for admins even when they cannot join the event" do
create_event(livestream: true, status: :private, raw_invitees: ["some_group"])
expect(parsed_json_for(Fabricate(:admin))["chat_channel_id"]).to eq(
topic.topic_chat_channel.chat_channel_id,
)
end
end
end