mirror of
https://github.com/discourse/discourse.git
synced 2026-08-08 17:53:55 +08:00
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>
110 lines
3.8 KiB
Ruby
Vendored
110 lines
3.8 KiB
Ruby
Vendored
# 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 & 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 & 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
|