Files
59a17a117b MM-66659/MM-66832 Remove caretPosition state from AdvancedTextEditor (#34643) (#34721)
* 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]>
2025-12-12 11:10:00 +02:00
..

@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.