Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions e2e/specs/linux/wayland_launch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {test, expect} from '../../fixtures/index';

test(
'LNX-05 app launches in a Wayland session',
{tag: ['@P2', '@wayland']},
async ({electronApp, mainWindow}) => {
const sessionType = await electronApp.evaluate(() => process.env.XDG_SESSION_TYPE ?? '');
expect(sessionType.toLowerCase()).toBe('wayland');

expect(mainWindow).toBeDefined();
await expect.poll(
() => mainWindow.evaluate(() => document.readyState === 'complete'),
{timeout: 15_000},
).toBe(true);
},
);
55 changes: 33 additions & 22 deletions e2e/specs/linux_dark_mode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

import {test, expect} from '../fixtures/index';

async function toggleDarkMode(electronApp: import('playwright').ElectronApplication) {
await electronApp.evaluate(({app}) => {
const viewMenu = (app as any).applicationMenu?.getMenuItemById('view');
async function toggleDarkModeLinux(electronApp: import('playwright').ElectronApplication) {
await electronApp.evaluate(({Menu}) => {
const viewMenu = Menu.getApplicationMenu()?.getMenuItemById('view');
const darkModeItem = viewMenu?.submenu?.items?.find(
(item: any) => item.label?.toLowerCase().includes('dark mode'),
(item) => item.label?.toLowerCase().includes('dark mode'),
);
if (!darkModeItem) {
throw new Error('Toggle Dark Mode menu item not found in View menu');
Expand All @@ -16,31 +16,42 @@ async function toggleDarkMode(electronApp: import('playwright').ElectronApplicat
});
}

test.describe('dark_mode', () => {
test('MM-T2465 Linux Dark Mode Toggle', {tag: ['@P2', '@linux']}, async ({mainWindow, electronApp}) => {
if (process.platform !== 'linux') {
test.skip(true, 'Linux only');
return;
async function setDarkModeConfig(electronApp: import('playwright').ElectronApplication, enabled: boolean) {
await electronApp.evaluate(({ipcMain}, darkMode: boolean) => {
const refs = (global as any).__e2eTestRefs;
const Config = refs?.Config;
if (!Config) {
throw new Error('__e2eTestRefs.Config is unavailable');
}
Config.set('darkMode', darkMode);
ipcMain.emit('emit-configuration', null, Config.data);
}, enabled);
}

test.describe('dark_mode', () => {
test('MM-T2465 Linux Dark Mode Toggle', {tag: ['@P2', '@linux']}, async ({mainWindow, electronApp}) => {
expect(mainWindow).not.toBeNull();

// Toggle Dark Mode
await toggleDarkMode(electronApp);

// The darkMode class is applied to document.body, not to .topBar directly
await mainWindow.waitForSelector('body.darkMode', {timeout: 10000});
await toggleDarkModeLinux(electronApp);
await mainWindow.waitForSelector('body.darkMode', {timeout: 10_000});
expect(await mainWindow.evaluate(() => document.body.className)).toContain('darkMode');

const bodyClassWithDarkMode = await mainWindow.evaluate(() => document.body.className);
expect(bodyClassWithDarkMode).toContain('darkMode');
await toggleDarkModeLinux(electronApp);
await mainWindow.waitForSelector('body:not(.darkMode)', {timeout: 10_000});
expect(await mainWindow.evaluate(() => document.body.className)).not.toContain('darkMode');
});

// Toggle Light Mode
await toggleDarkMode(electronApp);
test('MM-T1310 On Mac set Appearance to Dark — macOS ONLY', {tag: ['@P2', '@darwin']}, async ({mainWindow, electronApp}) => {
expect(mainWindow).not.toBeNull();

// Wait for dark mode class to be removed
await mainWindow.waitForSelector('body:not(.darkMode)', {timeout: 10000});
// macOS does not expose "Toggle Dark Mode" in the View menu (linux-only).
// Dark mode for the application chrome is driven by Config.darkMode.
await setDarkModeConfig(electronApp, true);
await mainWindow.waitForSelector('body.darkMode', {timeout: 10_000});
expect(await mainWindow.evaluate(() => document.body.className)).toContain('darkMode');

const bodyClassWithLightMode = await mainWindow.evaluate(() => document.body.className);
expect(bodyClassWithLightMode).not.toContain('darkMode');
await setDarkModeConfig(electronApp, false);
await mainWindow.waitForSelector('body:not(.darkMode)', {timeout: 10_000});
expect(await mainWindow.evaluate(() => document.body.className)).not.toContain('darkMode');
});
});
60 changes: 14 additions & 46 deletions e2e/specs/server_management/drag_and_drop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import * as os from 'os';
import * as path from 'path';

import {test, expect} from '../../fixtures/index';
import {waitForAppReady} from '../../helpers/appReadiness';
import {electronBinaryPath, appDir, demoMattermostConfig, writeConfigFile} from '../../helpers/config';
import {waitForLockFileRelease} from '../../helpers/cleanup';
import {demoMattermostConfig} from '../../helpers/config';
import {launchDirectTestApp} from '../../helpers/directLaunch';
import {closeElectronAppFast} from '../../helpers/electronApp';
import {loginToMattermost} from '../../helpers/login';
import {recoverServerViewIfNeeded, waitForMattermostShell} from '../../helpers/mattermostShell';
import {buildServerMap} from '../../helpers/serverMap';

if (!process.env.MM_TEST_SERVER_URL) {
Expand Down Expand Up @@ -59,34 +60,6 @@ async function waitForWindow(app: ElectronApplication, pattern: string, timeout
throw new Error(`Timed out waiting for window matching "${pattern}"`);
}

async function closeElectronApp(app: ElectronApplication, dataDir: string) {
let pid: number | undefined;
try {
pid = app.process()?.pid;
} catch {
pid = undefined;
}

let cleanClosed = false;
await Promise.race([
app.close().catch(() => {}).then(() => {
cleanClosed = true;
}),
new Promise<void>((resolve) => setTimeout(resolve, 10_000)),
]);

if (!cleanClosed && pid) {
try {
process.kill(pid, 'SIGTERM');
} catch {
// already exited
}
return;
}

await waitForLockFileRelease(dataDir).catch(() => {});
}

async function getMattermostServer() {
const serverMap = await buildServerMap(electronApp);
const mmServer = serverMap[config.servers[0].name]?.[0]?.win;
Expand Down Expand Up @@ -185,16 +158,7 @@ test.describe('server_management/drag_and_drop', () => {

test.beforeAll(async () => {
userDataDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'mm-drag-drop-e2e-'));
writeConfigFile(userDataDir, config);

const {_electron: electron} = await import('playwright');
electronApp = await electron.launch({
executablePath: electronBinaryPath,
args: [appDir, `--user-data-dir=${userDataDir}`, '--no-sandbox', '--disable-gpu'],
env: {...process.env, NODE_ENV: 'test'},
timeout: 60_000,
});
await waitForAppReady(electronApp);
electronApp = await launchDirectTestApp(userDataDir, config);
mainWindow = await waitForWindow(electronApp, 'index');
const mmServer = await getMattermostServer();
await loginToMattermost(mmServer);
Expand All @@ -206,7 +170,7 @@ test.describe('server_management/drag_and_drop', () => {
});

test.afterAll(async () => {
await closeElectronApp(electronApp, userDataDir);
await closeElectronAppFast(electronApp, userDataDir);
});

test.describe('MM-T2635 should be able to drag and drop tabs', () => {
Expand All @@ -231,13 +195,15 @@ test.describe('server_management/drag_and_drop', () => {
const secondTab = await mainWindow.waitForSelector('.TabBar li.serverTabItem:nth-child(2)', {timeout: 10_000});
await secondTab.click();
const secondView = localServerMap[serverName][1].win;
await secondView.waitForSelector('#sidebarItem_off-topic', {timeout: 30_000});
await waitForMattermostShell(secondView, {channelItem: '#sidebarItem_off-topic'});
await recoverServerViewIfNeeded(secondView, {channelItem: '#sidebarItem_off-topic'});
await secondView.click('#sidebarItem_off-topic');

const thirdTab = await mainWindow.waitForSelector('.TabBar li.serverTabItem:nth-child(3)', {timeout: 10_000});
await thirdTab.click();
const thirdView = localServerMap[serverName][2].win;
await thirdView.waitForSelector('#sidebarItem_town-square', {timeout: 30_000});
await waitForMattermostShell(thirdView, {channelItem: '#sidebarItem_town-square'});
await recoverServerViewIfNeeded(thirdView, {channelItem: '#sidebarItem_town-square'});
await thirdView.click('#sidebarItem_town-square');

// Tab titles update asynchronously after channel navigation — poll for each.
Expand All @@ -263,13 +229,15 @@ test.describe('server_management/drag_and_drop', () => {
const secondTab = await mainWindow.waitForSelector('.TabBar li.serverTabItem:nth-child(2)', {timeout: 10_000});
await secondTab.click();
const secondView = localServerMap[serverName][1].win;
await secondView.waitForSelector('#sidebarItem_off-topic', {timeout: 30_000});
await waitForMattermostShell(secondView, {channelItem: '#sidebarItem_off-topic'});
await recoverServerViewIfNeeded(secondView, {channelItem: '#sidebarItem_off-topic'});
await secondView.click('#sidebarItem_off-topic');

const thirdTab = await mainWindow.waitForSelector('.TabBar li.serverTabItem:nth-child(3)', {timeout: 10_000});
await thirdTab.click();
const thirdView = localServerMap[serverName][2].win;
await thirdView.waitForSelector('#sidebarItem_town-square', {timeout: 30_000});
await waitForMattermostShell(thirdView, {channelItem: '#sidebarItem_town-square'});
await recoverServerViewIfNeeded(thirdView, {channelItem: '#sidebarItem_town-square'});
await thirdView.click('#sidebarItem_town-square');

const visibleTabOrder = await getVisibleTabOrder();
Expand Down
Loading
Loading