Files
discourse/spec/system/header_spec.rb
Joffrey JAFFEUX b6aad28ccf DEV: replace selenium driver with playwright (#31977)
This commit is replacing the system specs driver (selenium) by
Playwright: https://playwright.dev/

We are still using Capybara to write the specs but they will now be run
by Playwright. To achieve this we are using the non official ruby
driver: https://github.com/YusukeIwaki/capybara-playwright-driver

### Notable changes

- `CHROME_DEV_TOOLS` has been removed, it's not working well with
playwright use `pause_test` and inspect browser for now.

- `fill_in` is not generating key events in playwright, use `send_keys`
if you need this.

### New spec options

#### trace

Allows to capture a trace in a zip file which you can load at
https://trace.playwright.dev or locally through `npx playwright
show-trace /path/to/trace.zip`

_Example usage:_

```ruby
it "shows bar", trace: true do
  visit("/")

  find(".foo").click

  expect(page).to have_css(".bar")
end
```

#### video

Allows to capture a video of your spec.

_Example usage:_

```ruby
it "shows bar", video: true do
  visit("/")

  find(".foo").click

  expect(page).to have_css(".bar")
end
```

### New env variable

#### PLAYWRIGHT_SLOW_MO_MS

Allow to force playwright to wait DURATION (in ms) at each action.

_Example usage:_

```
PLAYWRIGHT_SLOW_MO_MS=1000 rspec foo_spec.rb
```

#### PLAYWRIGHT_HEADLESS

Allow to be in headless mode or not. Default will be headless.

_Example usage:_

```
PLAYWRIGHT_HEADLESS=0 rspec foo_spec.rb # will show the browser
```

### New helpers

#### with_logs

Allows to access the browser logs and check if something specific has
been logged.

_Example usage:_

```ruby
with_logs do |logger|
  # do something

  expect(logger.logs.map { |log| log[:message] }).to include("foo")
end
```

#### add_cookie

Allows to add a cookie on the browser session.

_Example usage:_

```ruby
add_cookie(name: "destination_url", value: "/new")
```

#### get_style

Get the property style value of an element.

_Example usage:_

```ruby
expect(get_style(find(".foo"), "height")).to eq("200px")
```

#### get_rgb_color

Get the rgb color of an element.

_Example usage:_

```ruby
expect(get_rgb_color(find("html"), "backgroundColor")).to eq("rgb(170, 51, 159)")
```
2025-05-06 10:44:14 +02:00

298 lines
10 KiB
Ruby

# 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) { Fabricate(: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) { Fabricate(: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) { Fabricate(:user) }
it "displays current user when logged in and login required" do
SiteSetting.login_required = true
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