mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-24 11:34:26 +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)") ```
249 lines
6.2 KiB
JavaScript
Vendored
249 lines
6.2 KiB
JavaScript
Vendored
import { tracked } from "@glimmer/tracking";
|
|
import Controller from "@ember/controller";
|
|
import { action, computed } from "@ember/object";
|
|
import { service } from "@ember/service";
|
|
import { TrackedArray } from "@ember-compat/tracked-built-ins";
|
|
import CanCheckEmailsHelper from "discourse/lib/can-check-emails-helper";
|
|
import { computedI18n, setting } from "discourse/lib/computed";
|
|
import discourseDebounce from "discourse/lib/debounce";
|
|
import discourseComputed, { bind } from "discourse/lib/decorators";
|
|
import { INPUT_DELAY } from "discourse/lib/environment";
|
|
import { i18n } from "discourse-i18n";
|
|
import BulkUserDeleteConfirmation from "admin/components/bulk-user-delete-confirmation";
|
|
import AdminUser from "admin/models/admin-user";
|
|
|
|
const MAX_BULK_SELECT_LIMIT = 100;
|
|
|
|
export default class AdminUsersListShowController extends Controller {
|
|
@service dialog;
|
|
@service modal;
|
|
@service toasts;
|
|
|
|
@tracked bulkSelect = false;
|
|
@tracked displayBulkActions = false;
|
|
@tracked bulkSelectedUserIdsSet = new Set();
|
|
@tracked bulkSelectedUsersMap = {};
|
|
@setting("moderators_view_emails") canModeratorsViewEmails;
|
|
|
|
query = null;
|
|
order = null;
|
|
asc = null;
|
|
showEmails = false;
|
|
refreshing = false;
|
|
listFilter = null;
|
|
lastSelected = null;
|
|
lastBulkDeleteMessageBusId = null;
|
|
|
|
@computedI18n("search_hint") searchHint;
|
|
|
|
_page = 1;
|
|
_results = new TrackedArray();
|
|
_canLoadMore = true;
|
|
|
|
get users() {
|
|
return this._results.flat();
|
|
}
|
|
|
|
@discourseComputed("query")
|
|
title(query) {
|
|
return i18n("admin.users.titles." + query);
|
|
}
|
|
|
|
@discourseComputed("showEmails")
|
|
columnCount(showEmails) {
|
|
let colCount = 7; // note that the first column is hardcoded in the template
|
|
|
|
if (showEmails) {
|
|
colCount += 1;
|
|
}
|
|
|
|
if (this.siteSettings.must_approve_users) {
|
|
colCount += 1;
|
|
}
|
|
|
|
return colCount;
|
|
}
|
|
|
|
@computed("model.id", "currentUser.id")
|
|
get canCheckEmails() {
|
|
return new CanCheckEmailsHelper(
|
|
this.model?.id,
|
|
this.canModeratorsViewEmails,
|
|
this.currentUser
|
|
).canCheckEmails;
|
|
}
|
|
|
|
@computed("model.id", "currentUser.id")
|
|
get canAdminCheckEmails() {
|
|
return new CanCheckEmailsHelper(
|
|
this.model?.id,
|
|
this.canModeratorsViewEmails,
|
|
this.currentUser
|
|
).canAdminCheckEmails;
|
|
}
|
|
|
|
@computed("query")
|
|
get showSilenceReason() {
|
|
return this.query === "silenced";
|
|
}
|
|
|
|
resetFilters() {
|
|
this._page = 1;
|
|
this._results.length = 0;
|
|
this._canLoadMore = true;
|
|
return this._refreshUsers();
|
|
}
|
|
|
|
stripHtml(html) {
|
|
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
return doc.body.textContent || "";
|
|
}
|
|
|
|
_refreshUsers() {
|
|
if (!this._canLoadMore) {
|
|
return;
|
|
}
|
|
|
|
const page = this._page;
|
|
this.set("refreshing", true);
|
|
|
|
return AdminUser.findAll(this.query, {
|
|
filter: this.listFilter,
|
|
show_emails: this.showEmails,
|
|
order: this.order,
|
|
asc: this.asc,
|
|
page,
|
|
})
|
|
.then((result) => {
|
|
this.lastBulkDeleteMessageBusId =
|
|
result.meta?.message_bus_last_ids?.bulk_delete;
|
|
|
|
this._results[page] = result.users;
|
|
|
|
if (result.users.length === 0) {
|
|
this._canLoadMore = false;
|
|
}
|
|
})
|
|
.finally(() => {
|
|
this.set("refreshing", false);
|
|
});
|
|
}
|
|
|
|
@action
|
|
onListFilterChange(event) {
|
|
this.set("listFilter", event.target.value);
|
|
discourseDebounce(this, this.resetFilters, INPUT_DELAY);
|
|
}
|
|
|
|
@action
|
|
loadMore() {
|
|
if (this.refreshing) {
|
|
return;
|
|
}
|
|
this._page += 1;
|
|
this._refreshUsers();
|
|
}
|
|
|
|
@action
|
|
toggleEmailVisibility() {
|
|
this.toggleProperty("showEmails");
|
|
this.resetFilters();
|
|
}
|
|
|
|
@action
|
|
updateOrder(field, asc) {
|
|
this.setProperties({
|
|
order: field,
|
|
asc,
|
|
});
|
|
}
|
|
|
|
@action
|
|
toggleBulkSelect() {
|
|
this.bulkSelect = !this.bulkSelect;
|
|
this.displayBulkActions = false;
|
|
this.bulkSelectedUsersMap = {};
|
|
this.bulkSelectedUserIdsSet = new Set();
|
|
}
|
|
|
|
@action
|
|
bulkSelectItemToggle(userId, event) {
|
|
if (event.target.checked) {
|
|
if (!this.#canBulkSelectMoreUsers(1)) {
|
|
this.#showBulkSelectionLimitToast(event);
|
|
return;
|
|
}
|
|
|
|
if (event.shiftKey && this.lastSelected) {
|
|
const list = Array.from(
|
|
document.querySelectorAll(
|
|
"input.directory-table__cell-bulk-select:not([disabled])"
|
|
)
|
|
);
|
|
const lastSelectedIndex = list.indexOf(this.lastSelected);
|
|
if (lastSelectedIndex !== -1) {
|
|
const newSelectedIndex = list.indexOf(event.target);
|
|
const start = Math.min(lastSelectedIndex, newSelectedIndex);
|
|
const end = Math.max(lastSelectedIndex, newSelectedIndex);
|
|
|
|
if (!this.#canBulkSelectMoreUsers(end - start)) {
|
|
this.#showBulkSelectionLimitToast(event);
|
|
return;
|
|
}
|
|
|
|
list.slice(start, end).forEach((input) => {
|
|
input.checked = true;
|
|
this.#addUserToBulkSelection(parseInt(input.dataset.userId, 10));
|
|
});
|
|
}
|
|
}
|
|
this.#addUserToBulkSelection(userId);
|
|
this.lastSelected = event.target;
|
|
} else {
|
|
this.bulkSelectedUserIdsSet.delete(userId);
|
|
delete this.bulkSelectedUsersMap[userId];
|
|
}
|
|
|
|
this.displayBulkActions = this.bulkSelectedUserIdsSet.size > 0;
|
|
}
|
|
|
|
@bind
|
|
async afterBulkDelete() {
|
|
await this.resetFilters();
|
|
this.bulkSelectedUsersMap = {};
|
|
this.bulkSelectedUserIdsSet = new Set();
|
|
this.displayBulkActions = false;
|
|
}
|
|
|
|
@action
|
|
openBulkDeleteConfirmation() {
|
|
this.modal.show(BulkUserDeleteConfirmation, {
|
|
model: {
|
|
lastBulkDeleteMessageBusId: this.lastBulkDeleteMessageBusId,
|
|
userIds: Array.from(this.bulkSelectedUserIdsSet),
|
|
afterBulkDelete: this.afterBulkDelete,
|
|
},
|
|
});
|
|
}
|
|
|
|
#addUserToBulkSelection(userId) {
|
|
this.bulkSelectedUserIdsSet.add(userId);
|
|
this.bulkSelectedUsersMap[userId] = 1;
|
|
}
|
|
|
|
#canBulkSelectMoreUsers(count) {
|
|
return this.bulkSelectedUserIdsSet.size + count <= MAX_BULK_SELECT_LIMIT;
|
|
}
|
|
|
|
#showBulkSelectionLimitToast(event) {
|
|
this.toasts.error({
|
|
duration: 3000,
|
|
data: {
|
|
message: i18n("admin.users.bulk_actions.too_many_selected_users", {
|
|
count: MAX_BULK_SELECT_LIMIT,
|
|
}),
|
|
},
|
|
});
|
|
event.preventDefault();
|
|
}
|
|
}
|