0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-06 13:08:40 +08:00
discourse/spec/system/admin_customize_themes_config_area_spec.rb
Penar Musaraj ef406fc8c0
DEV: Add theme screenshots system spec and skill (#39426)
Previously, there was no systematic way to visually review changes
across multiple screens in a theme. Or to compare how Foundation and
Horizon render key UI screens

This adds a `screenshot_marker(label:)` helper in system specs (globally
included via ThemeScreenshotMarker) that captures a PNG at that point in
a test. A matrix runner spec at `theme_screenshots_spec.rb`
auto-discovers any system spec containing screenshot markers and runs
those blocks against each theme × color mode × device combination. At
the end it generates a `compare.html` viewer for side-by-side
comparison.

<img width="3399" height="1400" alt="image"
src="https://github.com/user-attachments/assets/4ddd650a-6133-4da5-901e-5ec2f2d0906a"
/>


This can be run via CLI or via the attached skill. Directly:

### All themes × light/dark × desktop/mobile (default)
TAKE_SCREENSHOTS=1 bin/rspec spec/system/theme_screenshots_spec.rb


#### Foundation only, dark mode, desktop only
TAKE_SCREENSHOTS=1 SCREENSHOTS_THEMES=foundation SCREENSHOTS_MODES=dark
SCREENSHOTS_DEVICES=desktop bin/rspec
spec/system/theme_screenshots_spec.rb

#### Third-party theme in addition to core themes
TAKE_SCREENSHOTS=1
SCREENSHOTS_THEME_URL=https://github.com/discourse/discourse-air
bin/rspec spec/system/theme_screenshots_spec.rb



You can also run this via the agent skill, which builds the command from
natural language: `/discourse-screenshots` for the full default matrix,
or `/discourse-screenshots foundation only, dark only, desktop` and
similar limited runs.

### Use cases

- review broad changes in core against key UI screens across the app
- compare layouts/screens between core themes and/or a custom theme
- preview a new theme's look and feel across different areas of the app

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 09:36:29 -04:00

155 lines
5.2 KiB
Ruby
Vendored

# frozen_string_literal: true
describe "Admin Customize Themes Config Area Page" do
fab!(:admin)
fab!(:theme) { Fabricate(:theme, name: "First theme") }
fab!(:foundation_theme) { Theme.foundation_theme }
fab!(:horizon_theme) { Theme.horizon_theme }
fab!(:theme_child_theme) do
Fabricate(:theme, name: "Child theme", component: true, enabled: true, parent_themes: [theme])
end
fab!(:theme_2) { Fabricate(:theme, name: "Second theme") }
let(:config_area) { PageObjects::Pages::AdminCustomizeThemesConfigArea.new }
let(:install_modal) { PageObjects::Modals::InstallTheme.new }
let(:admin_customize_themes_page) { PageObjects::Pages::AdminCustomizeThemes.new }
before { sign_in(admin) }
it "has an install button in the subheader" do
config_area.visit
screenshot_marker(label: "admin-themes", only: "desktop")
install_modal = config_area.click_install_button
expect(install_modal.popular_options.first).to have_text("Graceful")
end
it "opens an install modal when coming from the install theme button on Meta" do
config_area.visit(
{ "repoName" => "graceful", "repoUrl" => "https://github.com/discourse/graceful" },
)
expect(install_modal).to be_open
expect(install_modal).to have_content("github.com/discourse/graceful")
install_modal.close
expect(page).to have_current_path("/admin/config/customize/themes")
end
it "allows to delete not system and not default theme" do
theme.set_default!
config_area.visit
expect(config_area).to have_disabled_delete_button(theme)
expect(config_area).to have_disabled_delete_button(horizon_theme)
expect(config_area).to have_themes(["First theme", "Horizon", "Foundation", "Second theme"])
config_area.delete_theme(theme_2)
expect(config_area).to have_no_theme("Second theme")
end
it "allows to mark theme as default" do
config_area.visit
expect(config_area).to have_default_badge(foundation_theme)
expect(config_area).to have_no_default_badge(theme_2)
config_area.mark_as_default(theme_2)
expect(config_area).to have_default_badge(theme_2)
expect(config_area).to have_no_default_badge(foundation_theme)
end
it "allows to make theme selectable by users" do
config_area.visit
expect(config_area).to have_no_badge(theme, "--selectable")
config_area.toggle_selectable(theme)
expect(config_area).to have_badge(theme, "--selectable")
config_area.toggle_selectable(theme)
expect(config_area).to have_no_badge(theme, "--selectable")
end
it "allows a theme to be created" do
config_area.visit.click_install_button.create_new_theme(name: "some new theme")
expect(PageObjects::Components::Toasts.new).to have_success(
I18n.t("admin_js.admin.customize.theme.install_success", theme: "some new theme"),
)
expect(page).to have_current_path(%r{/admin/customize/themes/\d+})
end
it "allows to edit and delete theme" do
config_area.visit
config_area.click_edit(theme)
expect(page).to have_current_path("/admin/customize/themes/#{theme.id}")
admin_customize_themes_page.click_delete
admin_customize_themes_page.confirm_delete
expect(page).to have_current_path("/admin/config/customize/themes")
end
it "has new look when edit theme is visited directly and can go back to themes" do
visit("/admin/customize/themes/#{theme.id}")
admin_customize_themes_page.click_back_to_themes
expect(page).to have_current_path("/admin/config/customize/themes")
expect(page).to have_content(
I18n.t("admin_js.admin.config_areas.themes_and_components.themes.title"),
)
end
describe "screenshot toggle" do
fab!(:light_upload, :image_upload)
fab!(:dark_upload, :image_upload)
fab!(:theme_with_screenshots) do
Fabricate(
:theme,
name: "Theme with screenshots",
theme_fields: [
ThemeField.new(
target_id: Theme.targets[:common],
name: "screenshot_light",
type_id: ThemeField.types[:theme_screenshot_upload_var],
upload_id: light_upload.id,
value: "",
),
ThemeField.new(
target_id: Theme.targets[:common],
name: "screenshot_dark",
type_id: ThemeField.types[:theme_screenshot_upload_var],
upload_id: dark_upload.id,
value: "",
),
],
)
end
it "allows toggling between light and dark screenshots" do
config_area.visit
light_src = config_area.screenshot_image(theme_with_screenshots)[:src]
expect(light_src).to include(light_upload.url)
expect(config_area).to have_screenshot_with_icon(theme_with_screenshots, "moon")
config_area.click_screenshot_toggle(theme_with_screenshots)
expect(config_area.screenshot_image(theme_with_screenshots)[:src]).to include(dark_upload.url)
expect(config_area.screenshot_image(theme_with_screenshots)[:src]).not_to eq(light_src)
expect(config_area).to have_screenshot_with_icon(theme_with_screenshots, "sun")
config_area.click_screenshot_toggle(theme_with_screenshots)
expect(config_area.screenshot_image(theme_with_screenshots)[:src]).to include(light_src)
config_area.visit
expect(config_area).to have_no_screenshot_toggle_button(theme)
end
end
end