discourse/spec/system/wizard_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

101 lines
3.6 KiB
Ruby

# frozen_string_literal: true
describe "Wizard" do
fab!(:admin)
let(:wizard_page) { PageObjects::Pages::Wizard.new }
before { sign_in(admin) }
it "successfully completes the setup wizard" do
visit("/wizard")
screenshot_marker(label: "wizard")
expect(wizard_page).to be_on_step("setup")
wizard_page.fill_field("text", "title", "My Test Site")
wizard_page.select_dropdown_option("default-locale", "en")
wizard_page.click_jump_in
expect(page).to have_current_path("/")
end
describe "Setup step" do
it "lets user configure site settings including member access" do
wizard_page.go_to_step("setup")
expect(SiteSetting.login_required).to eq(false)
expect(SiteSetting.invite_only).to eq(false)
expect(SiteSetting.must_approve_users).to eq(false)
expect(wizard_page.privacy_step).to have_selected_choice("login-required", "public")
expect(wizard_page.privacy_step).to have_selected_choice("invite-only", "sign_up")
expect(wizard_page.privacy_step).to have_selected_choice("must-approve-users", "no")
wizard_page.fill_field("text", "title", "My Test Site")
wizard_page.privacy_step.select_access_option("login-required", "private")
wizard_page.privacy_step.select_access_option("invite-only", "invite_only")
wizard_page.privacy_step.select_access_option("must-approve-users", "yes")
wizard_page.click_jump_in
expect(SiteSetting.login_required).to eq(true)
expect(SiteSetting.invite_only).to eq(true)
expect(SiteSetting.must_approve_users).to eq(true)
end
it "redirects to homepage when completed" do
wizard_page.go_to_step("setup")
wizard_page.fill_field("text", "title", "My Test Site")
wizard_page.click_jump_in
expect(page).to have_current_path("/")
end
it "prevents submission when title is empty" do
wizard_page.go_to_step("setup")
wizard_page.fill_field("text", "title", "")
wizard_page.click_jump_in
expect(wizard_page).to be_on_step("setup")
expect(page).to have_css(".wizard-container__field.text-title.invalid")
end
it "prevents submission when title is 'Discourse'" do
wizard_page.go_to_step("setup")
wizard_page.fill_field("text", "title", "Discourse")
wizard_page.click_jump_in
expect(wizard_page).to be_on_step("setup")
expect(page).to have_css(".wizard-container__field.text-title.invalid")
end
it "allows keyboard navigation of radio inputs" do
wizard_page.go_to_step("setup")
radio_field = wizard_page.find_field("radio", "login-required")
first_radio = radio_field.find("input[type='radio']", match: :first, visible: :all)
choice_id = first_radio[:value]
page.execute_script("arguments[0].focus()", first_radio)
expect(radio_field).to have_css(".wizard-container__radio-choice:focus-within")
expect(page.evaluate_script("document.activeElement.type")).to eq("radio")
first_radio.send_keys(:space)
expect(radio_field).to have_css(
".wizard-container__radio-choice[data-choice-id='#{choice_id}'].--selected",
)
end
it "keeps arrow key navigation within same radio group" do
wizard_page.go_to_step("setup")
login_required_field = wizard_page.find_field("radio", "login-required")
private_radio =
login_required_field.find("[data-choice-id='private'] input[type='radio']", visible: :all)
private_radio.click
private_radio.send_keys(:right)
expect(login_required_field).to have_css("input[type='radio']:focus", visible: :all)
end
end
end