This repository has been archived on 2023-03-18. You can view files and clone it, but cannot push or open issues or pull requests.
osr-discourse-src/app/assets/javascripts/discourse/tests/acceptance/preferences-test.js
Jeff Wong d1bdb6c65d
FEATURE: upload an avatar option for uploading avatars with selectable avatars (#15878)
* FEATURE: upload an avatar option for uploading avatars with selectable avatars

Allow staff or users at or above a trust level to upload avatars even when the site
has selectable avatars enabled.

Everyone can still pick from the list of avatars. The option to upload is shown
below the selectable avatar list.

refactored boolean site setting into an enum with the following values:

disabled: No selectable avatars enabled (default)
everyone: Show selectable avatars, and allow everyone to upload custom avatars
tl1: Show selectable avatars, but require tl1+ and staff to upload custom avatars
tl2: Show selectable avatars, but require tl2+ and staff to upload custom avatars
tl3: Show selectable avatars, but require tl3+ and staff to upload custom avatars
tl4: Show selectable avatars, but require tl4 and staff to upload custom avatars
staff: Show selectable avatars, but only allow staff to upload custom avatars
no_one: Show selectable avatars. No users can upload custom avatars

Co-authored-by: Régis Hanol <regis@hanol.fr>
2022-02-24 12:57:39 -08:00

693 lines
20 KiB
JavaScript

import {
acceptance,
count,
exists,
queryAll,
updateCurrentUser,
} from "discourse/tests/helpers/qunit-helpers";
import {
click,
currentRouteName,
currentURL,
fillIn,
visit,
} from "@ember/test-helpers";
import I18n from "I18n";
import User from "discourse/models/user";
import selectKit from "discourse/tests/helpers/select-kit-helper";
import { test } from "qunit";
function preferencesPretender(server, helper) {
server.post("/u/second_factors.json", () => {
return helper.response({
success: "OK",
password_required: "true",
});
});
server.post("/u/create_second_factor_totp.json", () => {
return helper.response({
key: "rcyryaqage3jexfj",
qr: "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=",
});
});
server.post("/u/create_second_factor_security_key.json", () => {
return helper.response({
challenge: "a6d393d12654c130b2273e68ca25ca232d1d7f4c2464c2610fb8710a89d4",
rp_id: "localhost",
rp_name: "Discourse",
supported_algorithms: [-7, -257],
});
});
server.post("/u/enable_second_factor_totp.json", () => {
return helper.response({ error: "invalid token" });
});
server.put("/u/second_factors_backup.json", () => {
return helper.response({
backup_codes: ["dsffdsd", "fdfdfdsf", "fddsds"],
});
});
server.post("/u/eviltrout/preferences/revoke-account", () => {
return helper.response({
success: true,
});
});
server.put("/u/eviltrout/preferences/email", () => {
return helper.response({
success: true,
});
});
server.post("/user_avatar/eviltrout/refresh_gravatar.json", () => {
return helper.response({
gravatar_upload_id: 6543,
gravatar_avatar_template: "/images/avatar.png",
});
});
server.get("/u/eviltrout/activity.json", () => {
return helper.response({});
});
}
acceptance("User Preferences", function (needs) {
needs.user();
needs.pretender(preferencesPretender);
test("update some fields", async function (assert) {
await visit("/u/eviltrout/preferences");
assert.ok($("body.user-preferences-page").length, "has the body class");
assert.strictEqual(
currentURL(),
"/u/eviltrout/preferences/account",
"defaults to account tab"
);
assert.ok(exists(".user-preferences"), "it shows the preferences");
const savePreferences = async () => {
assert.ok(!exists(".saved"), "it hasn't been saved yet");
await click(".save-changes");
assert.ok(exists(".saved"), "it displays the saved message");
queryAll(".saved").remove();
};
await fillIn(".pref-name input[type=text]", "Jon Snow");
await savePreferences();
await click(".preferences-nav .nav-profile a");
await fillIn("#edit-location", "Westeros");
await savePreferences();
await click(".preferences-nav .nav-emails a");
await click(".pref-activity-summary input[type=checkbox]");
await savePreferences();
await click(".preferences-nav .nav-notifications a");
await selectKit(
".control-group.notifications .combo-box.duration"
).expand();
await selectKit(
".control-group.notifications .combo-box.duration"
).selectRowByValue(1440);
await savePreferences();
await click(".preferences-nav .nav-categories a");
const categorySelector = selectKit(
".tracking-controls .category-selector "
);
await categorySelector.expand();
await categorySelector.fillInFilter("faq");
await savePreferences();
assert.ok(
!exists(".preferences-nav .nav-tags a"),
"tags tab isn't there when tags are disabled"
);
await click(".preferences-nav .nav-interface a");
await click(".control-group.other input[type=checkbox]:nth-of-type(1)");
await savePreferences();
assert.ok(
!exists(".preferences-nav .nav-apps a"),
"apps tab isn't there when you have no authorized apps"
);
});
test("username", async function (assert) {
await visit("/u/eviltrout/preferences/username");
assert.ok(exists("#change_username"), "it has the input element");
});
test("email", async function (assert) {
await visit("/u/eviltrout/preferences/email");
assert.ok(exists("#change-email"), "it has the input element");
await fillIn("#change-email", "invalidemail");
assert.strictEqual(
queryAll(".tip.bad").text().trim(),
I18n.t("user.email.invalid"),
"it should display invalid email tip"
);
});
test("email field always shows up", async function (assert) {
await visit("/u/eviltrout/preferences/email");
assert.ok(exists("#change-email"), "it has the input element");
await fillIn("#change-email", "eviltrout@discourse.org");
await click(".user-preferences button.btn-primary");
await visit("/u/eviltrout/preferences");
await visit("/u/eviltrout/preferences/email");
assert.ok(exists("#change-email"), "it has the input element");
});
test("connected accounts", async function (assert) {
await visit("/u/eviltrout/preferences/account");
assert.ok(
exists(".pref-associated-accounts"),
"it has the connected accounts section"
);
assert.ok(
queryAll(
".pref-associated-accounts table tr:nth-of-type(1) td:nth-of-type(1)"
)
.html()
.indexOf("Facebook") > -1,
"it lists facebook"
);
await click(
".pref-associated-accounts table tr:nth-of-type(1) td:last-child button"
);
queryAll(".pref-associated-accounts table tr:nth-of-type(1) td:last button")
.html()
.indexOf("Connect") > -1;
});
test("second factor totp", async function (assert) {
await visit("/u/eviltrout/preferences/second-factor");
assert.ok(exists("#password"), "it has a password input");
await fillIn("#password", "secrets");
await click(".user-preferences .btn-primary");
assert.notOk(exists("#password"), "it hides the password input");
await click(".new-totp");
assert.ok(exists(".qr-code img"), "shows qr code image");
await click(".add-totp");
assert.ok(
queryAll(".alert-error").html().indexOf("provide a name and the code") >
-1,
"shows name/token missing error message"
);
});
test("second factor security keys", async function (assert) {
await visit("/u/eviltrout/preferences/second-factor");
assert.ok(exists("#password"), "it has a password input");
await fillIn("#password", "secrets");
await click(".user-preferences .btn-primary");
assert.notOk(exists("#password"), "it hides the password input");
await click(".new-security-key");
assert.ok(exists("#security-key-name"), "shows security key name input");
fillIn("#security-key-name", "");
// The following tests can only run when Webauthn is enabled. This is not
// always the case, for example on a browser running on a non-standard port
if (typeof PublicKeyCredential !== "undefined") {
await click(".add-security-key");
assert.ok(
queryAll(".alert-error").html().indexOf("provide a name") > -1,
"shows name missing error message"
);
}
});
test("default avatar selector", async function (assert) {
await visit("/u/eviltrout/preferences");
await click(".pref-avatar .btn");
assert.ok(exists(".avatar-choice", "opens the avatar selection modal"));
await click(".avatar-selector-refresh-gravatar");
assert.strictEqual(
User.currentProp("gravatar_avatar_upload_id"),
6543,
"it should set the gravatar_avatar_upload_id property"
);
});
});
acceptance("Second Factor Backups", function (needs) {
needs.user();
needs.pretender((server, helper) => {
server.post("/u/second_factors.json", () => {
return helper.response({
success: "OK",
totps: [{ id: 1, name: "one of them" }],
});
});
server.put("/u/second_factors_backup.json", () => {
return helper.response({
backup_codes: ["dsffdsd", "fdfdfdsf", "fddsds"],
});
});
server.get("/u/eviltrout/activity.json", () => {
return helper.response({});
});
});
test("second factor backup", async function (assert) {
updateCurrentUser({ second_factor_enabled: true });
await visit("/u/eviltrout/preferences/second-factor");
await click(".edit-2fa-backup");
assert.ok(
exists(".second-factor-backup-preferences"),
"shows the 2fa backup panel"
);
await click(".second-factor-backup-preferences .btn-primary");
assert.ok(exists(".backup-codes-area"), "shows backup codes");
});
});
acceptance(
"Avatar selector when selectable avatars is enabled",
function (needs) {
needs.user();
needs.settings({ selectable_avatars_mode: "no_one" });
needs.pretender((server, helper) => {
server.get("/site/selectable-avatars.json", () =>
helper.response([
"https://www.discourse.org",
"https://meta.discourse.org",
])
);
});
test("selectable avatars", async function (assert) {
await visit("/u/eviltrout/preferences");
await click(".pref-avatar .btn");
assert.ok(
exists(".selectable-avatars", "opens the avatar selection modal")
);
assert.notOk(
exists(
"#uploaded-avatar",
"avatar selection modal does not include option to upload"
)
);
});
}
);
acceptance(
"Avatar selector when selectable avatars allows staff to upload",
function (needs) {
needs.user();
needs.settings({ selectable_avatars_mode: "staff" });
needs.pretender((server, helper) => {
server.get("/site/selectable-avatars.json", () =>
helper.response([
"https://www.discourse.org",
"https://meta.discourse.org",
])
);
});
test("allows staff to upload", async function (assert) {
await updateCurrentUser({
trust_level: 3,
moderator: true,
admin: false,
});
await visit("/u/eviltrout/preferences");
await click(".pref-avatar .btn");
assert.ok(
exists(".selectable-avatars", "opens the avatar selection modal")
);
assert.ok(
exists(
"#uploaded-avatar",
"avatar selection modal includes option to upload"
)
);
});
test("disallow nonstaff", async function (assert) {
await visit("/u/eviltrout/preferences");
await updateCurrentUser({
trust_level: 3,
moderator: false,
admin: false,
});
await click(".pref-avatar .btn");
assert.ok(
exists(".selectable-avatars", "opens the avatar selection modal")
);
assert.notOk(
exists(
"#uploaded-avatar",
"avatar selection modal does not include option to upload"
)
);
});
}
);
acceptance(
"Avatar selector when selectable avatars allows trust level 3+ to upload",
function (needs) {
needs.user();
needs.settings({ selectable_avatars_mode: "tl3" });
needs.pretender((server, helper) => {
server.get("/site/selectable-avatars.json", () =>
helper.response([
"https://www.discourse.org",
"https://meta.discourse.org",
])
);
});
test("with a tl3 user", async function (assert) {
await visit("/u/eviltrout/preferences");
await updateCurrentUser({
trust_level: 3,
moderator: false,
admin: false,
});
await click(".pref-avatar .btn");
assert.ok(
exists(".selectable-avatars", "opens the avatar selection modal")
);
assert.ok(
exists(
"#uploaded-avatar",
"avatar selection modal does includes option to upload"
)
);
});
test("with a tl2 user", async function (assert) {
await visit("/u/eviltrout/preferences");
await updateCurrentUser({
trust_level: 2,
moderator: false,
admin: false,
});
await click(".pref-avatar .btn");
assert.ok(
exists(".selectable-avatars", "opens the avatar selection modal")
);
assert.notOk(
exists(
"#uploaded-avatar",
"avatar selection modal does not include option to upload"
)
);
});
test("always allow staff to upload", async function (assert) {
await visit("/u/eviltrout/preferences");
await updateCurrentUser({
trust_level: 2,
moderator: true,
admin: false,
});
await click(".pref-avatar .btn");
assert.ok(
exists(".selectable-avatars", "opens the avatar selection modal")
);
assert.ok(
exists(
"#uploaded-avatar",
"avatar selection modal includes option to upload"
)
);
});
}
);
acceptance("User Preferences when badges are disabled", function (needs) {
needs.user();
needs.settings({ enable_badges: false });
needs.pretender(preferencesPretender);
test("visit my preferences", async function (assert) {
await visit("/u/eviltrout/preferences");
assert.ok($("body.user-preferences-page").length, "has the body class");
assert.strictEqual(
currentURL(),
"/u/eviltrout/preferences/account",
"defaults to account tab"
);
assert.ok(exists(".user-preferences"), "it shows the preferences");
});
});
acceptance(
"User can select a topic to feature on profile if site setting in enabled",
function (needs) {
needs.user();
needs.settings({ allow_featured_topic_on_user_profiles: true });
needs.pretender((server, helper) => {
server.put("/u/eviltrout/feature-topic", () => {
return helper.response({
success: true,
});
});
});
test("setting featured topic on profile", async function (assert) {
await visit("/u/eviltrout/preferences/profile");
assert.ok(
!exists(".featured-topic-link"),
"no featured topic link to present"
);
assert.ok(
!exists(".clear-feature-topic-on-profile-btn"),
"clear button not present"
);
const selectTopicBtn = queryAll(
".feature-topic-on-profile-btn:nth-of-type(1)"
)[0];
assert.ok(exists(selectTopicBtn), "feature topic button is present");
await click(selectTopicBtn);
assert.ok(
exists(".feature-topic-on-profile"),
"topic picker modal is open"
);
const topicRadioBtn = queryAll(
'input[name="choose_topic_id"]:nth-of-type(1)'
)[0];
assert.ok(exists(topicRadioBtn), "Topic options are prefilled");
await click(topicRadioBtn);
await click(".save-featured-topic-on-profile");
assert.ok(
exists(".featured-topic-link"),
"link to featured topic is present"
);
assert.ok(
exists(".clear-feature-topic-on-profile-btn"),
"clear button is present"
);
});
}
);
acceptance("Custom User Fields", function (needs) {
needs.user();
needs.site({
user_fields: [
{
id: 30,
name: "What kind of pet do you have?",
field_type: "dropdown",
options: ["Dog", "Cat", "Hamster"],
required: true,
},
],
});
needs.pretender(preferencesPretender);
test("can select an option from a dropdown", async function (assert) {
await visit("/u/eviltrout/preferences/profile");
assert.ok(exists(".user-field"), "it has at least one user field");
await click(".user-field.dropdown");
const field = selectKit(
".user-field-what-kind-of-pet-do-you-have .combo-box"
);
await field.expand();
await field.selectRowByValue("Cat");
assert.strictEqual(
field.header().value(),
"Cat",
"it sets the value of the field"
);
});
});
acceptance(
"User Preferences, selecting bookmarks discovery as user's default homepage",
function (needs) {
needs.user();
needs.settings({
top_menu: "categories|latest|top|bookmarks",
});
test("selecting bookmarks as home directs home to bookmarks", async function (assert) {
await visit("/u/eviltrout/preferences/interface");
assert.ok(exists(".home .combo-box"), "it has a home selector combo-box");
const field = selectKit(".home .combo-box");
await field.expand();
await field.selectRowByValue("6");
await click(".save-changes");
await visit("/");
assert.ok(exists(".topic-list"), "The list of topics was rendered");
assert.strictEqual(
currentRouteName(),
"discovery.bookmarks",
"it navigates to bookmarks"
);
});
}
);
acceptance("Ignored users", function (needs) {
needs.user();
needs.settings({ min_trust_level_to_allow_ignore: 1 });
test("when trust level < min level to ignore", async function (assert) {
await visit(`/u/eviltrout/preferences/users`);
await updateCurrentUser({ trust_level: 0, moderator: false, admin: false });
assert.ok(
!exists(".user-ignore"),
"it does not show the list of ignored users"
);
});
test("when trust level >= min level to ignore", async function (assert) {
await visit(`/u/eviltrout/preferences/users`);
await updateCurrentUser({ trust_level: 1 });
assert.ok(exists(".user-ignore"), "it shows the list of ignored users");
});
test("staff can always see ignored users", async function (assert) {
await visit(`/u/eviltrout/preferences/users`);
await updateCurrentUser({ moderator: true });
assert.ok(exists(".user-ignore"), "it shows the list of ignored users");
});
});
acceptance("Security", function (needs) {
needs.user();
needs.pretender(preferencesPretender);
test("recently connected devices", async function (assert) {
await visit("/u/eviltrout/preferences/security");
assert.strictEqual(
queryAll(".auth-tokens > .auth-token:nth-of-type(1) .auth-token-device")
.text()
.trim(),
"Linux Computer",
"it should display active token first"
);
assert.strictEqual(
queryAll(".pref-auth-tokens > a:nth-of-type(1)").text().trim(),
I18n.t("user.auth_tokens.show_all", { count: 3 }),
"it should display two tokens"
);
assert.strictEqual(
count(".pref-auth-tokens .auth-token"),
2,
"it should display two tokens"
);
await click(".pref-auth-tokens > a:nth-of-type(1)");
assert.strictEqual(
count(".pref-auth-tokens .auth-token"),
3,
"it should display three tokens"
);
const authTokenDropdown = selectKit(".auth-token-dropdown");
await authTokenDropdown.expand();
await authTokenDropdown.selectRowByValue("notYou");
assert.strictEqual(count(".d-modal:visible"), 1, "modal should appear");
await click(".modal-footer .btn-primary");
assert.strictEqual(
count(".pref-password.highlighted"),
1,
"it should highlight password preferences"
);
});
});
acceptance(
"User Preferences for staged user and don't allow tracking prefs",
function (needs) {
needs.settings({
allow_changing_staged_user_tracking: false,
tagging_enabled: true,
});
needs.pretender(preferencesPretender);
test("staged user doesn't show category and tag preferences", async function (assert) {
await visit("/u/staged/preferences");
assert.ok($("body.user-preferences-page").length, "has the body class");
assert.strictEqual(
currentURL(),
"/u/staged/preferences/account",
"defaults to account tab"
);
assert.ok(exists(".user-preferences"), "it shows the preferences");
assert.ok(
!exists(".preferences-nav .nav-categories a"),
"categories tab isn't there for staged users"
);
assert.ok(
!exists(".preferences-nav .nav-tags a"),
"tags tab isn't there for staged users"
);
});
}
);