0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-08 17:53:55 +08:00
discourse/plugins/discourse-rss-polling/spec/models/feed_item_spec.rb
Régis Hanol 4fc1144521
UX: Redesign the RSS Polling admin interface (#40790)
Previously, the RSS Polling plugin's admin page predated Discourse's
admin interface guidelines: it was a single inline-editable table with
no breadcrumbs or page header, it truncated long feed URLs, it required
horizontal scrolling on mobile, and it gave admins no way to verify a
feed before saving it.

This change rebuilds the page on the standard plugin "show route"
structure (breadcrumbs, page header, and Settings + Feeds tabs) with a
`d-table` feed list and dedicated FormKit new/edit routes, so the UI now
matches the rest of the admin interface and works on mobile. It also:

- Adds a **Test feed** dry-run that fetches a feed and previews which
items would be imported or skipped, with plain-language reasons, so
admins can validate a feed (and fix setup errors) before saving it.
- Adds the `rss_polling_feed_request_timeout` and
`rss_polling_verbose_logging` site settings.
- Extracts the fetch/parse and import/skip logic into `FeedFetcher` and
`FeedAnalyzer`, shared by the poll job and the new preview so the
dry-run and the real import always agree.
- Normalizes feed dates and categories so Atom feeds (which use
`<updated>` and `term`-style `<category>` elements) are handled like
RSS, and returns a clean error instead of a 500 when a feed cannot be
read.

---------

Co-authored-by: Martin Brennan <martin@discourse.org>
2026-07-02 07:40:25 +02:00

110 lines
3.8 KiB
Ruby
Vendored
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# frozen_string_literal: true
require "rss"
RSpec.describe DiscourseRssPolling::FeedItem do
RSpec.shared_examples "correctly parses the feed" do |**expected|
let(:feed_item) { DiscourseRssPolling::FeedItem.new(raw_feed_item) }
it { expect(feed_item.content).to eq(expected[:content]) }
it { expect(feed_item.url).to eq(expected[:url]) }
it { expect(feed_item.title).to eq(expected[:title]) }
it { expect(feed_item.categories).to eq(expected[:categories]) } if expected.key?(:categories)
end
context "with empty item" do
let(:raw_feed_item) { {} }
include_examples("correctly parses the feed", content: nil, url: nil, title: nil)
end
context "with RSS item" do
let(:feed) { RSS::Parser.parse(file_from_fixtures("feed.rss", "feed")) }
let(:raw_feed_item) { feed.items.first }
include_examples(
"correctly parses the feed",
content: "<p>This is the body &amp; content. </p>",
url: "https://blog.discourse.org/2017/09/poll-feed-spec-fixture/",
title: "Poll Feed Spec Fixture",
)
end
context "with escaped title" do
let(:raw_feed) { rss_polling_file_fixture("escaped_html.atom").read }
let(:feed) { RSS::Parser.parse(raw_feed) }
let(:raw_feed_item) { feed.entries.first }
include_examples(
"correctly parses the feed",
content: "Here are some random descriptions...",
url: "https://blog.discourse.org/2017/09/poll-feed-spec-fixture/",
title: "Wellington: “Progress is hard!” Other cities: “Hold my beer”",
)
end
context "with ATOM item with content element" do
let(:feed) { RSS::Parser.parse(file_from_fixtures("feed.atom", "feed")) }
let(:raw_feed_item) { feed.entries.first }
include_examples(
"correctly parses the feed",
content: "<p>This is the body &amp; content. </p>",
url: "https://blog.discourse.org/2017/09/poll-feed-spec-fixture/",
title: "Poll Feed Spec Fixture",
)
end
context "with ATOM item with summary element" do
let(:raw_feed) { rss_polling_file_fixture("no_content_only_summary.atom").read }
let(:feed) { RSS::Parser.parse(raw_feed) }
let(:raw_feed_item) { feed.entries.first }
include_examples(
"correctly parses the feed",
content: "Here are some random descriptions...",
url: "https://blog.discourse.org/2017/09/poll-feed-spec-fixture/",
title: "Poll Feed Spec Fixture",
)
end
context "with ATOM items with categories elements" do
let(:raw_feed) { rss_polling_file_fixture("multiple_categories.atom").read }
let(:feed) { RSS::Parser.parse(raw_feed, false) }
let(:raw_feed_item) { feed.entries.first }
include_examples(
"correctly parses the feed",
content: "Here are some random descriptions...",
url: "https://blog.discourse.org/2017/09/poll-feed-spec-fixture/",
title: "Poll Feed Spec Fixture",
categories: ["spec", "xrav3nz diary"],
)
end
context "with Youtube playlist" do
let(:raw_feed) { rss_polling_file_fixture("youtube_playlist.xml").read }
let(:feed) { RSS::Parser.parse(raw_feed, false) }
let(:raw_feed_item) { feed.entries.first }
include_examples(
"correctly parses the feed",
content: "https://www.youtube.com/watch?v=K56soYl0U1w",
url: "https://www.youtube.com/watch?v=K56soYl0U1w",
title: "The Ramones - Blitzkrieg Bop (With Lyrics)",
)
end
context "with youtube channel" do
let(:raw_feed) { rss_polling_file_fixture("youtube_channel.xml").read }
let(:feed) { RSS::Parser.parse(raw_feed, false) }
let(:raw_feed_item) { feed.entries.first }
include_examples(
"correctly parses the feed",
content: "https://www.youtube.com/watch?v=peYYl2vrIt4",
url: "https://www.youtube.com/watch?v=peYYl2vrIt4",
title: "An Uncontroversial Opinion AMD RX 6600 XT Announcement",
)
end
end