|
|
|
|
@@ -16,14 +16,13 @@ const puppeteer_dir = path.join(root_dir, "var/puppeteer");
|
|
|
|
|
|
|
|
|
|
type Message = Record<string, string | boolean> & {recipient?: string; content: string};
|
|
|
|
|
|
|
|
|
|
class CommonUtils {
|
|
|
|
|
browser: Browser | null = null;
|
|
|
|
|
screenshot_id = 0;
|
|
|
|
|
is_firefox = process.env.PUPPETEER_PRODUCT === "firefox";
|
|
|
|
|
realm_url = "http://zulip.zulipdev.com:9981/";
|
|
|
|
|
gps = new StackTraceGPS({ajax: async (url) => (await fetch(url)).text()});
|
|
|
|
|
let browser: Browser | null = null;
|
|
|
|
|
let screenshot_id = 0;
|
|
|
|
|
export const is_firefox = process.env.PUPPETEER_PRODUCT === "firefox";
|
|
|
|
|
let realm_url = "http://zulip.zulipdev.com:9981/";
|
|
|
|
|
const gps = new StackTraceGPS({ajax: async (url) => (await fetch(url)).text()});
|
|
|
|
|
|
|
|
|
|
pm_recipient = {
|
|
|
|
|
export const pm_recipient = {
|
|
|
|
|
async set(page: Page, recipient: string): Promise<void> {
|
|
|
|
|
// Without using the delay option here there seems to be
|
|
|
|
|
// a flake where the typeahead doesn't show up.
|
|
|
|
|
@@ -34,36 +33,32 @@ class CommonUtils {
|
|
|
|
|
// meant for something else; e.g., the private message
|
|
|
|
|
// input typeahead is different from the topic input
|
|
|
|
|
// typeahead but both can be present in the DOM.
|
|
|
|
|
const entry = await page.waitForSelector(
|
|
|
|
|
'.typeahead[style*="display: block"] .active a',
|
|
|
|
|
{visible: true},
|
|
|
|
|
);
|
|
|
|
|
const entry = await page.waitForSelector('.typeahead[style*="display: block"] .active a', {
|
|
|
|
|
visible: true,
|
|
|
|
|
});
|
|
|
|
|
await entry!.click();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async expect(page: Page, expected: string): Promise<void> {
|
|
|
|
|
const actual_recipients = await page.evaluate(() =>
|
|
|
|
|
zulip_test.private_message_recipient(),
|
|
|
|
|
);
|
|
|
|
|
const actual_recipients = await page.evaluate(() => zulip_test.private_message_recipient());
|
|
|
|
|
assert.equal(actual_recipients, expected);
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
fullname: Record<string, string> = {
|
|
|
|
|
export const fullname: Record<string, string> = {
|
|
|
|
|
cordelia: "Cordelia, Lear's daughter",
|
|
|
|
|
othello: "Othello, the Moor of Venice",
|
|
|
|
|
hamlet: "King Hamlet",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
window_size = {
|
|
|
|
|
export const window_size = {
|
|
|
|
|
width: 1400,
|
|
|
|
|
height: 1024,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
async ensure_browser(): Promise<Browser> {
|
|
|
|
|
if (this.browser === null) {
|
|
|
|
|
const {window_size} = this;
|
|
|
|
|
this.browser = await puppeteer.launch({
|
|
|
|
|
export async function ensure_browser(): Promise<Browser> {
|
|
|
|
|
if (browser === null) {
|
|
|
|
|
browser = await puppeteer.launch({
|
|
|
|
|
args: [
|
|
|
|
|
`--window-size=${window_size.width},${window_size.height}`,
|
|
|
|
|
"--no-sandbox",
|
|
|
|
|
@@ -75,19 +70,19 @@ class CommonUtils {
|
|
|
|
|
headless: true,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return this.browser;
|
|
|
|
|
return browser;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async get_page(): Promise<Page> {
|
|
|
|
|
const browser = await this.ensure_browser();
|
|
|
|
|
export async function get_page(): Promise<Page> {
|
|
|
|
|
const browser = await ensure_browser();
|
|
|
|
|
const page = await browser.newPage();
|
|
|
|
|
return page;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async screenshot(page: Page, name: string | null = null): Promise<void> {
|
|
|
|
|
export async function screenshot(page: Page, name: string | null = null): Promise<void> {
|
|
|
|
|
if (name === null) {
|
|
|
|
|
name = `${this.screenshot_id}`;
|
|
|
|
|
this.screenshot_id += 1;
|
|
|
|
|
name = `${screenshot_id}`;
|
|
|
|
|
screenshot_id += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const screenshot_path = path.join(puppeteer_dir, `${name}.png`);
|
|
|
|
|
@@ -96,7 +91,7 @@ class CommonUtils {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async page_url_with_fragment(page: Page): Promise<string> {
|
|
|
|
|
export async function page_url_with_fragment(page: Page): Promise<string> {
|
|
|
|
|
// `page.url()` does not include the url fragment when running
|
|
|
|
|
// Puppeteer with Firefox: https://github.com/puppeteer/puppeteer/issues/6787.
|
|
|
|
|
//
|
|
|
|
|
@@ -108,7 +103,7 @@ class CommonUtils {
|
|
|
|
|
|
|
|
|
|
// This function will clear the existing value of the element and
|
|
|
|
|
// replace it with the text.
|
|
|
|
|
async clear_and_type(page: Page, selector: string, text: string): Promise<void> {
|
|
|
|
|
export async function clear_and_type(page: Page, selector: string, text: string): Promise<void> {
|
|
|
|
|
// Select all text currently in the element.
|
|
|
|
|
await page.click(selector, {clickCount: 3});
|
|
|
|
|
await page.type(selector, text);
|
|
|
|
|
@@ -131,7 +126,7 @@ class CommonUtils {
|
|
|
|
|
* terms: true
|
|
|
|
|
* });
|
|
|
|
|
*/
|
|
|
|
|
async fill_form(
|
|
|
|
|
export async function fill_form(
|
|
|
|
|
page: Page,
|
|
|
|
|
form_selector: string,
|
|
|
|
|
params: Record<string, boolean | string>,
|
|
|
|
|
@@ -167,7 +162,7 @@ class CommonUtils {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async check_form_contents(
|
|
|
|
|
export async function check_form_contents(
|
|
|
|
|
page: Page,
|
|
|
|
|
form_selector: string,
|
|
|
|
|
params: Record<string, boolean | string>,
|
|
|
|
|
@@ -191,37 +186,35 @@ class CommonUtils {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async get_element_text(element: ElementHandle<Element>): Promise<string> {
|
|
|
|
|
export async function get_element_text(element: ElementHandle<Element>): Promise<string> {
|
|
|
|
|
const text = await (await element.getProperty("innerText"))!.jsonValue();
|
|
|
|
|
assert.ok(typeof text === "string");
|
|
|
|
|
return text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async get_text_from_selector(page: Page, selector: string): Promise<string> {
|
|
|
|
|
export async function get_text_from_selector(page: Page, selector: string): Promise<string> {
|
|
|
|
|
const elements = await page.$$(selector);
|
|
|
|
|
const texts = await Promise.all(
|
|
|
|
|
elements.map(async (element) => this.get_element_text(element)),
|
|
|
|
|
);
|
|
|
|
|
const texts = await Promise.all(elements.map(async (element) => get_element_text(element)));
|
|
|
|
|
return texts.join("").trim();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async get_stream_id(page: Page, stream_name: string): Promise<number> {
|
|
|
|
|
export async function get_stream_id(page: Page, stream_name: string): Promise<number> {
|
|
|
|
|
return await page.evaluate(
|
|
|
|
|
(stream_name: string) => zulip_test.get_stream_id(stream_name),
|
|
|
|
|
stream_name,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async get_user_id_from_name(page: Page, name: string): Promise<number> {
|
|
|
|
|
if (this.fullname[name] !== undefined) {
|
|
|
|
|
name = this.fullname[name];
|
|
|
|
|
export async function get_user_id_from_name(page: Page, name: string): Promise<number> {
|
|
|
|
|
if (fullname[name] !== undefined) {
|
|
|
|
|
name = fullname[name];
|
|
|
|
|
}
|
|
|
|
|
return await page.evaluate((name: string) => zulip_test.get_user_id_from_name(name), name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async get_internal_email_from_name(page: Page, name: string): Promise<string> {
|
|
|
|
|
if (this.fullname[name] !== undefined) {
|
|
|
|
|
name = this.fullname[name];
|
|
|
|
|
export async function get_internal_email_from_name(page: Page, name: string): Promise<string> {
|
|
|
|
|
if (fullname[name] !== undefined) {
|
|
|
|
|
name = fullname[name];
|
|
|
|
|
}
|
|
|
|
|
return await page.evaluate((fullname: string) => {
|
|
|
|
|
const user_id = zulip_test.get_user_id_from_name(fullname);
|
|
|
|
|
@@ -229,13 +222,13 @@ class CommonUtils {
|
|
|
|
|
}, name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async log_in(
|
|
|
|
|
export async function log_in(
|
|
|
|
|
page: Page,
|
|
|
|
|
credentials: {username: string; password: string} | null = null,
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
console.log("Logging in");
|
|
|
|
|
await page.goto(this.realm_url + "login/");
|
|
|
|
|
assert.equal(this.realm_url + "login/", page.url());
|
|
|
|
|
await page.goto(realm_url + "login/");
|
|
|
|
|
assert.equal(realm_url + "login/", page.url());
|
|
|
|
|
if (credentials === null) {
|
|
|
|
|
credentials = test_credentials.default_user;
|
|
|
|
|
}
|
|
|
|
|
@@ -244,14 +237,14 @@ class CommonUtils {
|
|
|
|
|
username: credentials.username,
|
|
|
|
|
password: credentials.password,
|
|
|
|
|
};
|
|
|
|
|
await this.fill_form(page, "#login_form", params);
|
|
|
|
|
await fill_form(page, "#login_form", params);
|
|
|
|
|
await page.$eval("#login_form", (form) => (form as HTMLFormElement).submit());
|
|
|
|
|
|
|
|
|
|
await page.waitForSelector("#recent_topics_filter_buttons", {visible: true});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async log_out(page: Page): Promise<void> {
|
|
|
|
|
await page.goto(this.realm_url);
|
|
|
|
|
export async function log_out(page: Page): Promise<void> {
|
|
|
|
|
await page.goto(realm_url);
|
|
|
|
|
const menu_selector = "#settings-dropdown";
|
|
|
|
|
const logout_selector = '.dropdown-menu a[href="#logout"]';
|
|
|
|
|
console.log("Logging out");
|
|
|
|
|
@@ -266,7 +259,11 @@ class CommonUtils {
|
|
|
|
|
assert.ok(page.url().includes("/login/"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async ensure_enter_does_not_send(page: Page): Promise<void> {
|
|
|
|
|
export function set_realm_url(new_realm_url: string): void {
|
|
|
|
|
realm_url = new_realm_url;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function ensure_enter_does_not_send(page: Page): Promise<void> {
|
|
|
|
|
let enter_sends = false;
|
|
|
|
|
await page.$eval(".enter_sends_false", (el) => {
|
|
|
|
|
if ((el as HTMLElement).style.display !== "none") {
|
|
|
|
|
@@ -281,7 +278,10 @@ class CommonUtils {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async assert_compose_box_content(page: Page, expected_value: string): Promise<void> {
|
|
|
|
|
export async function assert_compose_box_content(
|
|
|
|
|
page: Page,
|
|
|
|
|
expected_value: string,
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
const compose_box_element = await page.waitForSelector("#compose-textarea");
|
|
|
|
|
const compose_box_content = await page.evaluate((element) => {
|
|
|
|
|
if (!(element instanceof HTMLTextAreaElement)) {
|
|
|
|
|
@@ -296,7 +296,7 @@ class CommonUtils {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async wait_for_fully_processed_message(page: Page, content: string): Promise<void> {
|
|
|
|
|
export async function wait_for_fully_processed_message(page: Page, content: string): Promise<void> {
|
|
|
|
|
await page.waitForFunction(
|
|
|
|
|
(content: string) => {
|
|
|
|
|
/*
|
|
|
|
|
@@ -353,14 +353,18 @@ class CommonUtils {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wait for any previous send to finish, then send a message.
|
|
|
|
|
async send_message(page: Page, type: "stream" | "private", params: Message): Promise<void> {
|
|
|
|
|
export async function send_message(
|
|
|
|
|
page: Page,
|
|
|
|
|
type: "stream" | "private",
|
|
|
|
|
params: Message,
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
// If a message is outside the view, we do not need
|
|
|
|
|
// to wait for it to be processed later.
|
|
|
|
|
const outside_view = params.outside_view;
|
|
|
|
|
delete params.outside_view;
|
|
|
|
|
|
|
|
|
|
// Compose box content should be empty before sending the message.
|
|
|
|
|
await this.assert_compose_box_content(page, "");
|
|
|
|
|
await assert_compose_box_content(page, "");
|
|
|
|
|
|
|
|
|
|
if (type === "stream") {
|
|
|
|
|
await page.keyboard.press("KeyC");
|
|
|
|
|
@@ -368,7 +372,7 @@ class CommonUtils {
|
|
|
|
|
await page.keyboard.press("KeyX");
|
|
|
|
|
const recipients = params.recipient!.split(", ");
|
|
|
|
|
for (const recipient of recipients) {
|
|
|
|
|
await this.pm_recipient.set(page, recipient);
|
|
|
|
|
await pm_recipient.set(page, recipient);
|
|
|
|
|
}
|
|
|
|
|
delete params.recipient;
|
|
|
|
|
} else {
|
|
|
|
|
@@ -385,17 +389,17 @@ class CommonUtils {
|
|
|
|
|
delete params.topic;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await this.fill_form(page, 'form[action^="/json/messages"]', params);
|
|
|
|
|
await this.assert_compose_box_content(page, params.content);
|
|
|
|
|
await this.ensure_enter_does_not_send(page);
|
|
|
|
|
await fill_form(page, 'form[action^="/json/messages"]', params);
|
|
|
|
|
await assert_compose_box_content(page, params.content);
|
|
|
|
|
await ensure_enter_does_not_send(page);
|
|
|
|
|
await page.waitForSelector("#compose-send-button", {visible: true});
|
|
|
|
|
await page.click("#compose-send-button");
|
|
|
|
|
|
|
|
|
|
// Sending should clear compose box content.
|
|
|
|
|
await this.assert_compose_box_content(page, "");
|
|
|
|
|
await assert_compose_box_content(page, "");
|
|
|
|
|
|
|
|
|
|
if (!outside_view) {
|
|
|
|
|
await this.wait_for_fully_processed_message(page, params.content);
|
|
|
|
|
await wait_for_fully_processed_message(page, params.content);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close the compose box after sending the message.
|
|
|
|
|
@@ -404,9 +408,9 @@ class CommonUtils {
|
|
|
|
|
await page.waitForSelector("#compose-textarea", {hidden: true});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async send_multiple_messages(page: Page, msgs: Message[]): Promise<void> {
|
|
|
|
|
export async function send_multiple_messages(page: Page, msgs: Message[]): Promise<void> {
|
|
|
|
|
for (const msg of msgs) {
|
|
|
|
|
await this.send_message(page, msg.stream !== undefined ? "stream" : "private", msg);
|
|
|
|
|
await send_message(page, msg.stream !== undefined ? "stream" : "private", msg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -419,15 +423,18 @@ class CommonUtils {
|
|
|
|
|
*
|
|
|
|
|
* The messages are sorted chronologically.
|
|
|
|
|
*/
|
|
|
|
|
async get_rendered_messages(page: Page, table = "zhome"): Promise<[string, string[]][]> {
|
|
|
|
|
export async function get_rendered_messages(
|
|
|
|
|
page: Page,
|
|
|
|
|
table = "zhome",
|
|
|
|
|
): Promise<[string, string[]][]> {
|
|
|
|
|
const recipient_rows = await page.$$(`#${CSS.escape(table)} .recipient_row`);
|
|
|
|
|
return Promise.all(
|
|
|
|
|
recipient_rows.map(async (element): Promise<[string, string[]]> => {
|
|
|
|
|
const stream_label = await element.$(".stream_label");
|
|
|
|
|
const stream_name = (await this.get_element_text(stream_label!)).trim();
|
|
|
|
|
const stream_name = (await get_element_text(stream_label!)).trim();
|
|
|
|
|
const topic_label = await element.$(".stream_topic a");
|
|
|
|
|
const topic_name =
|
|
|
|
|
topic_label === null ? "" : (await this.get_element_text(topic_label)).trim();
|
|
|
|
|
topic_label === null ? "" : (await get_element_text(topic_label)).trim();
|
|
|
|
|
let key = stream_name;
|
|
|
|
|
if (topic_name !== "") {
|
|
|
|
|
// If topic_name is '' then this is PMs, so only
|
|
|
|
|
@@ -438,7 +445,7 @@ class CommonUtils {
|
|
|
|
|
const messages = await Promise.all(
|
|
|
|
|
(
|
|
|
|
|
await element.$$(".message_row .message_content")
|
|
|
|
|
).map(async (message_row) => (await this.get_element_text(message_row)).trim()),
|
|
|
|
|
).map(async (message_row) => (await get_element_text(message_row)).trim()),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return [key, messages];
|
|
|
|
|
@@ -451,13 +458,13 @@ class CommonUtils {
|
|
|
|
|
// message is { "stream > topic": [messages] }.
|
|
|
|
|
// The method will only check that all the messages in the
|
|
|
|
|
// messages array passed exist in the order they are passed.
|
|
|
|
|
async check_messages_sent(
|
|
|
|
|
export async function check_messages_sent(
|
|
|
|
|
page: Page,
|
|
|
|
|
table: string,
|
|
|
|
|
messages: [string, string[]][],
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await page.waitForSelector(`#${CSS.escape(table)}`, {visible: true});
|
|
|
|
|
const rendered_messages = await this.get_rendered_messages(page, table);
|
|
|
|
|
const rendered_messages = await get_rendered_messages(page, table);
|
|
|
|
|
|
|
|
|
|
// We only check the last n messages because if we run
|
|
|
|
|
// the test with --interactive there will be duplicates.
|
|
|
|
|
@@ -465,17 +472,17 @@ class CommonUtils {
|
|
|
|
|
assert.deepStrictEqual(last_n_messages, messages);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async open_streams_modal(page: Page): Promise<void> {
|
|
|
|
|
export async function open_streams_modal(page: Page): Promise<void> {
|
|
|
|
|
const all_streams_selector = "#subscribe-to-more-streams";
|
|
|
|
|
await page.waitForSelector(all_streams_selector, {visible: true});
|
|
|
|
|
await page.click(all_streams_selector);
|
|
|
|
|
|
|
|
|
|
await page.waitForSelector("#subscription_overlay.new-style", {visible: true});
|
|
|
|
|
const url = await this.page_url_with_fragment(page);
|
|
|
|
|
const url = await page_url_with_fragment(page);
|
|
|
|
|
assert.ok(url.includes("#streams/all"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async manage_organization(page: Page): Promise<void> {
|
|
|
|
|
export async function manage_organization(page: Page): Promise<void> {
|
|
|
|
|
const menu_selector = "#settings-dropdown";
|
|
|
|
|
await page.waitForSelector(menu_selector, {visible: true});
|
|
|
|
|
await page.click(menu_selector);
|
|
|
|
|
@@ -484,21 +491,21 @@ class CommonUtils {
|
|
|
|
|
await page.click(organization_settings);
|
|
|
|
|
await page.waitForSelector("#settings_overlay_container.show", {visible: true});
|
|
|
|
|
|
|
|
|
|
const url = await this.page_url_with_fragment(page);
|
|
|
|
|
const url = await page_url_with_fragment(page);
|
|
|
|
|
assert.match(url, /^http:\/\/[^/]+\/#organization/, "Unexpected manage organization URL");
|
|
|
|
|
|
|
|
|
|
const organization_settings_data_section = "li[data-section='organization-settings']";
|
|
|
|
|
await page.click(organization_settings_data_section);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async select_item_via_typeahead(
|
|
|
|
|
export async function select_item_via_typeahead(
|
|
|
|
|
page: Page,
|
|
|
|
|
field_selector: string,
|
|
|
|
|
str: string,
|
|
|
|
|
item: string,
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
console.log(`Looking in ${field_selector} to select ${str}, ${item}`);
|
|
|
|
|
await this.clear_and_type(page, field_selector, str);
|
|
|
|
|
await clear_and_type(page, field_selector, str);
|
|
|
|
|
const entry = await page.waitForSelector(
|
|
|
|
|
`xpath///*[@class="typeahead dropdown-menu" and contains(@style, "display: block")]//li[contains(normalize-space(), "${item}")]//a`,
|
|
|
|
|
{visible: true},
|
|
|
|
|
@@ -513,28 +520,28 @@ class CommonUtils {
|
|
|
|
|
}, entry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async wait_for_modal_to_close(page: Page): Promise<void> {
|
|
|
|
|
export async function wait_for_modal_to_close(page: Page): Promise<void> {
|
|
|
|
|
// This function will ensure that the mouse events are enabled for the background for further tests.
|
|
|
|
|
await page.waitForFunction(
|
|
|
|
|
() => document.querySelector(".overlay.show")?.getAttribute("style") === null,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async wait_for_micromodal_to_open(page: Page): Promise<void> {
|
|
|
|
|
export async function wait_for_micromodal_to_open(page: Page): Promise<void> {
|
|
|
|
|
// We manually add the `modal--open` class to the modal after the modal animation completes.
|
|
|
|
|
await page.waitForFunction(() => document.querySelector(".modal--open") !== null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async wait_for_micromodal_to_close(page: Page): Promise<void> {
|
|
|
|
|
export async function wait_for_micromodal_to_close(page: Page): Promise<void> {
|
|
|
|
|
// This function will ensure that the mouse events are enabled for the background for further tests.
|
|
|
|
|
await page.waitForFunction(() => document.querySelector(".modal--open") === null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async run_test_async(test_function: (page: Page) => Promise<void>): Promise<void> {
|
|
|
|
|
export async function run_test_async(test_function: (page: Page) => Promise<void>): Promise<void> {
|
|
|
|
|
// Pass a page instance to test so we can take
|
|
|
|
|
// a screenshot of it when the test fails.
|
|
|
|
|
const browser = await this.ensure_browser();
|
|
|
|
|
const page = await this.get_page();
|
|
|
|
|
const browser = await ensure_browser();
|
|
|
|
|
const page = await get_page();
|
|
|
|
|
|
|
|
|
|
// Used to keep console messages in order after async source mapping
|
|
|
|
|
let console_ready = Promise.resolve();
|
|
|
|
|
@@ -551,7 +558,7 @@ class CommonUtils {
|
|
|
|
|
columnNumber: columnNumber === undefined ? undefined : columnNumber + 1,
|
|
|
|
|
});
|
|
|
|
|
try {
|
|
|
|
|
frame = await this.gps.getMappedLocation(frame);
|
|
|
|
|
frame = await gps.getMappedLocation(frame);
|
|
|
|
|
} catch {
|
|
|
|
|
// Ignore source mapping errors
|
|
|
|
|
}
|
|
|
|
|
@@ -590,7 +597,7 @@ class CommonUtils {
|
|
|
|
|
ErrorStackParser.parse(error1).map(async (frame1) => {
|
|
|
|
|
let frame = frame1 as unknown as StackFrame;
|
|
|
|
|
try {
|
|
|
|
|
frame = await this.gps.getMappedLocation(frame);
|
|
|
|
|
frame = await gps.getMappedLocation(frame);
|
|
|
|
|
} catch {
|
|
|
|
|
// Ignore source mapping errors
|
|
|
|
|
}
|
|
|
|
|
@@ -607,8 +614,8 @@ class CommonUtils {
|
|
|
|
|
console_ready = (async () => {
|
|
|
|
|
try {
|
|
|
|
|
// Take a screenshot, and increment the screenshot_id.
|
|
|
|
|
await this.screenshot(page, `failure-${this.screenshot_id}`);
|
|
|
|
|
this.screenshot_id += 1;
|
|
|
|
|
await screenshot(page, `failure-${screenshot_id}`);
|
|
|
|
|
screenshot_id += 1;
|
|
|
|
|
} finally {
|
|
|
|
|
await console_ready2;
|
|
|
|
|
console.log("Closing page to stop the test...");
|
|
|
|
|
@@ -619,7 +626,7 @@ class CommonUtils {
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await test_function(page);
|
|
|
|
|
await this.log_out(page);
|
|
|
|
|
await log_out(page);
|
|
|
|
|
|
|
|
|
|
if (page_errored) {
|
|
|
|
|
throw new Error("Page threw an error");
|
|
|
|
|
@@ -627,8 +634,8 @@ class CommonUtils {
|
|
|
|
|
} catch (error: unknown) {
|
|
|
|
|
if (!page_errored) {
|
|
|
|
|
// Take a screenshot, and increment the screenshot_id.
|
|
|
|
|
await this.screenshot(page, `failure-${this.screenshot_id}`);
|
|
|
|
|
this.screenshot_id += 1;
|
|
|
|
|
await screenshot(page, `failure-${screenshot_id}`);
|
|
|
|
|
screenshot_id += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw error;
|
|
|
|
|
@@ -638,12 +645,9 @@ class CommonUtils {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
run_test(test_function: (page: Page) => Promise<void>): void {
|
|
|
|
|
this.run_test_async(test_function).catch((error) => {
|
|
|
|
|
export function run_test(test_function: (page: Page) => Promise<void>): void {
|
|
|
|
|
run_test_async(test_function).catch((error) => {
|
|
|
|
|
console.error(error);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default new CommonUtils();
|
|
|
|
|
|