bigbluebutton-Github/bigbluebutton-tests/playwright/core/page.js

359 lines
13 KiB
JavaScript
Raw Normal View History

require('dotenv').config();
2022-03-29 21:53:07 +08:00
const { expect, default: test } = require('@playwright/test');
2022-03-21 23:04:43 +08:00
const { readFileSync } = require('fs');
const { format } = require('node:util');
// This is version 4 of chalk, not version 5, which uses ESM
const chalk = require('chalk');
const parameters = require('./parameters');
const helpers = require('./helpers');
2021-11-18 04:07:14 +08:00
const e = require('./elements');
const { env } = require('node:process');
2021-11-27 04:01:41 +08:00
const { ELEMENT_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME, VIDEO_LOADING_WAIT_TIME } = require('./constants');
2022-02-05 03:26:35 +08:00
const { checkElement, checkElementLengthEqualTo } = require('./util');
2022-06-08 02:52:22 +08:00
const { generateSettingsData, getSettings } = require('./settings');
function formatWithCss(CONSOLE_options, ...args) {
// For Chrome, args[0] is a format string that we will process using
// node.js's util.format, but that function discards css style
// information from "%c" format specifiers. So first loop over the
// format string, replacing every "%c" with "%s" and replacing the
// corresponding css style with an ANSI color sequence.
//
// See https://console.spec.whatwg.org/ sections 2.2.1 and 2.3.4
let split_arg0 = args[0].split("%");
2023-02-14 21:59:46 +08:00
for (let i = 1, j = 1; i < split_arg0.length; i++, j++) {
if (split_arg0[i].startsWith('c')) {
split_arg0[i] = 's' + split_arg0[i].substr(1);
const styles = args[j].split(';');
args[j] = '';
for (const style of styles) {
2023-02-14 21:59:46 +08:00
const stdStyle = style.trim().toLowerCase();
if (stdStyle.startsWith('color:') && CONSOLE_options.colorize) {
const color = stdStyle.substr(6).trim();
args[j] = chalk.keyword(color)._styler.open;
} else if (stdStyle.startsWith('font-size:') && CONSOLE_options.drop_references) {
// For Chrome, we "drop references" by discarding everything after a font size change
2023-02-14 21:59:46 +08:00
split_arg0.length = i;
args.length = j;
}
}
} else if (split_arg0[i] == "") {
// format is "%%", so don't do special processing for
// split_arg0[i+1], and only increment i, not j
2023-02-14 21:59:46 +08:00
i++; // NOSONAR
}
}
args[0] = split_arg0.join('%');
return format(...args);
}
async function console_format(msg, CONSOLE_options) {
// see playwright consoleMessage class documentation
const args = await Promise.all(msg.args().map(itm => itm.jsonValue()));
let result = formatWithCss(CONSOLE_options, ...args);
if (CONSOLE_options.drop_references) {
// For Firefox, we "drop references" by discarding a URL at the end of the line
result = result.replace(/https:\/\/\S*$/, '');
}
if (CONSOLE_options.noClientLogger) {
result = result.replace(/clientLogger: /, '');
}
if (CONSOLE_options.drop_timestamps) {
// timestamp formatting is a bit complicated, with four "%s" fields and corresponding arguments,
// so just filter them out (if requested) after all the other formatting is done
result = result.replace(/\[\d\d:\d\d:\d\d:\d\d\d\d\] /, '');
}
if (CONSOLE_options.line_label) {
if (CONSOLE_options.colorize) {
result = chalk.keyword('green')(CONSOLE_options.line_label) + result;
} else {
result = CONSOLE_options.line_label + result;
}
}
return result;
}
class Page {
constructor(browser, page) {
this.browser = browser;
this.page = page;
this.initParameters = Object.assign({}, parameters);
}
2021-11-30 21:42:57 +08:00
async bringToFront() {
await this.page.bringToFront();
}
async getLastTargetPage(context) {
const contextPages = await context.pages();
return new Page(this.browser, contextPages[contextPages.length - 1]);
}
2021-11-19 04:09:29 +08:00
async init(isModerator, shouldCloseAudioModal, initOptions) {
2022-08-30 04:07:44 +08:00
const { fullName, meetingId, customParameter, customMeetingId } = initOptions || {};
2021-11-19 04:09:29 +08:00
if (!isModerator) this.initParameters.moderatorPW = '';
if (fullName) this.initParameters.fullName = fullName;
2022-01-29 03:52:22 +08:00
this.username = this.initParameters.fullName;
2021-11-19 04:09:29 +08:00
if (env.CONSOLE !== undefined) {
const CONSOLE_strings = env.CONSOLE.split(',').map(opt => opt.trim().toLowerCase());
const CONSOLE_options = {
2023-02-14 21:59:46 +08:00
colorize: CONSOLE_strings.includes('color') || CONSOLE_strings.includes('colour'),
drop_references: CONSOLE_strings.includes('norefs'),
drop_timestamps: CONSOLE_strings.includes('nots'),
line_label: CONSOLE_strings.includes('label') ? this.username + " " : undefined,
noClientLogger: CONSOLE_strings.includes('nocl') || CONSOLE_strings.includes('noclientlogger'),
};
this.page.on('console', async (msg) => console.log(await console_format(msg, CONSOLE_options)));
}
2023-02-14 21:59:46 +08:00
this.meetingId = (meetingId) ? meetingId : await helpers.createMeeting(parameters, customParameter, customMeetingId);
const joinUrl = helpers.getJoinURL(this.meetingId, this.initParameters, isModerator, customParameter);
2022-06-08 02:52:22 +08:00
const response = await this.page.goto(joinUrl);
await expect(response.ok()).toBeTruthy();
const hasErrorLabel = await this.checkElement(e.errorMessageLabel);
2022-06-08 02:52:22 +08:00
await expect(hasErrorLabel, 'Getting error when joining. Check if the BBB_URL and BBB_SECRET are set correctly').toBeFalsy();
2022-03-29 21:53:07 +08:00
this.settings = await generateSettingsData(this.page);
const { autoJoinAudioModal } = this.settings;
if (shouldCloseAudioModal && autoJoinAudioModal) await this.closeAudioModal();
}
2023-02-16 02:41:21 +08:00
async handleDownload(locator, testInfo, timeout = ELEMENT_WAIT_TIME) {
2022-03-21 23:04:43 +08:00
const [download] = await Promise.all([
this.page.waitForEvent('download', { timeout }),
2023-02-16 03:52:06 +08:00
locator.click({ timeout }),
2022-03-21 23:04:43 +08:00
]);
await expect(download).toBeTruthy();
const filePath = await download.path();
const content = await readFileSync(filePath, 'utf8');
await testInfo.attach('downloaded', { path: filePath });
2022-03-21 23:04:43 +08:00
return {
download,
content,
}
}
2023-02-14 21:59:46 +08:00
async handleNewTab(selector, context) {
2022-09-07 00:52:45 +08:00
const [newPage] = await Promise.all([
context.waitForEvent('page'),
this.waitAndClick(selector),
]);
return newPage;
}
2021-11-27 04:01:41 +08:00
async joinMicrophone() {
await this.waitForSelector(e.audioModal);
await this.waitAndClick(e.microphoneButton);
await this.waitForSelector(e.stopHearingButton);
await this.waitAndClick(e.joinEchoTestButton);
await this.waitForSelector(e.establishingAudioLabel);
await this.wasRemoved(e.establishingAudioLabel, ELEMENT_WAIT_LONGER_TIME);
2021-11-27 04:01:41 +08:00
await this.waitForSelector(e.isTalking);
}
2021-12-15 01:10:44 +08:00
async leaveAudio() {
await this.waitAndClick(e.audioDropdownMenu);
2021-12-15 01:10:44 +08:00
await this.waitAndClick(e.leaveAudio);
await this.waitForSelector(e.joinAudio);
}
async logoutFromMeeting() {
2022-01-13 23:11:17 +08:00
await this.waitAndClick(e.optionsButton);
2021-12-15 01:10:44 +08:00
await this.waitAndClick(e.logout);
}
2022-01-29 03:52:22 +08:00
async shareWebcam(shouldConfirmSharing = true, videoPreviewTimeout = ELEMENT_WAIT_TIME) {
2022-06-08 02:52:22 +08:00
const { webcamSharingEnabled } = getSettings();
2022-03-29 21:53:07 +08:00
test.fail(!webcamSharingEnabled, 'Webcam sharing is disabled');
2021-11-27 04:01:41 +08:00
await this.waitAndClick(e.joinVideo);
if (shouldConfirmSharing) {
await this.bringToFront();
2021-11-27 04:01:41 +08:00
await this.waitForSelector(e.videoPreview, videoPreviewTimeout);
await this.waitAndClick(e.startSharingWebcam);
2021-11-26 02:23:58 +08:00
}
await this.waitForSelector(e.webcamContainer, VIDEO_LOADING_WAIT_TIME);
2021-11-27 04:01:41 +08:00
await this.waitForSelector(e.leaveVideo, VIDEO_LOADING_WAIT_TIME);
await this.wasRemoved(e.webcamConnecting);
2021-11-27 04:01:41 +08:00
}
2022-01-20 03:50:59 +08:00
getLocator(selector) {
2021-11-26 02:23:58 +08:00
return this.page.locator(selector);
}
2022-02-05 03:26:35 +08:00
getLocatorByIndex(selector, index) {
return this.page.locator(selector).nth(index);
}
async getSelectorCount(selector) {
2022-01-20 03:50:59 +08:00
const locator = this.getLocator(selector);
2021-11-26 02:23:58 +08:00
return locator.count();
}
2022-09-16 22:22:05 +08:00
async getCopiedText(context) {
2023-02-14 21:59:46 +08:00
await context.grantPermissions(['clipboard-write', 'clipboard-read'], { origin: process.env.BBB_URL });
2022-09-16 22:22:05 +08:00
return this.page.evaluate(async () => navigator.clipboard.readText());
}
async closeAudioModal() {
2021-11-26 02:23:58 +08:00
await this.waitForSelector(e.audioModal, ELEMENT_WAIT_LONGER_TIME);
2022-01-20 21:03:18 +08:00
await this.waitAndClick(e.closeModal);
}
async waitForSelector(selector, timeout = ELEMENT_WAIT_TIME) {
await this.page.waitForSelector(selector, { timeout });
}
2023-02-14 21:59:46 +08:00
async waitForSelectorDetached(selector, timeout = ELEMENT_WAIT_TIME) {
await this.page.waitForSelector(selector, { state: 'detached', timeout });
}
async getElementBoundingBox(selector) {
const element = await this.page.$(selector);
return element.boundingBox();
}
2022-02-05 03:26:35 +08:00
async waitUntilHaveCountSelector(selector, count, timeout = ELEMENT_WAIT_TIME) {
await this.page.waitForFunction(
checkElementLengthEqualTo,
[selector, count],
{ timeout },
);
}
async type(selector, text) {
2022-01-20 03:50:59 +08:00
const handle = this.getLocator(selector);
await handle.focus();
await handle.type(text, { timeout: ELEMENT_WAIT_TIME });
}
2021-11-26 02:23:58 +08:00
async waitAndClickElement(element, index = 0, timeout = ELEMENT_WAIT_TIME) {
await this.waitForSelector(element, timeout);
await this.page.evaluate(([elem, i]) => {
document.querySelectorAll(elem)[i].click();
}, [element, index]);
}
async waitAndClick(selector, timeout = ELEMENT_WAIT_TIME) {
await this.waitForSelector(selector, timeout);
await this.page.focus(selector);
await this.page.click(selector, { timeout });
}
2021-11-23 01:06:59 +08:00
2022-02-03 08:45:23 +08:00
async clickOnLocator(locator, timeout = ELEMENT_WAIT_TIME) {
await locator.click({ timeout });
}
2021-12-01 22:02:26 +08:00
async checkElement(selector, index = 0) {
return this.page.evaluate(checkElement, [selector, index]);
}
2021-11-23 01:06:59 +08:00
async wasRemoved(selector, timeout = ELEMENT_WAIT_TIME) {
2022-01-20 03:50:59 +08:00
const locator = this.getLocator(selector);
2021-11-23 01:06:59 +08:00
await expect(locator).toBeHidden({ timeout });
}
async wasNthElementRemoved(selector, count, timeout = ELEMENT_WAIT_TIME) {
const locator = this.getLocator(':nth-match(' + selector + ',' + count + ')');
await expect(locator).toBeHidden({ timeout });
}
2021-11-23 01:06:59 +08:00
async hasElement(selector, timeout = ELEMENT_WAIT_TIME) {
2022-01-20 03:50:59 +08:00
const locator = this.getLocator(selector);
2021-11-23 01:06:59 +08:00
await expect(locator).toBeVisible({ timeout });
}
2021-11-26 02:23:58 +08:00
async hasNElements(selector, count, timeout = ELEMENT_WAIT_TIME) {
const locator = this.getLocator(':nth-match(' + selector + ',' + count + ')');
await expect(locator).toBeVisible({ timeout });
}
2022-01-29 03:52:22 +08:00
async hasElementDisabled(selector, timeout = ELEMENT_WAIT_TIME) {
const locator = this.getLocator(selector);
await expect(locator).toBeDisabled({ timeout });
}
2022-02-03 08:45:23 +08:00
async hasElementEnabled(selector, timeout = ELEMENT_WAIT_TIME) {
const locator = this.getLocator(`${selector}:not([disabled])`);
2022-02-03 08:45:23 +08:00
await expect(locator).toBeEnabled({ timeout });
}
2021-11-26 02:23:58 +08:00
async hasText(selector, text, timeout = ELEMENT_WAIT_TIME) {
const locator = this.getLocator(selector).first();
2021-11-26 02:23:58 +08:00
await expect(locator).toContainText(text, { timeout });
}
2022-07-21 03:44:09 +08:00
async press(key) {
await this.page.keyboard.press(key);
}
async down(key) {
await this.page.keyboard.down(key);
}
async up(key) {
await this.page.keyboard.up(key);
}
2023-02-14 21:59:46 +08:00
2022-12-01 19:19:03 +08:00
async mouseDoubleClick(x, y) {
await this.page.mouse.dblclick(x, y);
}
2022-09-16 02:31:10 +08:00
async dragDropSelector(selector, position) {
await this.getLocator(selector).dragTo(this.page.locator(position), { timeout: ELEMENT_WAIT_TIME });
}
async dragAndDropWebcams(position) {
await this.getLocator(e.webcamContainer).first().hover({ timeout: 5000 });
await this.page.mouse.down();
await this.getLocator(e.whiteboard).hover({ timeout: 5000 }); // action for dispatching isDragging event
await this.getLocator(position).hover({ timeout: 5000 });
await this.page.mouse.up();
2022-09-16 02:31:10 +08:00
}
async checkElementCount(selector, count) {
const locator = await this.page.locator(selector);
await expect(locator).toHaveCount(count);
}
async hasValue(selector, value) {
2023-02-14 21:59:46 +08:00
const locator = await this.page.locator(selector);
await expect(locator).toHaveValue(value);
}
2022-11-11 04:25:18 +08:00
2022-11-24 22:52:06 +08:00
async backgroundColorTest(selector, color) {
2022-11-11 04:25:18 +08:00
await expect(await this.page.$eval(selector, e => getComputedStyle(e).backgroundColor)).toBe(color);
}
2022-11-24 22:52:06 +08:00
async textColorTest(selector, color) {
2022-11-11 04:25:18 +08:00
await expect(await this.page.$eval(selector, e => getComputedStyle(e).color)).toBe(color);
}
2023-01-19 00:29:45 +08:00
async fontSizeCheck(selector, size) {
await expect(await this.page.$eval(selector, e => getComputedStyle(e).fontSize)).toBe(size);
}
2023-02-11 00:20:14 +08:00
async comparingSelectorsBackgroundColor(selector1, selector2) {
const getBackgroundColorComputed = (locator) => locator.evaluate((elem) => getComputedStyle(elem).backgroundColor);
const avatarInToastElementColor = this.page.locator(selector1);
const avatarInUserListColor = this.page.locator(selector2);
await expect(getBackgroundColorComputed(avatarInToastElementColor)).toStrictEqual(getBackgroundColorComputed(avatarInUserListColor));
}
2023-03-09 04:34:54 +08:00
async reloadPage() {
await this.page.reload();
}
}
2021-11-03 06:50:20 +08:00
module.exports = exports = Page;