Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
532 lines
22 KiB
TypeScript
532 lines
22 KiB
TypeScript
import assert from "node:assert/strict";
|
||
|
||
import type {Page} from "puppeteer";
|
||
|
||
import {GENERIC_BOT_TYPE, OUTGOING_WEBHOOK_BOT_TYPE} from "../src/bot_type_values.ts";
|
||
|
||
import * as common from "./lib/common.ts";
|
||
import {test_credentials} from "./lib/common.ts";
|
||
|
||
const zuliprc_regex =
|
||
/^data:application\/octet-stream;charset=utf-8,\[api]\nemail=.+\nkey=.+\nsite=.+\n$/;
|
||
|
||
async function get_decoded_url_in_selector(page: Page, selector: string): Promise<string> {
|
||
const a = await page.$(`a:is(${selector})`);
|
||
return decodeURIComponent(await (await a!.getProperty("href")).jsonValue());
|
||
}
|
||
|
||
async function open_manage_bot_tab(page: Page, user_id: number): Promise<void> {
|
||
const manage_button_selector = `#personal_your_bots_table .user_row[data-user-id="${user_id}"] .manage-user-button`;
|
||
await page.waitForSelector(manage_button_selector, {visible: true});
|
||
await page.click(manage_button_selector);
|
||
|
||
// Wait for modal, then go to tab
|
||
await common.wait_for_micromodal_to_open(page);
|
||
}
|
||
|
||
async function open_settings(page: Page): Promise<void> {
|
||
await common.open_personal_menu(page);
|
||
|
||
const settings_selector = "#personal-menu-dropdown a[href^='#settings']";
|
||
await page.waitForSelector(settings_selector, {visible: true});
|
||
await page.click(settings_selector);
|
||
|
||
await page.waitForSelector("#settings_content .profile-settings-form", {visible: true});
|
||
const page_url = await common.page_url_with_fragment(page);
|
||
assert.ok(
|
||
page_url.includes("/#settings/"),
|
||
`Page url: ${page_url} does not contain /#settings/`,
|
||
);
|
||
// Wait for settings overlay to open.
|
||
await page.waitForSelector("#settings_overlay_container", {visible: true});
|
||
}
|
||
|
||
async function close_settings_and_date_picker(page: Page): Promise<void> {
|
||
const date_picker_selector = ".date-field-alt-input";
|
||
await page.click(date_picker_selector);
|
||
|
||
await page.waitForSelector(".flatpickr-calendar", {visible: true});
|
||
|
||
await page.keyboard.press("Escape");
|
||
await page.waitForSelector(".flatpickr-calendar", {hidden: true});
|
||
await page.waitForSelector("#settings_overlay_container", {hidden: true});
|
||
}
|
||
|
||
async function test_change_full_name(page: Page): Promise<void> {
|
||
await page.waitForSelector("#full_name", {visible: true});
|
||
await page.click("#full_name");
|
||
|
||
const full_name_input_selector = 'input[name="full_name"]';
|
||
await common.clear_and_type(page, full_name_input_selector, "New name");
|
||
|
||
await page.click("#settings_content .profile-settings-form");
|
||
await page.waitForSelector(".full-name-change-container .alert-success", {visible: true});
|
||
await page.waitForFunction(
|
||
() => document.querySelector<HTMLInputElement>("#full_name")?.value === "New name",
|
||
);
|
||
}
|
||
|
||
async function test_change_password(page: Page): Promise<void> {
|
||
await page.click("#change_password");
|
||
|
||
const change_password_button_selector = "#change_password_modal .dialog_submit_button";
|
||
await page.waitForSelector(change_password_button_selector, {visible: true});
|
||
|
||
await common.wait_for_micromodal_to_open(page);
|
||
await page.type("#old_password", test_credentials.default_user.password);
|
||
test_credentials.default_user.password = "new_password";
|
||
await page.type("#new_password", test_credentials.default_user.password);
|
||
|
||
// The password strength checker (zxcvbn) is imported asynchronously,
|
||
// and submitting before it loads makes the handler bail out with an
|
||
// internal error, leaving the modal open. The strength bar is hidden
|
||
// until the module finishes loading, so wait for it to become visible.
|
||
await page.waitForSelector("#pw_strength .bar", {visible: true});
|
||
await page.click(change_password_button_selector);
|
||
|
||
// On success the change password modal gets closed.
|
||
await common.wait_for_micromodal_to_close(page);
|
||
}
|
||
|
||
async function test_get_api_key(page: Page): Promise<void> {
|
||
await page.click('[data-section="account-and-privacy"]');
|
||
const show_change_api_key_selector = "#api_key_button";
|
||
await page.waitForSelector(show_change_api_key_selector, {visible: true});
|
||
await page.click(show_change_api_key_selector);
|
||
|
||
const get_api_key_button_selector = "#get_api_key_button";
|
||
await page.waitForSelector(get_api_key_button_selector, {visible: true});
|
||
await common.wait_for_micromodal_to_open(page);
|
||
await common.fill_form(page, "#api_key_form", {
|
||
password: test_credentials.default_user.password,
|
||
});
|
||
|
||
// When typing the password in Firefox, it shows "Not Secure" warning
|
||
// which was hiding the Get API key button.
|
||
// You can see the screenshot of it in https://github.com/zulip/zulip/pull/17136.
|
||
// Focusing on it will remove the warning.
|
||
await page.focus(get_api_key_button_selector);
|
||
await page.click(get_api_key_button_selector);
|
||
|
||
await page.waitForSelector("#show_api_key", {visible: true});
|
||
const api_key = await common.get_text_from_selector(page, "#api_key_value");
|
||
assert.match(api_key, /[\dA-Za-z]{32}/, "Incorrect API key format.");
|
||
|
||
const download_zuliprc_selector = "#download_zuliprc";
|
||
await page.click(download_zuliprc_selector);
|
||
const zuliprc_decoded_url = await get_decoded_url_in_selector(page, download_zuliprc_selector);
|
||
assert.match(zuliprc_decoded_url, zuliprc_regex, "Incorrect zuliprc file");
|
||
await page.click("#api_key_modal .modal__close");
|
||
await common.wait_for_micromodal_to_close(page);
|
||
}
|
||
|
||
async function test_webhook_bot_creation(page: Page): Promise<void> {
|
||
await page.waitForSelector("#personal-bot-list .add-a-new-bot", {visible: true});
|
||
await page.click("#personal-bot-list .add-a-new-bot");
|
||
await common.wait_for_micromodal_to_open(page);
|
||
assert.strictEqual(
|
||
await common.get_text_from_selector(page, ".dialog_heading"),
|
||
"Add a new bot",
|
||
"Unexpected title for deactivate user modal",
|
||
);
|
||
assert.strictEqual(
|
||
await common.get_text_from_selector(page, ".micromodal .dialog_submit_button"),
|
||
"Add",
|
||
"Deactivate button has incorrect text.",
|
||
);
|
||
await common.fill_form(page, "#create_bot_form", {
|
||
bot_name: "Bot 1",
|
||
bot_short_name: "1",
|
||
bot_type: OUTGOING_WEBHOOK_BOT_TYPE,
|
||
payload_url: "http://hostname.example.com/bots/followup",
|
||
});
|
||
await page.click(".micromodal .dialog_submit_button");
|
||
await common.wait_for_micromodal_to_close(page);
|
||
|
||
// Wait for the bot to appear in the local data store, since the
|
||
// bot_add event may not have been processed yet when the modal closes.
|
||
await page.waitForFunction(
|
||
(bot_name: string) => zulip_test.get_user_id_from_name(bot_name) !== undefined,
|
||
{},
|
||
"Bot 1",
|
||
);
|
||
const user_id = await common.get_user_id_from_name(page, "Bot 1");
|
||
await open_manage_bot_tab(page, user_id!);
|
||
|
||
const outgoing_webhook_zuliprc_regex =
|
||
/^data:application\/octet-stream;charset=utf-8,\[api]\nemail=.+\nkey=.+\nsite=.+\ntoken=.+\n$/;
|
||
|
||
const zuliprc_url_selector = `.micromodal .hidden-zuliprc-download`;
|
||
const download_zuliprc_selector = `.download-bot-zuliprc`;
|
||
|
||
await page.waitForSelector(download_zuliprc_selector, {visible: true});
|
||
await page.click(download_zuliprc_selector);
|
||
|
||
// Wait for API request to be complete by checking when the loading indicator
|
||
// is shown and hidden.
|
||
const download_zuliprc_loader_selector = ".download-bot-zuliprc .button-loading-indicator";
|
||
await page.waitForSelector(download_zuliprc_loader_selector, {visible: true});
|
||
await page.waitForSelector(download_zuliprc_loader_selector, {hidden: true});
|
||
|
||
const zuliprc_decoded_url = await get_decoded_url_in_selector(page, zuliprc_url_selector);
|
||
assert.match(
|
||
zuliprc_decoded_url,
|
||
outgoing_webhook_zuliprc_regex,
|
||
"Incorrect outgoing webhook bot zuliprc format",
|
||
);
|
||
await page.click(".micromodal .modal__close");
|
||
await common.wait_for_micromodal_to_close(page);
|
||
}
|
||
|
||
async function test_normal_bot_creation(page: Page): Promise<void> {
|
||
await page.waitForSelector("#personal-bot-list .add-a-new-bot", {visible: true});
|
||
await page.click("#personal-bot-list .add-a-new-bot");
|
||
await common.wait_for_micromodal_to_open(page);
|
||
assert.strictEqual(
|
||
await common.get_text_from_selector(page, ".dialog_heading"),
|
||
"Add a new bot",
|
||
"Unexpected title for deactivate user modal",
|
||
);
|
||
assert.strictEqual(
|
||
await common.get_text_from_selector(page, ".micromodal .dialog_submit_button span"),
|
||
"Add",
|
||
"Deactivate button has incorrect text.",
|
||
);
|
||
await common.fill_form(page, "#create_bot_form", {
|
||
bot_name: "Bot 2",
|
||
bot_short_name: "2",
|
||
bot_type: GENERIC_BOT_TYPE,
|
||
});
|
||
await page.click(".micromodal .dialog_submit_button");
|
||
await common.wait_for_micromodal_to_close(page);
|
||
|
||
// Wait for the bot to appear in the local data store, since the
|
||
// bot_add event may not have been processed yet when the modal closes.
|
||
await page.waitForFunction(
|
||
(bot_name: string) => zulip_test.get_user_id_from_name(bot_name) !== undefined,
|
||
{},
|
||
"Bot 2",
|
||
);
|
||
const user_id = await common.get_user_id_from_name(page, "Bot 2");
|
||
await open_manage_bot_tab(page, user_id!);
|
||
|
||
const zuliprc_url_selector = `.micromodal .hidden-zuliprc-download`;
|
||
const download_zuliprc_selector = `.download-bot-zuliprc`;
|
||
|
||
await page.waitForSelector(download_zuliprc_selector, {visible: true});
|
||
await page.click(download_zuliprc_selector);
|
||
|
||
// Wait for API request to be complete by checking when the loading indicator
|
||
// is shown and hidden.
|
||
const download_zuliprc_loader_selector = ".download-bot-zuliprc .button-loading-indicator";
|
||
await page.waitForSelector(download_zuliprc_loader_selector, {visible: true});
|
||
await page.waitForSelector(download_zuliprc_loader_selector, {hidden: true});
|
||
|
||
const zuliprc_decoded_url = await get_decoded_url_in_selector(page, zuliprc_url_selector);
|
||
assert.match(zuliprc_decoded_url, zuliprc_regex, "Incorrect zuliprc format for bot.");
|
||
await page.click(".micromodal .modal__close");
|
||
await common.wait_for_micromodal_to_close(page);
|
||
}
|
||
|
||
async function test_botserverrc(page: Page): Promise<void> {
|
||
await page.waitForSelector("#personal-bot-list .download-botserverrc-file", {visible: true});
|
||
await page.click("#personal-bot-list .download-botserverrc-file");
|
||
await page.waitForSelector(
|
||
'#personal-bot-list .hidden-botserverrc-download[href^="data:application"]',
|
||
);
|
||
const botserverrc_decoded_url = await get_decoded_url_in_selector(
|
||
page,
|
||
"#personal-bot-list .hidden-botserverrc-download",
|
||
);
|
||
const botserverrc_regex =
|
||
/^data:application\/octet-stream;charset=utf-8,\[]\nemail=.+\nkey=.+\nsite=.+\ntoken=.+\n$/;
|
||
assert.match(botserverrc_decoded_url, botserverrc_regex, "Incorrect botserverrc format.");
|
||
}
|
||
|
||
// Disabled the below test due to non-deterministic failures.
|
||
// The test often fails to close the modal, as does the
|
||
// test_invalid_edit_bot_form above.
|
||
// TODO: Debug this and re-enable with a fix.
|
||
async function test_edit_bot_form(page: Page): Promise<void> {
|
||
return;
|
||
const bot1_email = "1-bot@zulip.testserver";
|
||
const bot1_edit_button = `.open_edit_bot_form[data-email="${CSS.escape(bot1_email)}"]`;
|
||
await page.click(bot1_edit_button);
|
||
|
||
const edit_form_selector = `#bot-edit-form[data-email="${CSS.escape(bot1_email)}"]`;
|
||
await page.waitForSelector(edit_form_selector, {visible: true});
|
||
assert.equal(
|
||
await page.$eval(`${edit_form_selector} input[name=full_name]`, (el) => el.value),
|
||
"Bot 1",
|
||
);
|
||
|
||
await common.fill_form(page, edit_form_selector, {full_name: "Bot one"});
|
||
const save_button_selector = "#user-profile-modal .dialog_submit_button";
|
||
await page.click(save_button_selector);
|
||
|
||
// The form gets closed on saving. So, assert it's closed by waiting for it to be hidden.
|
||
await page.waitForSelector("#edit_bot_modal", {hidden: true});
|
||
|
||
await page.waitForSelector(
|
||
`xpath///*[${common.has_class_x(
|
||
"open_edit_bot_form",
|
||
)} and @data-email="${bot1_email}"]/ancestor::*[${common.has_class_x(
|
||
"details",
|
||
)}]/*[${common.has_class_x("name")} and text()="Bot one"]`,
|
||
);
|
||
|
||
await common.wait_for_micromodal_to_close(page);
|
||
}
|
||
|
||
// Disabled the below test due to non-deterministic failures.
|
||
// The test often fails to close the modal.
|
||
// TODO: Debug this and re-enable with a fix.
|
||
async function test_invalid_edit_bot_form(page: Page): Promise<void> {
|
||
return;
|
||
const bot1_email = "1-bot@zulip.testserver";
|
||
const bot1_edit_button = `.open_edit_bot_form[data-email="${CSS.escape(bot1_email)}"]`;
|
||
await page.click(bot1_edit_button);
|
||
|
||
const edit_form_selector = `#bot-edit-form[data-email="${CSS.escape(bot1_email)}"]`;
|
||
await page.waitForSelector(edit_form_selector, {visible: true});
|
||
assert.equal(
|
||
await page.$eval(`${edit_form_selector} input[name=full_name]`, (el) => el.value),
|
||
"Bot one",
|
||
);
|
||
|
||
await common.fill_form(page, edit_form_selector, {full_name: "Bot 2"});
|
||
const save_button_selector = "#user-profile-modal .dialog_submit_button";
|
||
await page.click(save_button_selector);
|
||
|
||
// The form should not get closed on saving. Errors should be visible on the form.
|
||
await common.wait_for_micromodal_to_open(page);
|
||
await page.waitForSelector("#dialog_error", {visible: true});
|
||
assert.strictEqual(
|
||
await common.get_text_from_selector(page, "#dialog_error"),
|
||
"Failed: Name is already in use.",
|
||
);
|
||
|
||
const cancel_button_selector = "#user-profile-modal .dialog_exit_button";
|
||
await page.waitForFunction(
|
||
(cancel_button_selector: string) =>
|
||
!document.querySelector(cancel_button_selector)?.hasAttribute("disabled"),
|
||
{},
|
||
cancel_button_selector,
|
||
);
|
||
await page.click(cancel_button_selector);
|
||
await page.waitForSelector(
|
||
`xpath///*[${common.has_class_x(
|
||
"open_edit_bot_form",
|
||
)} and @data-email="${bot1_email}"]/ancestor::*[${common.has_class_x(
|
||
"details",
|
||
)}]/*[${common.has_class_x("name")} and text()="Bot one"]`,
|
||
);
|
||
|
||
await common.wait_for_micromodal_to_close(page);
|
||
}
|
||
|
||
async function test_your_bots_section(page: Page): Promise<void> {
|
||
await page.click('.normal-settings-list [data-section="bots"]');
|
||
await test_webhook_bot_creation(page);
|
||
await test_normal_bot_creation(page);
|
||
await test_botserverrc(page);
|
||
await test_edit_bot_form(page);
|
||
await test_invalid_edit_bot_form(page);
|
||
}
|
||
|
||
const alert_word_status_banner_selector = ".alert-word-status-banner";
|
||
|
||
async function add_alert_word(page: Page, word: string): Promise<void> {
|
||
await page.click("#open-add-alert-word-modal");
|
||
await common.wait_for_micromodal_to_open(page);
|
||
|
||
await page.type("#add-alert-word-name", word);
|
||
await page.click("#add-alert-word .dialog_submit_button");
|
||
|
||
await common.wait_for_micromodal_to_close(page);
|
||
}
|
||
|
||
async function check_alert_word_added(page: Page, word: string): Promise<void> {
|
||
const added_alert_word_selector = `.alert-word-item[data-word='${CSS.escape(word)}']`;
|
||
await page.waitForSelector(added_alert_word_selector, {visible: true});
|
||
}
|
||
|
||
async function get_alert_words_status_text(page: Page): Promise<string> {
|
||
await page.waitForSelector(alert_word_status_banner_selector, {visible: true});
|
||
const status_text = await common.get_text_from_selector(
|
||
page,
|
||
".alert-word-status-banner .banner-label",
|
||
);
|
||
return status_text;
|
||
}
|
||
|
||
async function close_alert_words_status(page: Page): Promise<void> {
|
||
const status_close_button = ".alert-word-status-banner .banner-close-button";
|
||
await page.click(status_close_button);
|
||
await page.waitForSelector(alert_word_status_banner_selector, {hidden: true});
|
||
}
|
||
|
||
async function test_duplicate_alert_words_cannot_be_added(
|
||
page: Page,
|
||
duplicate_word: string,
|
||
): Promise<void> {
|
||
await page.click("#open-add-alert-word-modal");
|
||
await common.wait_for_micromodal_to_open(page);
|
||
|
||
await page.type("#add-alert-word-name", duplicate_word);
|
||
await page.click("#add-alert-word .dialog_submit_button");
|
||
|
||
const alert_word_status_selector = "#dialog_error";
|
||
await page.waitForSelector(alert_word_status_selector, {visible: true});
|
||
const status_text = await common.get_text_from_selector(page, alert_word_status_selector);
|
||
assert.strictEqual(status_text, "Alert word already exists!");
|
||
|
||
await page.click("#add-alert-word .dialog_exit_button");
|
||
await common.wait_for_micromodal_to_close(page);
|
||
}
|
||
|
||
async function delete_alert_word(page: Page, word: string): Promise<void> {
|
||
const delete_button_selector = `tr[data-word="${CSS.escape(word)}"] .remove-alert-word`;
|
||
await page.click(delete_button_selector);
|
||
await page.waitForSelector(delete_button_selector, {hidden: true});
|
||
}
|
||
|
||
async function test_alert_word_deletion(page: Page, word: string): Promise<void> {
|
||
await delete_alert_word(page, word);
|
||
const status_text = await get_alert_words_status_text(page);
|
||
assert.strictEqual(status_text, `Alert word ${word} removed successfully!`);
|
||
await close_alert_words_status(page);
|
||
}
|
||
|
||
async function test_alert_words_section(page: Page): Promise<void> {
|
||
await page.click('[data-section="alert-words"]');
|
||
const word = "puppeteer";
|
||
await add_alert_word(page, word);
|
||
await check_alert_word_added(page, word);
|
||
await test_duplicate_alert_words_cannot_be_added(page, word);
|
||
await test_alert_word_deletion(page, word);
|
||
}
|
||
|
||
async function change_language(page: Page, language_data_code: string): Promise<void> {
|
||
await page.waitForSelector("#default_language_widget", {
|
||
visible: true,
|
||
});
|
||
await page.click("#default_language_widget");
|
||
await page.waitForSelector(".dropdown-list", {visible: true});
|
||
const language_selector = `li[data-unique-id="${CSS.escape(language_data_code)}"]`;
|
||
await page.click(language_selector);
|
||
}
|
||
|
||
async function check_language_setting_status(page: Page): Promise<void> {
|
||
await page.waitForSelector("#user-preferences .general-settings-status .reload_link", {
|
||
visible: true,
|
||
});
|
||
}
|
||
|
||
async function assert_language_changed_to_chinese(page: Page): Promise<void> {
|
||
await page.waitForSelector("#default_language_widget", {
|
||
visible: true,
|
||
});
|
||
const default_language = await common.get_text_from_selector(page, ".dropdown_widget_value");
|
||
assert.match(
|
||
default_language,
|
||
/^中文(简体)/v,
|
||
"Default language has not been changed to Chinese.",
|
||
);
|
||
}
|
||
|
||
async function test_i18n_language_precedence(page: Page): Promise<void> {
|
||
const settings_url_for_german = "http://zulip.zulipdev.com:9981/de/#settings";
|
||
await page.goto(settings_url_for_german);
|
||
await page.waitForSelector("#settings-change-box", {visible: true});
|
||
const page_language_code = await page.evaluate(() => document.documentElement.lang);
|
||
assert.strictEqual(page_language_code, "de");
|
||
}
|
||
|
||
async function test_default_language_setting(page: Page): Promise<void> {
|
||
// Since the "Bots" section of Personal Settings redirects us to Organization Settings > Bots with the "Your Bots" tab preselected,
|
||
// we need to switch back to the Personal Settings tab to proceed with further testing.
|
||
await page.waitForSelector('.tab-switcher .ind-tab[data-tab-key="settings"]', {visible: true});
|
||
await page.click('.tab-switcher .ind-tab[data-tab-key="settings"]');
|
||
await page.waitForSelector("#settings_overlay_container", {visible: true});
|
||
|
||
const preferences_section = '[data-section="preferences"]';
|
||
await page.click(preferences_section);
|
||
|
||
const chinese_language_data_code = "zh-hans";
|
||
await change_language(page, chinese_language_data_code);
|
||
// Check that the saved indicator appears
|
||
await check_language_setting_status(page);
|
||
await page.click(".reload_link");
|
||
await page.waitForSelector("#default_language_widget", {
|
||
visible: true,
|
||
});
|
||
await assert_language_changed_to_chinese(page);
|
||
await test_i18n_language_precedence(page);
|
||
await page.waitForSelector(preferences_section, {visible: true});
|
||
await page.click(preferences_section);
|
||
|
||
// Change the language back to English so that subsequent tests pass.
|
||
await change_language(page, "en");
|
||
|
||
// Check that the saved indicator appears
|
||
await check_language_setting_status(page);
|
||
await page.goto("http://zulip.zulipdev.com:9981/#settings"); // get back to normal language.
|
||
await page.waitForSelector(preferences_section, {visible: true});
|
||
await page.click(preferences_section);
|
||
await page.waitForSelector("#user-preferences .general-settings-status", {
|
||
visible: true,
|
||
});
|
||
await page.waitForSelector("#default_language_widget", {
|
||
visible: true,
|
||
});
|
||
}
|
||
|
||
async function test_notifications_section(page: Page): Promise<void> {
|
||
await page.click('[data-section="notifications"]');
|
||
// At the beginning, "DMs, mentions, and alerts"(checkbox name=enable_sounds) audio will be on
|
||
// and "Streams"(checkbox name=enable_stream_audible_notifications) audio will be off by default.
|
||
|
||
const notification_sound_enabled =
|
||
"#user-notification-settings .setting_notification_sound:enabled";
|
||
await page.waitForSelector(notification_sound_enabled, {visible: true});
|
||
|
||
await common.fill_form(page, "#user-notification-settings .notification-settings-form", {
|
||
enable_stream_audible_notifications: true,
|
||
enable_sounds: false,
|
||
});
|
||
await page.waitForSelector(notification_sound_enabled, {visible: true});
|
||
|
||
await common.fill_form(page, "#user-notification-settings .notification-settings-form", {
|
||
enable_stream_audible_notifications: true,
|
||
});
|
||
/*
|
||
Usually notifications sound dropdown gets disabled on disabling
|
||
all audio notifications. But this seems flaky in tests.
|
||
TODO: Find the right fix and enable this.
|
||
|
||
const notification_sound_disabled = ".setting_notification_sound:disabled";
|
||
await page.waitForSelector(notification_sound_disabled);
|
||
*/
|
||
}
|
||
|
||
async function settings_tests(page: Page): Promise<void> {
|
||
await common.log_in(page);
|
||
await open_settings(page);
|
||
await close_settings_and_date_picker(page);
|
||
await open_settings(page);
|
||
await test_change_full_name(page);
|
||
await test_alert_words_section(page);
|
||
await test_your_bots_section(page);
|
||
await test_default_language_setting(page);
|
||
await test_notifications_section(page);
|
||
await test_get_api_key(page);
|
||
await test_change_password(page);
|
||
// test_change_password should be the very last test, because it
|
||
// replaces your session, which can lead to some nondeterministic
|
||
// failures in test code after it, involving `GET /events`
|
||
// returning a 401. (We reset the test database after each file).
|
||
}
|
||
|
||
await common.run_test(settings_tests);
|