2
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2025-10-03 17:21:20 +08:00
discourse/spec/system/header_spec.rb
Martin Brennan 19af83d39e
FEATURE: Themeable site settings (#32233)
This commit introduces the concept of themeable site settings,
which is a new tool for theme authors that lives alongside theme
modifiers and theme settings. Here is a quick summary:

* Theme settings - These are custom settings used to control UI and functionality within your theme or component and provide configuration options. These cannot change core Discourse functionality.
* Theme modifiers - Allows a theme or a component to modify selected server-side functionality of core Discourse as an alternative to building a plugin.
* Themeable site settings (new) - Allows a theme (not components) to override a small subset of core site settings, which generally control parts of the UI and other minor functionality. This allows themes to have a greater control over the full site experience.

Themeable site settings will be shown for all themes, whether the theme
changes
the value or not, and have a similar UI to custom theme settings.

We are also introducing a new page at
`/admin/config/theme-site-settings` that
allows admins to see all possible themeable site settings, and which
themes
are changing the value from the default.

### Configuration

Theme authors can configure initial values themeable site settings using
a section in the `about.json` file like so:

```json
"theme_site_settings": {
  "search_experience": "search_field"
}
```

These values will not change when the theme updates, because we cannot
know if admins have manually changed them.

### Limitations

Themeable site settings are only really intended to control elements of
the UI, and when retrieving their value we require a theme ID, so these
limitations apply:

- Themeable site settings cannot be used in Sidekiq jobs
- Themeable site settings cannot be used in markdown rules
- Themeable site settings will be cached separately to client site
settings using theme ID as a key
- Themeable site settings will override keys on the `siteSettings`
service on the client using the application preloader
- `SiteSetting.client_settings_json` will not include themeable site
settings, instead you can call `SiteSetting.theme_site_settings_json`
with a theme ID

### Initial settings

There are only two site settings that will be themeable to begin with:

* `enable_welcome_banner`
* `search_experience`

And our new Horizon theme will take advantage of both. Over time, more
settings that control elements of the UI will be exposed this way.
2025-07-16 11:00:21 +10:00

298 lines
10 KiB
Ruby
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# frozen_string_literal: true
RSpec.describe "Glimmer Header", type: :system do
let(:header) { PageObjects::Pages::Header.new }
let(:search) { PageObjects::Pages::Search.new }
fab!(:current_user, :user)
fab!(:topic)
it "renders basics" do
visit "/"
expect(page).to have_css("header.d-header")
expect(page).to have_css("#site-logo")
end
it "displays sign up button" do
visit "/"
expect(page).to have_css("button.sign-up-button")
expect(page).to have_css("button.login-button")
find("button.sign-up-button").click
expect(page).to have_css(".signup-fullpage")
end
it "displays login button" do
visit "/"
expect(page).to have_css("button.sign-up-button")
expect(page).to have_css("button.login-button")
find("button.login-button").click
expect(page).to have_css(".login-fullpage")
end
it "shows login button when login required" do
SiteSetting.login_required = true
visit "/"
expect(page).to have_css("button.login-button")
expect(page).to have_css("button.sign-up-button")
expect(page).not_to have_css("#search-button")
expect(page).not_to have_css("button.btn-sidebar-toggle")
end
it "renders unread notifications count when user's notifications count is updated" do
Fabricate(
:notification,
user: current_user,
high_priority: true,
read: false,
created_at: 8.minutes.ago,
)
sign_in(current_user)
visit "/"
expect(page).to have_selector(
".header-dropdown-toggle.current-user .unread-notifications",
text: "1",
)
end
it "doesn't show pending reviewables count for non-legacy navigation menu" do
SiteSetting.navigation_menu = "sidebar"
current_user.update!(admin: true)
Fabricate(:reviewable)
sign_in(current_user)
visit "/"
expect(page).not_to have_selector(".hamburger-dropdown .badge-notification")
end
it "closes revamped menu when clicking outside" do
sign_in(current_user)
visit "/"
find(".header-dropdown-toggle.current-user").click
expect(page).to have_selector(".user-menu.revamped")
find("header.d-header").click
expect(page).not_to have_selector(".user-menu.revamped")
end
it "closes menu-panel when keyboard focus leaves it" do
sign_in(current_user)
visit "/"
find(".header-dropdown-toggle.current-user").click
find("##{header.active_element_id}").send_keys(%i[shift tab])
expect(page).not_to have_selector(".user-menu.revamped")
end
it "automatically focuses the first link in the hamburger panel" do
SiteSetting.navigation_menu = "header dropdown"
visit "/"
find("#toggle-hamburger-menu").click
expect(page).to have_selector(".panel-body")
first_link = find(".panel-body a", match: :first)
first_link_href = first_link[:href]
focused_element_href = evaluate_script("document.activeElement.href")
expect(focused_element_href).to eq(first_link_href)
end
it "sets header's height css property" do
sign_in(current_user)
visit "/"
header.resize_element(".d-header", 90)
wait_for(timeout: 100) do
header.get_computed_style_value(".d-header", "--header-offset") == "90px"
end
expect(header.get_computed_style_value(".d-header", "--header-offset")).to eq("90px")
header.resize_element(".d-header", 60)
wait_for(timeout: 100) do
header.get_computed_style_value(".d-header", "--header-offset") == "60px"
end
expect(header.get_computed_style_value(".d-header", "--header-offset")).to eq("60px")
end
it "moves focus between tabs using arrow keys" do
sign_in(current_user)
visit "/"
find(".header-dropdown-toggle.current-user").click
expect(header.active_element_id).to eq("user-menu-button-all-notifications")
find("##{header.active_element_id}").send_keys(:down)
expect(header.active_element_id).to eq("user-menu-button-replies")
4.times { find("##{header.active_element_id}").send_keys(:down) }
expect(header.active_element_id).to eq("user-menu-button-profile")
find("##{header.active_element_id}").send_keys(:down)
expect(header.active_element_id).to eq("user-menu-button-all-notifications")
find("##{header.active_element_id}").send_keys(:up)
expect(header.active_element_id).to eq("user-menu-button-profile")
end
it "prioritizes new personal messages bubble over unseen reviewables and regular notifications bubbles" do
Fabricate(:private_message_notification, user: current_user)
Fabricate(
:notification,
user: current_user,
high_priority: true,
read: false,
created_at: 8.minutes.ago,
)
sign_in(current_user)
visit "/"
expect(page).not_to have_selector(
".header-dropdown-toggle.current-user .badge-notification.unread-notifications",
)
expect(page).not_to have_selector(
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-reviewables",
)
expect(page).to have_selector(
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-pms",
)
expect(page).to have_css(".d-icon-envelope")
expect(
find(".header-dropdown-toggle.current-user .badge-notification.with-icon.new-pms")[:title],
).to eq(I18n.t("js.notifications.tooltip.new_message_notification", count: 1))
end
it "prioritizes unseen reviewables bubble over regular notifications" do
current_user.update!(admin: true)
Fabricate(:reviewable)
sign_in(current_user)
visit "/"
expect(page).not_to have_selector(
".header-dropdown-toggle.current-user .badge-notification.unread-notifications",
)
expect(page).to have_selector(
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-reviewables",
)
expect(page).not_to have_selector(
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-pms",
)
end
it "shows regular notifications bubble if there are neither new personal messages nor unseen reviewables" do
Fabricate.times(
3,
:notification,
user: current_user,
high_priority: true,
read: false,
created_at: 8.minutes.ago,
)
sign_in(current_user)
visit "/"
expect(page).to have_selector(
".header-dropdown-toggle.current-user .badge-notification.unread-notifications",
text: "3",
)
expect(
find(".header-dropdown-toggle.current-user .badge-notification.unread-notifications")[:title],
).to eq(I18n.t("js.notifications.tooltip.regular", count: 3))
expect(page).not_to have_selector(
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-reviewables",
)
expect(page).not_to have_selector(
".header-dropdown-toggle.current-user .badge-notification.with-icon.new-pms",
)
end
context "when resetting password" do
fab!(:current_user, :user)
it "does not show search, login, or signup buttons" do
email_token =
current_user.email_tokens.create!(
email: current_user.email,
scope: EmailToken.scopes[:password_reset],
)
visit "/u/password-reset/#{email_token.token}"
expect(page).not_to have_css("button.login-button")
expect(page).not_to have_css("button.sign-up-button")
expect(page).not_to have_css(".search-dropdown #search-button")
end
end
context "when logged in and login required" do
fab!(:current_user, :user)
it "displays current user when logged in and login required" do
SiteSetting.login_required = true
Fabricate(:theme_site_setting_with_service, name: "enable_welcome_banner", value: false)
sign_in(current_user)
visit "/"
expect(page).not_to have_css("button.login-button")
expect(page).not_to have_css("button.sign-up-button")
expect(page).to have_css("#search-button")
expect(page).to have_css("button.btn-sidebar-toggle")
expect(page).to have_css("#current-user")
end
end
describe "mobile topic-info" do
fab!(:topic)
fab!(:posts) { Fabricate.times(21, :post, topic: topic) }
it "only shows when scrolled down", mobile: true do
visit "/t/#{topic.slug}/#{topic.id}"
expect(page).to have_css("#topic-title") # Main topic title
expect(page).to have_css("header.d-header .auth-buttons .login-button") # header buttons visible when no topic-info in header
page.execute_script("document.querySelector('#post_4').scrollIntoView()")
expect(page).not_to have_css("header.d-header .auth-buttons .login-button") # No header buttons
expect(page).to have_css("header.d-header .title-wrapper .topic-link") # Title is shown in header
page.execute_script("window.scrollTo(0, 0)")
expect(page).to have_css("#topic-title") # Main topic title
expect(page).to have_css("header.d-header .auth-buttons .login-button") # header buttons visible when no topic-info in header
end
it "shows when navigating direct to a later post", mobile: true do
visit "/t/#{topic.slug}/#{topic.id}/4"
expect(page).not_to have_css("header.d-header .auth-buttons .login-button") # No header buttons
expect(page).to have_css("header.d-header .title-wrapper .topic-link") # Title is shown in header
end
it "shows when jumping from OP to much later post", mobile: true do
visit "/t/#{topic.slug}/#{topic.id}"
expect(page).to have_css("#topic-title") # Main topic title
expect(page).to have_css("header.d-header .auth-buttons .login-button") # header buttons visible when no topic-info in header
page.execute_script("Discourse.visit('/t/#{topic.slug}/#{topic.id}/21');")
expect(page).to have_css("#post_21")
expect(page).not_to have_css("header.d-header .auth-buttons .login-button") # No header buttons
expect(page).to have_css("header.d-header .title-wrapper .topic-link") # Title is shown in header
end
it "shows and hides do-not-disturb icon" do
sign_in current_user
visit "/"
expect(page).to have_no_css(".d-header .do-not-disturb-background")
current_user.publish_do_not_disturb(ends_at: 1.hour.from_now)
try_until_success { expect(page).to have_css(".d-header .do-not-disturb-background") }
current_user.publish_do_not_disturb(ends_at: 1.second.from_now)
expect(page).to have_no_css(".d-header .do-not-disturb-background")
end
end
end