* Mock react-virtualized-auto-sizer in all unit tests to ensure contents are mounted
Without mocking that, anything inside of an `AutoSizer` wouldn't be rendered
at all, leading to weird behaviour like the emoji picker being empty in tests.
* Fix EmojiPickerCategoryRow not having a defaultMessage
This caused any unit tests involving the emoji picker to error unless you
also provided an intlMessages object which is hard to do due to how Jest's
`moduleNameMapper` option is configured currently.
* Fixed incorrect usage of useCallback in EmojiPickerItem
The way this was previously written, we weren't specifying dependencies
properly for those callbacks which lead to some actual performance issues
because of how heavy the emoji picker is, and it also lead to issues where
click handlers would have to be triggered multiple times in unit tests
for some reason.
* MM-66659 Stop using caretPosition in useEditorEmojiPicker
This bug seems to be caused by a race condition between React and Chrome which
only occurs on Windows and only now that we've upgraded to React 18. That race
condition occurs with how we map the selection range in the post textbox to
its `caretPosition` state. That state has always been pretty janky, so I've
decided to remove it.
This also has some other benefits like improving UX by using `execCommand`
which preserves the undo/redo stack when using the emoji picker and simplifying
the code because we no longer have to manually mess with the cursor position.
Also, while I was in there, I fixed some minor weirdness around how whitespace
is handled when adding those emojis.
* MM-66659 Stop using caretPosition in useKeyHandler
* MM-66659 Stop using caretPosition for prefill message
* MM-66659 Remove remaining traces of caretPosition in AdvancedTextEditor
* Fix exclusive test
* Fix "Fixed incorrect usage of useCallback in EmojiPickerItem"
* Fixed incorrect usage of FixedSizeList in emoji picker
This is the actual fix to the issue I thought I fixed previously by changing
how `useCallback` was called in EmojiPickerItem.
It turns out the actual issue is that we were passing a new function as the
child of FixedSizeList on every render cycle which caused it to re-render all
of the rows which somehow also assigned them new DOM Nodes as well. The
EmojiPicker re-renders whenever we mouse over an emoji, so it would re-render
the FixedSizeList and all of its children frequently when mousing over things.
Apart from causing performance issues, that also seems to cause RTL to fail to
click on an emoji the first time you call `userAgent.click` because the first
call would move the mouse which would trigger a re-render, and that would
seemingly cause RTL to forget what to click on.
To fix that, I used a new React context to pass the extra props to the
EmojiPickerCategoryOrEmojiRow. I could've also done that by adding them to the
FixedSizeList's `itemData` prop, but that already contains a lot of heavy data,
and I didn't want to add to that.
* Revert changes to postMessageOnKeyPress
* Run Prettier in playwright folder
* Added docs to new Playwright test
* Address feedback
(cherry picked from commit f0815ae9f8)
Co-authored-by: Harrison Healey <[email protected]>
@mattermost/playwright-lib
A comprehensive end-to-end testing library for Mattermost web, desktop and plugin applications using Playwright.
Overview
This library provides:
- Pre-built page objects and components for common Mattermost UI elements
- Server configuration and initialization utilities
- Test fixtures and helpers
- Visual testing support with Percy integration
- Accessibility testing support with axe-core
- Browser notification mocking
- File handling utilities
- Common test actions and assertions
Installation
npm install @mattermost/playwright-lib
Usage
Basic example of logging in and posting a message:
import {test, expect} from '@mattermost/playwright-lib';
test('user can post message', async ({pw}) => {
// # Create and login a new user
const {user} = await pw.initSetup();
const {channelsPage} = await pw.testBrowser.login(user);
// # Navigate and post a message
await channelsPage.goto();
const message = 'Hello World!';
await channelsPage.postMessage(message);
// * Verify message appears
const lastPost = await channelsPage.getLastPost();
await expect(lastPost).toHaveText(message);
});
Key Components
Page Objects
Ready-to-use page objects for common Mattermost pages:
- Login
- Signup
- Channels
- System Console
- And more...
UI Components
Reusable component objects for UI elements:
- Headers
- Posts
- Menus
- Modals
- And more...
Test Utilities
Helper functions for common testing needs:
- Server setup and configuration
- User/team creation
- File handling
- Visual testing
- And more...
Configuration
The library can be configured via optional environment variables:
Environment Variables
All environment variables are optional with sensible defaults.
Server Configuration
| Variable | Description | Default |
|---|---|---|
PW_BASE_URL |
Server URL | http://localhost:8065 |
PW_ADMIN_USERNAME |
Admin username | sysadmin |
PW_ADMIN_PASSWORD |
Admin password | Sys@dmin-sample1 |
PW_ADMIN_EMAIL |
Admin email | [email protected] |
PW_ENSURE_PLUGINS_INSTALLED |
Comma-separated list of plugins to install | [] |
PW_RESET_BEFORE_TEST |
Reset server before test | false |
High Availability Cluster Settings
| Variable | Description | Default |
|---|---|---|
PW_HA_CLUSTER_ENABLED |
Enable HA cluster | false |
PW_HA_CLUSTER_NODE_COUNT |
Number of cluster nodes | 2 |
PW_HA_CLUSTER_NAME |
Cluster name | mm_dev_cluster |
Push Notifications
| Variable | Description | Default |
|---|---|---|
PW_PUSH_NOTIFICATION_SERVER |
Push notification server URL | https://push-test.mattermost.com |
Playwright Settings
| Variable | Description | Default |
|---|---|---|
PW_HEADLESS |
Run tests headless | true |
PW_SLOWMO |
Add delay between actions in ms | 0 |
PW_WORKERS |
Number of parallel workers | 1 |
Visual Testing
| Variable | Description | Default |
|---|---|---|
PW_SNAPSHOT_ENABLE |
Enable snapshot testing | false |
PW_PERCY_ENABLE |
Enable Percy visual testing | false |
CI Settings
| Variable | Description | Default |
|---|---|---|
CI |
Set automatically in CI environments | N/A |
Accessibility Testing
The library includes built-in accessibility testing using axe-core:
import {test, expect} from '@mattermost/playwright-lib';
test('verify login page accessibility', async ({page, axe}) => {
// # Navigate to login page
await page.goto('/login');
// # Run accessibility scan
const results = await axe.builder(page).analyze();
// * Verify no accessibility violations
expect(results.violations).toHaveLength(0);
});
The axe-core integration:
- Runs WCAG 2.0 Level A & AA rules by default
- Provides detailed violation reports
- Supports rule customization
- Can be configured per-test or globally
Visual Testing
The library supports visual testing through Playwright's built-in visual comparisons and Percy integration:
import {test, expect} from '@mattermost/playwright-lib';
test('verify channel header appearance', async ({pw, browserName, viewport}, testInfo) => {
// # Setup and login
const {user} = await pw.initSetup();
const {page, channelsPage} = await pw.testBrowser.login(user);
// # Navigate and prepare page
await channelsPage.goto();
await expect(channelsPage.appBar.playbooksIcon).toBeVisible();
await pw.hideDynamicChannelsContent(page);
// * Take and verify snapshot
await pw.matchSnapshot(testInfo, {page, browserName, viewport});
});
Browser Notifications
Mock and verify browser notifications:
import {test, expect} from '@mattermost/playwright-lib';
test('verify notification on mention', async ({pw}) => {
// # Setup users and team
const {team, adminUser, user} = await pw.initSetup();
// # Setup admin browser with notifications
const {page: adminPage, channelsPage: adminChannelsPage} = await pw.testBrowser.login(adminUser);
await adminChannelsPage.goto(team.name, 'town-square');
await pw.stubNotification(adminPage, 'granted');
// # Setup user browser and post mention
const {channelsPage: userChannelsPage} = await pw.testBrowser.login(user);
await userChannelsPage.goto(team.name, 'off-topic');
await userChannelsPage.postMessage(`@ALL good morning, ${team.name}!`);
// * Verify notification received
const notifications = await pw.waitForNotification(adminPage);
expect(notifications.length).toBe(1);
});
Contributing
See CONTRIBUTING.md for development setup and guidelines.
License
See LICENSE.txt for license information.