diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx index f7b292fe93..217b1e6b9a 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx @@ -281,6 +281,7 @@ class UserOptions extends PureComponent { // description: , onClick: this.onSaveUserNames, icon: 'download', + dataTest: 'downloadUserNamesList', }); } diff --git a/bigbluebutton-tests/playwright/chat/chat.js b/bigbluebutton-tests/playwright/chat/chat.js index 4fa9861643..4a5f18c14a 100644 --- a/bigbluebutton-tests/playwright/chat/chat.js +++ b/bigbluebutton-tests/playwright/chat/chat.js @@ -3,6 +3,7 @@ const Page = require('../core/page'); const { openChat } = require('./util'); const p = require('../core/parameters'); const e = require('../core/elements'); +const { checkTextContent } = require('../core/util'); class Chat extends Page { constructor(browser, page) { @@ -10,7 +11,7 @@ class Chat extends Page { } async sendPublicMessage() { - await openChat(this.page); + await openChat(this); const message = this.getLocator(e.chatUserMessageText); await expect(message).toHaveCount(0); @@ -21,7 +22,7 @@ class Chat extends Page { } async clearChat() { - await openChat(this.page); + await openChat(this); const message = this.getLocator(e.chatUserMessageText); await this.type(e.chatBox, e.message); @@ -50,21 +51,30 @@ class Chat extends Page { await this.waitForSelector(e.chatUserMessageText); await this.waitAndClick(e.chatCopy); // enable access to browser context clipboard - await context.grantPermissions(['clipboard-write', 'clipboard-read'], { origin: process.env.BBB_SERVER_URL }); + await context.grantPermissions(['clipboard-write', 'clipboard-read'], { origin: process.env.BBB_URL }); const copiedText = await this.page.evaluate(async () => navigator.clipboard.readText()); const check = copiedText.includes(`${p.fullName}: ${e.message}`); expect(check).toBeTruthy(); } - async saveChat() { - await openChat(this.page); + async saveChat(testInfo) { + await openChat(this); + await this.type(e.chatBox, e.message); + await this.waitAndClick(e.sendButton); + await this.waitForSelector(e.chatUserMessageText); await this.waitAndClick(e.chatOptions); - await this.waitAndClick(e.chatSave); - await this.page.waitForEvent('click'); + const { content } = await this.handleDownload(e.chatSave, testInfo); + + const dataToCheck = [ + this.meetingId, + this.username, + e.message, + ]; + await checkTextContent(content, dataToCheck); } async characterLimit() { - await openChat(this.page); + await openChat(this); const messageLocator = this.getLocator(e.chatUserMessageText); await this.page.fill(e.chatBox, e.longMessage5000); @@ -80,7 +90,7 @@ class Chat extends Page { } async emptyMessage() { - await openChat(this.page); + await openChat(this); const messageLocator = this.getLocator(e.chatUserMessageText); await this.waitAndClick(e.sendButton); diff --git a/bigbluebutton-tests/playwright/chat/chat.spec.js b/bigbluebutton-tests/playwright/chat/chat.spec.js index 3f01d9aee5..301821ecc5 100644 --- a/bigbluebutton-tests/playwright/chat/chat.spec.js +++ b/bigbluebutton-tests/playwright/chat/chat.spec.js @@ -22,17 +22,16 @@ test.describe.parallel('Chat', () => { }); test('Copy chat', async ({ browser, context, page }, testInfo) => { - test.fixme(testInfo.config.projects[0].use.headless, 'Only works in headed mode'); + test.fixme(testInfo.project.use.headless, 'Only works in headed mode'); const chat = new Chat(browser, page); await chat.init(true, true); await chat.copyChat(context); }); - test.skip('Save chat', async ({ browser, page }) => { - test.fixme(); + test('Save chat', async ({ browser, page }, testInfo) => { const chat = new Chat(browser, page); await chat.init(true, true); - await chat.saveChat(); + await chat.saveChat(testInfo); }); test('Verify character limit (5000 characters)', async ({ browser, page }) => { diff --git a/bigbluebutton-tests/playwright/chat/util.js b/bigbluebutton-tests/playwright/chat/util.js index cefb575443..f5a4ae2776 100644 --- a/bigbluebutton-tests/playwright/chat/util.js +++ b/bigbluebutton-tests/playwright/chat/util.js @@ -1,8 +1,8 @@ const e = require('../core/elements'); -async function openChat(page) { - await page.waitForSelector(e.chatBox); - await page.waitForSelector(e.chatMessages); +async function openChat(test) { + await test.waitForSelector(e.chatBox); + await test.waitForSelector(e.chatMessages); } exports.openChat = openChat; diff --git a/bigbluebutton-tests/playwright/core/elements.js b/bigbluebutton-tests/playwright/core/elements.js index a19cd02667..a912a09435 100644 --- a/bigbluebutton-tests/playwright/core/elements.js +++ b/bigbluebutton-tests/playwright/core/elements.js @@ -217,6 +217,7 @@ exports.connectionStatusOfflineUser = 'div[data-test="offlineUser"]'; exports.connectionDataContainer = 'div[data-test="networkDataContainer"]'; exports.avatarsWrapperAvatar = 'div[data-test="avatarsWrapperAvatar"]'; exports.guestPolicyLabel = 'li[data-test="guestPolicyLabel"]'; +exports.downloadUserNamesList = 'li[data-test="downloadUserNamesList"]'; exports.waitingUsersBtn = 'div[data-test="waitingUsersBtn"]'; exports.joinMeetingDemoPage = 'div[class^="join-meeting"]'; exports.askModerator = 'button[data-test="askModerator"]'; diff --git a/bigbluebutton-tests/playwright/core/page.js b/bigbluebutton-tests/playwright/core/page.js index f6ad2ac00c..55019bdd4e 100644 --- a/bigbluebutton-tests/playwright/core/page.js +++ b/bigbluebutton-tests/playwright/core/page.js @@ -2,7 +2,7 @@ require('dotenv').config(); const { expect } = require('@playwright/test'); const yaml = require('js-yaml'); const path = require('path'); -const fs = require('fs'); +const { readFileSync } = require('fs'); const parameters = require('./parameters'); const helpers = require('./helpers'); const e = require('./elements'); @@ -18,7 +18,7 @@ class Page { async getSettingsYaml() { try { - return yaml.load(fs.readFileSync(path.join(__dirname, '../../../bigbluebutton-html5/private/config/settings.yml'), 'utf8')); + return yaml.load(readFileSync(path.join(__dirname, '../../../bigbluebutton-html5/private/config/settings.yml'), 'utf8')); } catch (err) { console.log(err); } @@ -46,6 +46,22 @@ class Page { if (shouldCloseAudioModal) await this.closeAudioModal(); } + async handleDownload(selector, testInfo, timeout = ELEMENT_WAIT_TIME) { + const [download] = await Promise.all([ + this.page.waitForEvent('download', { timeout }), + this.waitAndClick(selector, timeout), + ]); + await expect(download).toBeTruthy(); + const path = await download.path(); + const content = await readFileSync(path, 'utf8'); + await testInfo.attach('downloaded', { path }); + + return { + download, + content, + } + } + async joinMicrophone() { await this.waitForSelector(e.audioModal); await this.waitAndClick(e.microphoneButton); diff --git a/bigbluebutton-tests/playwright/core/util.js b/bigbluebutton-tests/playwright/core/util.js index b7ec0a08c6..71d5fea224 100644 --- a/bigbluebutton-tests/playwright/core/util.js +++ b/bigbluebutton-tests/playwright/core/util.js @@ -1,3 +1,5 @@ +const { expect } = require("@playwright/test"); + // Common function checkElement([element, index = 0]) { return document.querySelectorAll(element)[index] !== undefined; @@ -8,5 +10,14 @@ function checkElementLengthEqualTo([element, count]) { return document.querySelectorAll(element).length == count; } +// Text +async function checkTextContent(baseContent, checkData) { + if (typeof checkData === 'string' ) checkData = new Array(checkData); + + const check = checkData.every(word => baseContent.includes(word)); + await expect(check).toBeTruthy(); +} + exports.checkElement = checkElement; exports.checkElementLengthEqualTo = checkElementLengthEqualTo; +exports.checkTextContent = checkTextContent; diff --git a/bigbluebutton-tests/playwright/presentation/presentation.js b/bigbluebutton-tests/playwright/presentation/presentation.js index 5ddc9fc95c..58e0aee2b9 100644 --- a/bigbluebutton-tests/playwright/presentation/presentation.js +++ b/bigbluebutton-tests/playwright/presentation/presentation.js @@ -72,7 +72,7 @@ class Presentation extends MultiUsers { await expect(userSlides0).not.toEqual(userSlides1); } - async allowAndDisallowDownload() { + async allowAndDisallowDownload(testInfo) { // allow the presentation download await this.modPage.waitForSelector(e.whiteboard, ELEMENT_WAIT_LONGER_TIME); await this.modPage.waitAndClick(e.actions); @@ -83,6 +83,7 @@ class Presentation extends MultiUsers { await this.userPage.waitForSelector(e.toastDownload); // check download button in presentation after ALLOW it - should be true await this.userPage.hasElement(e.presentationDownloadBtn); + await this.userPage.handleDownload(e.presentationDownloadBtn, testInfo); // disallow the presentation download await this.modPage.waitAndClick(e.actions); diff --git a/bigbluebutton-tests/playwright/presentation/presentation.spec.js b/bigbluebutton-tests/playwright/presentation/presentation.spec.js index cc951c566e..29c155e834 100644 --- a/bigbluebutton-tests/playwright/presentation/presentation.spec.js +++ b/bigbluebutton-tests/playwright/presentation/presentation.spec.js @@ -27,10 +27,10 @@ test.describe.parallel('Presentation', () => { await presentation.uploadPresentationTest(); }); - test('Allow and disallow presentation download', async ({ browser, context, page }) => { + test('Allow and disallow presentation download', async ({ browser, context, page }, testInfo) => { const presentation = new Presentation(browser, context); await presentation.initPages(page); - await presentation.allowAndDisallowDownload(); + await presentation.allowAndDisallowDownload(testInfo); }); test('Remove all presentation', async ({ browser, context, page }) => { diff --git a/bigbluebutton-tests/playwright/user/multiusers.js b/bigbluebutton-tests/playwright/user/multiusers.js index 9ee13fd256..96c681c2c3 100644 --- a/bigbluebutton-tests/playwright/user/multiusers.js +++ b/bigbluebutton-tests/playwright/user/multiusers.js @@ -4,6 +4,7 @@ const e = require('../core/elements'); const { waitAndClearNotification } = require('../notifications/util'); const { sleep } = require('../core/helpers'); const { checkAvatarIcon, checkIsPresenter } = require('./util'); +const { checkTextContent } = require('../core/util'); class MultiUsers { constructor(browser, context) { @@ -151,6 +152,18 @@ class MultiUsers { await this.modPage.hasElement(e.chatButton); } + async saveUserNames(testInfo) { + await this.modPage.waitAndClick(e.manageUsers); + const { content } = await this.modPage.handleDownload(e.downloadUserNamesList, testInfo); + + const dataToCheck = [ + this.modPage.username, + this.userPage.username, + this.modPage.meetingId, + ]; + await checkTextContent(content, dataToCheck); + } + async selectRandomUser() { // check with no viewer joined await this.modPage.waitAndClick(e.actions); diff --git a/bigbluebutton-tests/playwright/user/user.spec.js b/bigbluebutton-tests/playwright/user/user.spec.js index c1f99ae0b3..a4ae53b427 100644 --- a/bigbluebutton-tests/playwright/user/user.spec.js +++ b/bigbluebutton-tests/playwright/user/user.spec.js @@ -142,6 +142,12 @@ test.describe.parallel('User', () => { }); }); + test('Save user names', async ({ browser, context, page }, testInfo) => { + const multiusers = new MultiUsers(browser, context); + await multiusers.initPages(page); + await multiusers.saveUserNames(testInfo); + }); + test('Select random user', async ({ browser, context, page }) => { const multiusers = new MultiUsers(browser, context); await multiusers.initModPage(page);