mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 03:12:29 +00:00
feat(tests): Adds chat panel and codec selection tests. (#15416)
* fix(tests): Another attempt to fix Firefox excludes. Drawback is that it will be a little bit slow. * feat(tests): Adds chatPanel tests. * feat(tests): Adds codec selection tests.
This commit is contained in:
14
package-lock.json
generated
14
package-lock.json
generated
@@ -136,6 +136,7 @@
|
||||
"@types/js-md5": "0.4.3",
|
||||
"@types/jsonwebtoken": "9.0.7",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/minimatch": "5.1.2",
|
||||
"@types/mocha": "10.0.10",
|
||||
"@types/moment-duration-format": "2.2.6",
|
||||
"@types/offscreencanvas": "2019.7.2",
|
||||
@@ -7176,6 +7177,13 @@
|
||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/minimatch": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
|
||||
"integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/mocha": {
|
||||
"version": "10.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz",
|
||||
@@ -29785,6 +29793,12 @@
|
||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/minimatch": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
|
||||
"integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/mocha": {
|
||||
"version": "10.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz",
|
||||
|
||||
@@ -142,6 +142,7 @@
|
||||
"@types/js-md5": "0.4.3",
|
||||
"@types/jsonwebtoken": "9.0.7",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/minimatch": "5.1.2",
|
||||
"@types/mocha": "10.0.10",
|
||||
"@types/moment-duration-format": "2.2.6",
|
||||
"@types/offscreencanvas": "2019.7.2",
|
||||
|
||||
@@ -5,6 +5,7 @@ import { multiremotebrowser } from '@wdio/globals';
|
||||
import { IConfig } from '../../react/features/base/config/configType';
|
||||
import { urlObjectToString } from '../../react/features/base/util/uri';
|
||||
import BreakoutRooms from '../pageobjects/BreakoutRooms';
|
||||
import ChatPanel from '../pageobjects/ChatPanel';
|
||||
import Filmstrip from '../pageobjects/Filmstrip';
|
||||
import IframeAPI from '../pageobjects/IframeAPI';
|
||||
import Notifications from '../pageobjects/Notifications';
|
||||
@@ -121,7 +122,10 @@ export class Participant {
|
||||
async joinConference(ctx: IContext, options: IJoinOptions = {}): Promise<void> {
|
||||
const config = {
|
||||
room: ctx.roomName,
|
||||
configOverwrite: this.config,
|
||||
configOverwrite: {
|
||||
...this.config,
|
||||
...options.configOverwrite || {}
|
||||
},
|
||||
interfaceConfigOverwrite: {
|
||||
SHOW_CHROME_EXTENSION_BANNER: false
|
||||
}
|
||||
@@ -338,6 +342,13 @@ export class Participant {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the chat panel for this participant.
|
||||
*/
|
||||
getChatPanel(): ChatPanel {
|
||||
return new ChatPanel(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BreakoutRooms for this participant.
|
||||
*
|
||||
|
||||
@@ -28,26 +28,25 @@ export async function ensureOneParticipant(ctx: IContext, options?: IJoinOptions
|
||||
* Ensure that there are three participants.
|
||||
*
|
||||
* @param {Object} ctx - The context.
|
||||
* @param {IJoinOptions} options - The options to use when joining the participant.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function ensureThreeParticipants(ctx: IContext): Promise<void> {
|
||||
await joinTheModeratorAsP1(ctx);
|
||||
|
||||
const p2 = new Participant('participant2');
|
||||
const p3 = new Participant('participant3');
|
||||
|
||||
ctx.p2 = p2;
|
||||
ctx.p3 = p3;
|
||||
export async function ensureThreeParticipants(ctx: IContext, options?: IJoinOptions): Promise<void> {
|
||||
await joinTheModeratorAsP1(ctx, options);
|
||||
|
||||
// these need to be all, so we get the error when one fails
|
||||
await Promise.all([
|
||||
p2.joinConference(ctx),
|
||||
p3.joinConference(ctx)
|
||||
_joinParticipant('participant2', ctx.p2, p => {
|
||||
ctx.p2 = p;
|
||||
}, options),
|
||||
_joinParticipant('participant3', ctx.p3, p => {
|
||||
ctx.p3 = p;
|
||||
}, options)
|
||||
]);
|
||||
|
||||
await Promise.all([
|
||||
p2.waitForRemoteStreams(2),
|
||||
p3.waitForRemoteStreams(2)
|
||||
ctx.p2.waitForRemoteStreams(2),
|
||||
ctx.p3.waitForRemoteStreams(2)
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { IConfig } from '../../react/features/base/config/configType';
|
||||
|
||||
import type { Participant } from './Participant';
|
||||
import WebhookProxy from './WebhookProxy';
|
||||
|
||||
@@ -17,6 +19,11 @@ export type IContext = {
|
||||
|
||||
export type IJoinOptions = {
|
||||
|
||||
/**
|
||||
* Config overwrites to use.
|
||||
*/
|
||||
configOverwrite?: IConfig;
|
||||
|
||||
/**
|
||||
* Whether to skip setting display name.
|
||||
*/
|
||||
|
||||
22
tests/pageobjects/ChatPanel.ts
Normal file
22
tests/pageobjects/ChatPanel.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import BasePageObject from './BasePageObject';
|
||||
|
||||
/**
|
||||
* Chat panel elements.
|
||||
*/
|
||||
export default class ChatPanel extends BasePageObject {
|
||||
/**
|
||||
* Is chat panel open.
|
||||
*/
|
||||
async isOpen() {
|
||||
return await this.participant.driver.$('#sideToolbarContainer').isExisting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Presses the "chat" keyboard shortcut which opens or closes the chat
|
||||
* panel.
|
||||
*/
|
||||
async pressShortcut() {
|
||||
await this.participant.driver.$('body').click();
|
||||
await this.participant.driver.keys([ 'c' ]);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ import BasePageObject from './BasePageObject';
|
||||
|
||||
const AUDIO_MUTE = 'Mute microphone';
|
||||
const AUDIO_UNMUTE = 'Unmute microphone';
|
||||
const CHAT = 'Open chat';
|
||||
const CLOSE_CHAT = 'Close chat';
|
||||
const CLOSE_PARTICIPANTS_PANE = 'Close participants pane';
|
||||
const OVERFLOW_MENU = 'More actions menu';
|
||||
const OVERFLOW = 'More actions';
|
||||
@@ -142,6 +144,22 @@ export default class Toolbar extends BasePageObject {
|
||||
await this.getButton(RAISE_HAND).click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks on the chat button that opens chat panel.
|
||||
*/
|
||||
async clickChatButton(): Promise<void> {
|
||||
this.participant.log('Clicking on: Chat Button');
|
||||
await this.getButton(CHAT).click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks on the chat button that closes chat panel.
|
||||
*/
|
||||
async clickCloseChatButton(): Promise<void> {
|
||||
this.participant.log('Clicking on: Close Chat Button');
|
||||
await this.getButton(CLOSE_CHAT).click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the overflow menu is open and clicks on a specified button.
|
||||
* @param accessibilityLabel The accessibility label of the button to be clicked.
|
||||
|
||||
121
tests/specs/3way/codecSelection.spec.ts
Normal file
121
tests/specs/3way/codecSelection.spec.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { ensureOneParticipant, ensureThreeParticipants, ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
describe('Codec selection - ', () => {
|
||||
it('asymmetric codecs', async () => {
|
||||
await ensureOneParticipant(ctx, {
|
||||
configOverwrite: {
|
||||
videoQuality: {
|
||||
codecPreferenceOrder: [ 'VP9', 'VP8', 'AV1' ]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await ensureTwoParticipants(ctx, {
|
||||
configOverwrite: {
|
||||
videoQuality: {
|
||||
codecPreferenceOrder: [ 'VP8', 'VP9', 'AV1' ]
|
||||
}
|
||||
}
|
||||
});
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
// Check if media is playing on both endpoints.
|
||||
expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
|
||||
expect(await p2.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
|
||||
|
||||
// Check if p1 is sending VP9 and p2 is sending VP8 as per their codec preferences.
|
||||
// Except on Firefox because it doesn't support VP9 encode.
|
||||
if (p1.driver.isFirefox) {
|
||||
expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp8())).toBe(true);
|
||||
} else {
|
||||
expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp9())).toBe(true);
|
||||
}
|
||||
|
||||
expect(await p2.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp8())).toBe(true);
|
||||
});
|
||||
|
||||
it('asymmetric codecs with AV1', async () => {
|
||||
await ensureThreeParticipants(ctx, {
|
||||
configOverwrite: {
|
||||
videoQuality: {
|
||||
codecPreferenceOrder: [ 'AV1', 'VP9', 'VP8' ]
|
||||
}
|
||||
}
|
||||
});
|
||||
const { p1, p2, p3 } = ctx;
|
||||
|
||||
// Check if media is playing on p3.
|
||||
expect(await p3.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
|
||||
|
||||
// Check if p1 is encoding in VP9, p2 in VP8 and p3 in AV1 as per their codec preferences.
|
||||
// Except on Firefox because it doesn't support AV1/VP9 encode and AV1 decode.
|
||||
if (p1.driver.isFirefox) {
|
||||
expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp8())).toBe(true);
|
||||
} else {
|
||||
expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp9())).toBe(true);
|
||||
}
|
||||
|
||||
expect(await p2.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp8())).toBe(true);
|
||||
|
||||
// If there is a Firefox ep in the call, all other eps will switch to VP9.
|
||||
if (p1.driver.isFirefox) {
|
||||
expect(await p3.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp9())).toBe(true);
|
||||
} else {
|
||||
expect(await p3.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingAv1())).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('codec switch over', async () => {
|
||||
await Promise.all([ ctx.p1.hangup(), ctx.p2.hangup(), ctx.p3.hangup() ]);
|
||||
|
||||
await ensureTwoParticipants(ctx, {
|
||||
configOverwrite: {
|
||||
videoQuality: {
|
||||
codecPreferenceOrder: [ 'VP9', 'VP8', 'AV1' ]
|
||||
}
|
||||
}
|
||||
});
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
// Disable this test on Firefox because it doesn't support VP9 encode.
|
||||
if (p1.driver.isFirefox) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if p1 and p2 are encoding in VP9 which is the default codec.
|
||||
expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp9())).toBe(true);
|
||||
expect(await p2.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp9())).toBe(true);
|
||||
|
||||
await ensureThreeParticipants(ctx, {
|
||||
configOverwrite: {
|
||||
videoQuality: {
|
||||
codecPreferenceOrder: [ 'VP8' ]
|
||||
}
|
||||
}
|
||||
});
|
||||
const { p3 } = ctx;
|
||||
|
||||
// Check if all three participants are encoding in VP8 now.
|
||||
expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp8())).toBe(true);
|
||||
expect(await p2.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp8())).toBe(true);
|
||||
expect(await p3.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp8())).toBe(true);
|
||||
|
||||
await p3.hangup();
|
||||
|
||||
// Check of p1 and p2 have switched to VP9.
|
||||
await p1.driver.waitUntil(
|
||||
async () => await p1.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp9()),
|
||||
{
|
||||
timeout: 10000,
|
||||
timeoutMsg: 'p1 did not switch back to VP9'
|
||||
}
|
||||
);
|
||||
await p2.driver.waitUntil(
|
||||
async () => await p2.driver.execute(() => JitsiMeetJS.app.testing.isLocalCameraEncodingVp9()),
|
||||
{
|
||||
timeout: 10000,
|
||||
timeoutMsg: 'p1 did not switch back to VP9'
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
34
tests/specs/alone/chatPanel.spec.ts
Normal file
34
tests/specs/alone/chatPanel.spec.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { ensureOneParticipant } from '../../helpers/participants';
|
||||
|
||||
describe('Chat Panel - ', () => {
|
||||
it('join participant', async () => {
|
||||
await ensureOneParticipant(ctx);
|
||||
});
|
||||
it('start closed', async () => {
|
||||
expect(await ctx.p1.getChatPanel().isOpen()).toBe(false);
|
||||
});
|
||||
it('open', async () => {
|
||||
const { p1 } = ctx;
|
||||
|
||||
await p1.getToolbar().clickChatButton();
|
||||
expect(await p1.getChatPanel().isOpen()).toBe(true);
|
||||
});
|
||||
it('use shortcut to close', async () => {
|
||||
const chatPanel = ctx.p1.getChatPanel();
|
||||
|
||||
await chatPanel.pressShortcut();
|
||||
expect(await chatPanel.isOpen()).toBe(false);
|
||||
});
|
||||
it('use shortcut to open', async () => {
|
||||
const chatPanel = ctx.p1.getChatPanel();
|
||||
|
||||
await chatPanel.pressShortcut();
|
||||
expect(await chatPanel.isOpen()).toBe(true);
|
||||
});
|
||||
it('use button to open', async () => {
|
||||
const { p1 } = ctx;
|
||||
|
||||
await p1.getToolbar().clickCloseChatButton();
|
||||
expect(await p1.getChatPanel().isOpen()).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
import AllureReporter from '@wdio/allure-reporter';
|
||||
import { multiremotebrowser } from '@wdio/globals';
|
||||
import { Buffer } from 'buffer';
|
||||
import minimatch from 'minimatch';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import pretty from 'pretty';
|
||||
@@ -62,7 +63,7 @@ export const config: WebdriverIO.MultiremoteConfig = {
|
||||
specs: [
|
||||
'specs/**'
|
||||
],
|
||||
maxInstances: 1,
|
||||
maxInstances: 1, // if changing check onWorkerStart logic
|
||||
|
||||
baseUrl: process.env.BASE_URL || 'https://alpha.jitsi.net/torture/',
|
||||
tsConfigPath: './tsconfig.json',
|
||||
@@ -236,13 +237,36 @@ export const config: WebdriverIO.MultiremoteConfig = {
|
||||
* @param {Object} context - The context object.
|
||||
*/
|
||||
beforeTest(test, context) {
|
||||
ctx.skipSuiteTests && context.skip();
|
||||
if (ctx.skipSuiteTests) {
|
||||
context.skip();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
multiremotebrowser.instances.forEach((instance: string) => {
|
||||
logInfo(multiremotebrowser.getInstance(instance), `---=== Start test ${test.title} ===---`);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets executed before a worker process is spawned and can be used to initialize specific service
|
||||
* for that worker as well as modify runtime environments in an async fashion.
|
||||
*/
|
||||
onWorkerStart(...args) {
|
||||
// We run a worker per suite, and replay on this logic here
|
||||
if (args[2].length > 1) {
|
||||
console.warn('Our worker is supposed to get a single suite, but got more than one');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We skip the suite tests if the suite is marked as such, we used that from firefox overwrite
|
||||
// @ts-ignore
|
||||
if (config?.ffExcludes.some((e: string) => minimatch(args[2][0].replace('file://', ''), `${__dirname}/${e}`))) {
|
||||
args[2].pop();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Function to be executed after a test (in Mocha/Jasmine only).
|
||||
*
|
||||
|
||||
@@ -24,7 +24,8 @@ const ffExcludes = [
|
||||
'specs/3way/activeSpeaker.spec.ts' // FF does not support setting a file as mic input
|
||||
];
|
||||
|
||||
export const config = merge(defaultConfig, {
|
||||
const mergedConfig = merge(defaultConfig, {
|
||||
ffExcludes,
|
||||
capabilities: {
|
||||
participant1: {
|
||||
capabilities: {
|
||||
@@ -62,3 +63,9 @@ export const config = merge(defaultConfig, {
|
||||
}
|
||||
}
|
||||
}, { clone: false });
|
||||
|
||||
// Remove the chrome options from the first participant
|
||||
// @ts-ignore
|
||||
mergedConfig.capabilities.participant1.capabilities['goog:chromeOptions'] = undefined;
|
||||
|
||||
export const config = mergedConfig;
|
||||
|
||||
Reference in New Issue
Block a user