mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-21 00:18:33 +08:00
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)") ```
354 lines
13 KiB
Ruby
Vendored
354 lines
13 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
describe "Admin Customize Themes Config Area Page", type: :system do
|
|
fab!(:admin)
|
|
fab!(:parent_theme) { Fabricate(:theme, name: "A theme") }
|
|
fab!(:parent_theme_2) { Fabricate(:theme, name: "B theme") }
|
|
fab!(:parent_theme_3) { Fabricate(:theme, name: "C theme") }
|
|
fab!(:parent_theme_4) { Fabricate(:theme, name: "D theme") }
|
|
|
|
let(:config_area) { PageObjects::Pages::AdminCustomizeComponentsConfigArea.new }
|
|
let(:toasts) { PageObjects::Components::Toasts.new }
|
|
let(:dialog) { PageObjects::Components::Dialog.new }
|
|
|
|
before { sign_in(admin) }
|
|
|
|
context "when there are components installed" do
|
|
fab!(:enabled_component) do
|
|
Fabricate(
|
|
:theme,
|
|
name: "Glorious component",
|
|
component: true,
|
|
enabled: true,
|
|
parent_themes: [parent_theme, parent_theme_2, parent_theme_3, parent_theme_4],
|
|
)
|
|
end
|
|
fab!(:disabled_component) do
|
|
Fabricate(:theme, name: "Glossy component", component: true, enabled: false)
|
|
end
|
|
fab!(:remote_component) do
|
|
Fabricate(
|
|
:theme,
|
|
component: true,
|
|
enabled: false,
|
|
parent_themes: [parent_theme_3],
|
|
remote_theme:
|
|
RemoteTheme.create!(
|
|
remote_url: "https://github.com/discourse/tc-1.git",
|
|
authors: "CDCK Inc.",
|
|
),
|
|
theme_fields: [
|
|
ThemeField.new(
|
|
name: "en",
|
|
type_id: ThemeField.types[:yaml],
|
|
target_id: Theme.targets[:translations],
|
|
value: <<~YAML,
|
|
en:
|
|
theme_metadata:
|
|
description: "Description of my remote component"
|
|
YAML
|
|
),
|
|
],
|
|
)
|
|
end
|
|
fab!(:remote_component_with_update) do
|
|
Fabricate(
|
|
:theme,
|
|
component: true,
|
|
enabled: false,
|
|
remote_theme:
|
|
RemoteTheme.create!(
|
|
remote_url: "https://github.com/discourse/discourse-kanban-theme.git",
|
|
commits_behind: 4,
|
|
),
|
|
)
|
|
end
|
|
|
|
it "can enable/disable components" do
|
|
config_area.visit
|
|
|
|
expect(config_area.component(enabled_component.id).enabled_toggle).to be_checked
|
|
expect(config_area.component(disabled_component.id).enabled_toggle).to be_unchecked
|
|
|
|
config_area.component(enabled_component.id).enabled_toggle.toggle
|
|
config_area.component(disabled_component.id).enabled_toggle.toggle
|
|
|
|
expect(config_area.component(enabled_component.id).enabled_toggle).to be_unchecked
|
|
expect(config_area.component(disabled_component.id).enabled_toggle).to be_checked
|
|
|
|
expect(enabled_component.reload.enabled).to eq(false)
|
|
expect(disabled_component.reload.enabled).to eq(true)
|
|
end
|
|
|
|
it "can filter components by status" do
|
|
config_area.visit
|
|
config_area.status_selector.select("used")
|
|
|
|
expect(config_area).to have_exact_components(enabled_component.id, remote_component.id)
|
|
|
|
config_area.status_selector.select("unused")
|
|
|
|
expect(config_area).to have_exact_components(
|
|
disabled_component.id,
|
|
remote_component_with_update.id,
|
|
)
|
|
|
|
config_area.status_selector.select("updates_available")
|
|
|
|
expect(config_area).to have_exact_components(remote_component_with_update.id)
|
|
end
|
|
|
|
it "can filter components by name" do
|
|
config_area.visit
|
|
|
|
config_area.name_filter_input.fill_in(with: "glo")
|
|
|
|
expect(config_area).to have_exact_components(enabled_component.id, disabled_component.id)
|
|
end
|
|
|
|
it "keeps the filters shown when there are no components matching the filters" do
|
|
config_area.visit
|
|
|
|
config_area.name_filter_input.fill_in(with: "stringthatshouldnotmatchanything")
|
|
expect(config_area).to have_no_components_found_text
|
|
expect(config_area).to have_no_components
|
|
expect(config_area).to have_name_filter_input
|
|
expect(config_area).to have_status_selector
|
|
end
|
|
|
|
it "navigates to the component page when clicking the Edit button" do
|
|
config_area.visit
|
|
|
|
config_area.component(enabled_component.id).edit_button.click
|
|
expect(page).to have_current_path("/admin/customize/themes/#{enabled_component.id}")
|
|
end
|
|
|
|
it "displays various metadata for components" do
|
|
disabled_component.update!(parent_themes: [parent_theme])
|
|
remote_component.update!(parent_themes: [parent_theme, parent_theme_2])
|
|
remote_component_with_update.update!(
|
|
parent_themes: [parent_theme, parent_theme_2, parent_theme_3],
|
|
)
|
|
|
|
config_area.visit
|
|
|
|
expect(config_area.component(remote_component.id)).to have_author("CDCK Inc.")
|
|
expect(config_area.component(remote_component.id)).to have_description(
|
|
"Description of my remote component",
|
|
)
|
|
expect(config_area.component(remote_component_with_update.id)).to have_description(
|
|
"Display and organize topics using a Kanban board interface.",
|
|
)
|
|
expect(config_area.component(remote_component.id)).to be_not_pending_update
|
|
|
|
expect(config_area.component(remote_component_with_update.id)).to be_pending_update
|
|
|
|
expect(config_area.component(disabled_component.id)).to have_one_parent_theme("A theme")
|
|
expect(config_area.component(remote_component.id)).to have_two_parent_themes(
|
|
"A theme",
|
|
"B theme",
|
|
)
|
|
expect(config_area.component(remote_component_with_update.id)).to have_three_parent_themes(
|
|
"A theme",
|
|
"B theme",
|
|
"C theme",
|
|
)
|
|
expect(config_area.component(enabled_component.id)).to have_three_and_more_parent_themes(
|
|
"A theme",
|
|
"B theme",
|
|
"C theme",
|
|
1,
|
|
)
|
|
end
|
|
|
|
it "shows actions that make sense for each component" do
|
|
config_area.visit
|
|
|
|
config_area.component(enabled_component.id).more_actions_menu.expand
|
|
expect(config_area.component(enabled_component.id)).to have_no_check_for_updates_button
|
|
expect(config_area.component(enabled_component.id)).to have_no_update_button
|
|
expect(config_area.component(enabled_component.id).preview_button["href"]).to end_with(
|
|
"/admin/themes/#{enabled_component.id}/preview",
|
|
)
|
|
expect(config_area.component(enabled_component.id).export_button["href"]).to end_with(
|
|
"/admin/customize/themes/#{enabled_component.id}/export",
|
|
)
|
|
config_area.component(enabled_component.id).more_actions_menu.collapse
|
|
|
|
config_area.component(disabled_component.id).more_actions_menu.expand
|
|
expect(config_area.component(disabled_component.id)).to have_no_check_for_updates_button
|
|
expect(config_area.component(disabled_component.id)).to have_no_update_button
|
|
expect(config_area.component(disabled_component.id).preview_button["href"]).to end_with(
|
|
"/admin/themes/#{disabled_component.id}/preview",
|
|
)
|
|
expect(config_area.component(disabled_component.id).export_button["href"]).to end_with(
|
|
"/admin/customize/themes/#{disabled_component.id}/export",
|
|
)
|
|
config_area.component(disabled_component.id).more_actions_menu.collapse
|
|
|
|
config_area.component(remote_component.id).more_actions_menu.expand
|
|
expect(config_area.component(remote_component.id)).to have_check_for_updates_button
|
|
expect(config_area.component(remote_component.id)).to have_no_update_button
|
|
expect(config_area.component(remote_component.id).preview_button["href"]).to end_with(
|
|
"/admin/themes/#{remote_component.id}/preview",
|
|
)
|
|
expect(config_area.component(remote_component.id).export_button["href"]).to end_with(
|
|
"/admin/customize/themes/#{remote_component.id}/export",
|
|
)
|
|
config_area.component(remote_component.id).more_actions_menu.collapse
|
|
|
|
config_area.component(remote_component_with_update.id).more_actions_menu.expand
|
|
expect(
|
|
config_area.component(remote_component_with_update.id),
|
|
).to have_no_check_for_updates_button
|
|
expect(config_area.component(remote_component_with_update.id)).to have_update_button
|
|
expect(
|
|
config_area.component(remote_component_with_update.id).preview_button["href"],
|
|
).to end_with("/admin/themes/#{remote_component_with_update.id}/preview")
|
|
expect(
|
|
config_area.component(remote_component_with_update.id).export_button["href"],
|
|
).to end_with("/admin/customize/themes/#{remote_component_with_update.id}/export")
|
|
config_area.component(remote_component_with_update.id).more_actions_menu.collapse
|
|
end
|
|
|
|
it "can delete a component" do
|
|
config_area.visit
|
|
|
|
config_area.component(disabled_component.id).more_actions_menu.expand
|
|
config_area.component(disabled_component.id).delete_button.click
|
|
|
|
dialog.click_yes
|
|
|
|
expect(toasts).to have_success(
|
|
I18n.t(
|
|
"admin_js.admin.config_areas.themes_and_components.components.deleted_successfully",
|
|
name: disabled_component.name,
|
|
),
|
|
)
|
|
expect(Theme.find_by(id: disabled_component.id)).to eq(nil)
|
|
expect(config_area).to have_no_component(disabled_component.id)
|
|
end
|
|
|
|
describe "checking for updates" do
|
|
let(:repo) do
|
|
setup_git_repo("about.json" => { name: "discourse-component-tt1", component: true }.to_json)
|
|
end
|
|
|
|
let(:url) do
|
|
MockGitImporter.register("https://example.com/discourse-component-tt1.git", repo)
|
|
end
|
|
|
|
before do
|
|
remote_component.remote_theme.update!(remote_url: url)
|
|
remote_component.remote_theme.update_from_remote
|
|
end
|
|
|
|
after { `rm -fr #{repo}` }
|
|
|
|
around(:each) { |group| MockGitImporter.with_mock { group.run } }
|
|
|
|
it 'shows an "Update to latest" button if there is a new update' do
|
|
config_area.visit
|
|
config_area.component(remote_component.id).more_actions_menu.expand
|
|
|
|
add_to_git_repo(repo, "about.json" => { name: "updated-name", component: true }.to_json)
|
|
|
|
config_area.component(remote_component.id).check_for_updates_button.click
|
|
|
|
expect(toasts).to have_default(
|
|
I18n.t(
|
|
"admin_js.admin.config_areas.themes_and_components.components.new_update_for_component",
|
|
name: remote_component.name,
|
|
),
|
|
)
|
|
expect(config_area.component(remote_component.id)).to have_update_button
|
|
expect(config_area.component(remote_component.id)).to be_pending_update
|
|
end
|
|
|
|
it 'keeps the "Check for updates" button if there is no new update' do
|
|
config_area.visit
|
|
config_area.component(remote_component.id).more_actions_menu.expand
|
|
|
|
config_area.component(remote_component.id).check_for_updates_button.click
|
|
|
|
expect(toasts).to have_default(
|
|
I18n.t(
|
|
"admin_js.admin.config_areas.themes_and_components.components.component_up_to_date",
|
|
name: remote_component.name,
|
|
),
|
|
)
|
|
expect(config_area.component(remote_component.id)).to have_check_for_updates_button
|
|
expect(config_area.component(remote_component.id)).to be_not_pending_update
|
|
end
|
|
end
|
|
|
|
describe "performing an update" do
|
|
let(:repo) do
|
|
setup_git_repo("about.json" => { name: "discourse-component-tt2", component: true }.to_json)
|
|
end
|
|
|
|
let(:url) do
|
|
MockGitImporter.register("https://example.com/discourse-component-tt2.git", repo)
|
|
end
|
|
|
|
before do
|
|
remote_component_with_update.remote_theme.update!(remote_url: url)
|
|
remote_component_with_update.remote_theme.update_from_remote
|
|
|
|
add_to_git_repo(repo, "about.json" => { name: "updated-name-tt2", component: true }.to_json)
|
|
remote_component_with_update.remote_theme.update_remote_version
|
|
end
|
|
|
|
after { `rm -fr #{repo}` }
|
|
|
|
around(:each) { |group| MockGitImporter.with_mock { group.run } }
|
|
|
|
it 'shows the "Check for updates" button after updating' do
|
|
config_area.visit
|
|
|
|
config_area.component(remote_component_with_update.id).more_actions_menu.expand
|
|
config_area.component(remote_component_with_update.id).update_button.click
|
|
|
|
expect(toasts).to have_success(
|
|
I18n.t(
|
|
"admin_js.admin.config_areas.themes_and_components.components.updated_successfully",
|
|
name: remote_component_with_update.name,
|
|
),
|
|
)
|
|
expect(
|
|
config_area.component(remote_component_with_update.id),
|
|
).to have_check_for_updates_button
|
|
expect(config_area.component(remote_component_with_update.id)).to be_not_pending_update
|
|
end
|
|
end
|
|
|
|
it "loads more components when scrolling to the bottom" do
|
|
Fabricate.times(4, :theme, component: true)
|
|
|
|
stub_const(Admin::Config::CustomizeController, "PAGE_SIZE", 4) do
|
|
resize_window(height: 800) do
|
|
config_area.visit
|
|
|
|
expect(config_area).to have_exactly_n_components(4)
|
|
|
|
page.execute_script("window.scrollTo(0, document.body.scrollHeight)")
|
|
|
|
expect(config_area).to have_component(enabled_component.id)
|
|
|
|
expect(config_area).to have_exactly_n_components(8)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when there are no components installed" do
|
|
it "doesn't display filters when there are no components installed" do
|
|
config_area.visit
|
|
|
|
expect(config_area).to have_no_components_installed_text
|
|
expect(config_area).to have_no_components
|
|
expect(config_area).to have_no_status_selector
|
|
expect(config_area).to have_no_name_filter_input
|
|
end
|
|
end
|
|
end
|