mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-23 09:54:56 +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)") ```
213 lines
5.6 KiB
Text
Vendored
213 lines
5.6 KiB
Text
Vendored
import Component from "@glimmer/component";
|
|
import { tracked } from "@glimmer/tracking";
|
|
import { on } from "@ember/modifier";
|
|
import { action } from "@ember/object";
|
|
import { service } from "@ember/service";
|
|
import { TrackedArray } from "@ember-compat/tracked-built-ins";
|
|
import { modifier as modifierFn } from "ember-modifier";
|
|
import DButton from "discourse/components/d-button";
|
|
import DModal from "discourse/components/d-modal";
|
|
import { ajax } from "discourse/lib/ajax";
|
|
import { extractError } from "discourse/lib/ajax-error";
|
|
import { bind } from "discourse/lib/decorators";
|
|
import { i18n } from "discourse-i18n";
|
|
|
|
const BULK_DELETE_CHANNEL = "/bulk-user-delete";
|
|
|
|
export default class BulkUserDeleteConfirmation extends Component {
|
|
@service messageBus;
|
|
|
|
@tracked confirmButtonDisabled = true;
|
|
@tracked deleteStarted = false;
|
|
@tracked logs = new TrackedArray();
|
|
failedUsernames = [];
|
|
|
|
callAfterBulkDelete = false;
|
|
blockIpAndEmail = false;
|
|
|
|
logsListener = modifierFn(() => {
|
|
this.messageBus.subscribe(
|
|
BULK_DELETE_CHANNEL,
|
|
this.onDeleteProgress,
|
|
this.args.model.lastBulkDeleteMessageBusId
|
|
);
|
|
|
|
return () => {
|
|
this.messageBus.unsubscribe(BULK_DELETE_CHANNEL, this.onDeleteProgress);
|
|
};
|
|
});
|
|
|
|
get confirmDeletePhrase() {
|
|
return i18n(
|
|
"admin.users.bulk_actions.delete.confirmation_modal.confirmation_phrase",
|
|
{ count: this.args.model.userIds.length }
|
|
);
|
|
}
|
|
|
|
#logError(line) {
|
|
this.#log(line, "error");
|
|
}
|
|
|
|
#logSuccess(line) {
|
|
this.#log(line, "success");
|
|
}
|
|
|
|
#logNeutral(line) {
|
|
this.#log(line, "neutral");
|
|
}
|
|
|
|
#log(line, type) {
|
|
this.logs.push({
|
|
line,
|
|
type,
|
|
});
|
|
}
|
|
|
|
@bind
|
|
onDeleteProgress(data) {
|
|
if (data.success) {
|
|
this.#logSuccess(
|
|
i18n(
|
|
"admin.users.bulk_actions.delete.confirmation_modal.user_delete_succeeded",
|
|
data
|
|
)
|
|
);
|
|
} else if (data.failed) {
|
|
this.failedUsernames.push(data.username);
|
|
this.#logError(
|
|
i18n(
|
|
"admin.users.bulk_actions.delete.confirmation_modal.user_delete_failed",
|
|
data
|
|
)
|
|
);
|
|
}
|
|
|
|
if (data.position === data.total) {
|
|
this.callAfterBulkDelete = true;
|
|
this.#logNeutral(
|
|
i18n(
|
|
"admin.users.bulk_actions.delete.confirmation_modal.bulk_delete_finished"
|
|
)
|
|
);
|
|
if (this.failedUsernames.length > 0) {
|
|
this.#logNeutral(
|
|
i18n(
|
|
"admin.users.bulk_actions.delete.confirmation_modal.failed_to_delete_users"
|
|
)
|
|
);
|
|
for (const username of this.failedUsernames) {
|
|
this.#logNeutral(`* ${username}`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@action
|
|
onPromptInput(event) {
|
|
this.confirmButtonDisabled =
|
|
event.target.value.toLowerCase() !== this.confirmDeletePhrase;
|
|
}
|
|
|
|
@action
|
|
async startDelete() {
|
|
this.deleteStarted = true;
|
|
this.confirmButtonDisabled = true;
|
|
this.#logNeutral(
|
|
i18n(
|
|
"admin.users.bulk_actions.delete.confirmation_modal.bulk_delete_starting"
|
|
)
|
|
);
|
|
|
|
try {
|
|
await ajax("/admin/users/destroy-bulk.json", {
|
|
type: "DELETE",
|
|
data: {
|
|
user_ids: this.args.model.userIds,
|
|
block_ip_and_email: this.blockIpAndEmail,
|
|
},
|
|
});
|
|
this.callAfterBulkDelete = true;
|
|
} catch (err) {
|
|
this.#logError(extractError(err));
|
|
this.confirmButtonDisabled = false;
|
|
}
|
|
}
|
|
|
|
@action
|
|
closeModal() {
|
|
this.args.closeModal();
|
|
if (this.callAfterBulkDelete) {
|
|
this.args.model?.afterBulkDelete();
|
|
}
|
|
}
|
|
|
|
@action
|
|
toggleBlockIpAndEmail(event) {
|
|
this.blockIpAndEmail = event.target.checked;
|
|
}
|
|
|
|
<template>
|
|
<DModal
|
|
class="bulk-user-delete-confirmation"
|
|
@closeModal={{this.closeModal}}
|
|
@title={{i18n
|
|
"admin.users.bulk_actions.delete.confirmation_modal.title"
|
|
count=@model.userIds.length
|
|
}}
|
|
{{this.logsListener}}
|
|
>
|
|
<:body>
|
|
{{#if this.deleteStarted}}
|
|
<div class="bulk-user-delete-confirmation__progress">
|
|
{{#each this.logs as |entry|}}
|
|
<div
|
|
class="bulk-user-delete-confirmation__progress-line -{{entry.type}}"
|
|
>
|
|
{{entry.line}}
|
|
</div>
|
|
{{/each}}
|
|
<div class="bulk-user-delete-confirmation__progress-anchor">
|
|
</div>
|
|
</div>
|
|
{{else}}
|
|
<p>{{i18n
|
|
"admin.users.bulk_actions.delete.confirmation_modal.prompt_text"
|
|
count=@model.userIds.length
|
|
confirmation_phrase=this.confirmDeletePhrase
|
|
}}
|
|
</p>
|
|
<input
|
|
class="confirmation-phrase"
|
|
type="text"
|
|
placeholder={{this.confirmDeletePhrase}}
|
|
{{on "input" this.onPromptInput}}
|
|
/>
|
|
<label class="checkbox-label">
|
|
<input
|
|
type="checkbox"
|
|
class="block-ip-and-email"
|
|
{{on "change" this.toggleBlockIpAndEmail}}
|
|
/>
|
|
{{i18n
|
|
"admin.users.bulk_actions.delete.confirmation_modal.block_ip_and_email"
|
|
}}
|
|
</label>
|
|
{{/if}}
|
|
</:body>
|
|
<:footer>
|
|
<DButton
|
|
class="confirm-delete btn-danger"
|
|
@icon="trash-can"
|
|
@label="admin.users.bulk_actions.delete.confirmation_modal.confirm"
|
|
@disabled={{this.confirmButtonDisabled}}
|
|
@action={{this.startDelete}}
|
|
/>
|
|
<DButton
|
|
class="btn-default"
|
|
@label="admin.users.bulk_actions.delete.confirmation_modal.close"
|
|
@action={{this.closeModal}}
|
|
/>
|
|
</:footer>
|
|
</DModal>
|
|
</template>
|
|
}
|