snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-16 00:29:55 +05:00
commit 240db4e2a7
15309 changed files with 2173406 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
/**
* @objective Verify that pressing Tab completes the highlighted at-mention suggestion in the message box.
*/
test('MM-T1273 completes at-mention autocomplete with Tab', {tag: '@keyboard_shortcuts'}, async ({pw}) => {
// # Create and log in as a test user
const {user, team} = await pw.initSetup();
const {channelsPage} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'town-square');
await channelsPage.toBeVisible();
// # Type the first characters of the user's own username to trigger the mention autocomplete
const {postCreate} = channelsPage.centerView;
await postCreate.selectFromAutocompleteWithTab(`@${user.username.substring(0, 5)}`);
// * Verify the highlighted username is completed into the message box
expect(await postCreate.getInputValue()).toContain(`@${user.username}`);
});
/**
* @objective Verify that pressing Tab completes the highlighted emoji suggestion in the message box.
*/
test('MM-T1274 completes emoji autocomplete with Tab', {tag: '@keyboard_shortcuts'}, async ({pw}) => {
// # Create and log in as a test user
const {user, team} = await pw.initSetup();
const {channelsPage} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'town-square');
await channelsPage.toBeVisible();
// # Type a partial emoji name to trigger the emoji autocomplete, then select with Tab
const {postCreate} = channelsPage.centerView;
await postCreate.selectFromAutocompleteWithTab(':tomato');
// * Verify the highlighted emoji is completed into the message box.
// Current behavior inserts the rendered emoji glyph rather than the `:tomato:` shortcode.
expect(await postCreate.getInputValue()).toContain('🍅');
});
/**
* @objective Verify that selecting "Keyboard shortcuts" from the Help menu opens the Keyboard Shortcuts modal.
*/
test('MM-T1279 opens keyboard shortcuts modal from the Help menu', {tag: '@keyboard_shortcuts'}, async ({pw}) => {
// # Create and log in as a test user
const {user, team} = await pw.initSetup();
const {channelsPage} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'town-square');
await channelsPage.toBeVisible();
// # Open the Help menu and click "Keyboard shortcuts"
await channelsPage.globalHeader.openKeyboardShortcuts();
// * Verify the Keyboard Shortcuts modal is displayed
await expect(channelsPage.keyboardShortcutsModal).toBeVisible();
// # Close the modal with the Escape key
await channelsPage.page.keyboard.press('Escape');
// * Verify the Keyboard Shortcuts modal is no longer displayed
await expect(channelsPage.keyboardShortcutsModal).not.toBeVisible();
});

View File

@@ -0,0 +1,176 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
/**
* @objective Verify the Ctrl/Cmd+K quick switcher can find and open a direct message, a group message,
* and a public channel.
*/
test(
'MM-T1247 finds and opens a direct message, group message, and channel with Ctrl/Cmd+K',
{tag: '@keyboard_shortcuts'},
async ({pw}) => {
const {adminClient, team, user} = await pw.initSetup();
const [member1, member2] = await adminClient.createUsers(team.id, 2, 'switch');
// # Create a direct message, a group message, and a public channel for the user
const dm = await adminClient.createDirectChannel([user.id, member1.id]);
await adminClient.createPost({channel_id: dm.id, user_id: member1.id, message: 'dm hello'});
const gm = await adminClient.createGroupChannel([user.id, member1.id, member2.id]);
await adminClient.createPost({channel_id: gm.id, user_id: member2.id, message: 'gm hello'});
const publicChannel = await adminClient.createPublicChannel(team.id, `Switcher ${pw.random.id()}`);
await adminClient.addToChannel(user.id, publicChannel.id);
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'town-square');
await channelsPage.toBeVisible();
const {findChannelsModal} = channelsPage;
const options = () => findChannelsModal.container.getByRole('option');
// # Open the quick switcher and open the direct message with member1 (the option with only member1)
await page.keyboard.press('ControlOrMeta+K');
await findChannelsModal.input.fill(member1.username);
await options().filter({hasText: member1.username, hasNotText: member2.username}).first().click();
// * Verify the direct message with member1 is opened
await channelsPage.centerView.header.toHaveTitle(member1.username);
// # Open the quick switcher and open the group message (the option containing both members)
await page.keyboard.press('ControlOrMeta+K');
await findChannelsModal.input.fill(member2.username);
await options().filter({hasText: member1.username}).filter({hasText: member2.username}).first().click();
// * Verify the group message (containing both members) is opened
await channelsPage.centerView.header.toHaveTitle(member1.username);
await channelsPage.centerView.header.toHaveTitle(member2.username);
// # Open the quick switcher and open the public channel
await page.keyboard.press('ControlOrMeta+K');
await findChannelsModal.input.fill(publicChannel.display_name);
await options().filter({hasText: publicChannel.display_name}).first().click();
// * Verify the public channel is opened
await channelsPage.centerView.header.toHaveTitle(publicChannel.display_name);
},
);
/**
* @objective Verify that pressing Shift+Up in the textbox in center channel opens the thread for the last post in RHS
* and correctly focuses the reply textbox, even when there are large messages with attachments from other users.
*/
test(
'MM-T1275 Keyboard shortcuts Shift+Up on center textbox opens the last post in the RHS and correctly focuses the reply textbox',
{tag: '@keyboard_shortcuts'},
async ({pw}, testInfo) => {
const ROOT_MESSAGE = 'The root message for testing Shift+Up keyboard shortcut';
const NUMBER_OF_REPLIES = 10;
const ATTACHMENT_FILES = ['mattermost.png', 'sample_text_file.txt', 'archive.zip'];
test.skip(testInfo.project.name === 'ipad', 'Skipping test on iPad');
// # Initialize setup with admin and user
const {adminUser, user, team} = await pw.initSetup();
// # Log in as admin in one browser session
const {channelsPage: adminChannelsPage} = await pw.testBrowser.login(adminUser);
await adminChannelsPage.goto(team.name, 'town-square');
await adminChannelsPage.toBeVisible();
// # Have admin post the root message for the thread
await adminChannelsPage.centerView.postCreate.postMessage(ROOT_MESSAGE);
// # Have admin open the thread and post multiple replies with attachments
const rootPost = await adminChannelsPage.getLastPost();
await rootPost.hover();
await rootPost.postMenu.toBeVisible();
await rootPost.postMenu.reply();
// * Verify RHS is visible for admin
await adminChannelsPage.sidebarRight.toBeVisible();
// # Firstly let admin create a series of random replies to the root message
for (let i = 1; i <= NUMBER_OF_REPLIES; i++) {
await adminChannelsPage.sidebarRight.postCreate.postMessage(`Random replies number ${i}`.repeat(40));
}
// # Secondly let admin create a series of random replies to the root message with attachments
for (const file of ATTACHMENT_FILES) {
await adminChannelsPage.sidebarRight.postCreate.postMessage(
`Random replies number with attachment: ${file}`,
[file],
);
}
// # Admin closes the RHS
await adminChannelsPage.sidebarRight.close();
// # Log in as regular user in a separate browser session
const {channelsPage: userChannelsPage, page: userPage} = await pw.testBrowser.login(user);
await userChannelsPage.goto(team.name, 'town-square');
await userChannelsPage.toBeVisible();
// # Bring focus to the post textbox in center channel
await userChannelsPage.centerView.postCreate.input.focus();
// * Verify the post textbox in center channel is focused
await expect(userChannelsPage.centerView.postCreate.input).toBeFocused();
// # Press Shift+Up to open the latest thread in the channel in the RHS
await userPage.keyboard.press('Shift+ArrowUp');
// * Verify RHS is visible
await userChannelsPage.sidebarRight.toBeVisible();
// * Verify the correct thread (admin's root message) is shown in RHS
await userChannelsPage.sidebarRight.toContainText(ROOT_MESSAGE);
// * Verify RHS reply textbox is focused only
await expect(userChannelsPage.sidebarRight.postCreate.input).toBeFocused();
// # Type a message to verify the textbox can receive input immediately
await userPage.keyboard.type('Reply typed after Shift+Up');
// * Verify the message was typed into the RHS textbox
const inputValue = await userChannelsPage.sidebarRight.postCreate.getInputValue();
expect(inputValue).toBe('Reply typed after Shift+Up');
// # Clear the input and close RHS
await userChannelsPage.sidebarRight.postCreate.input.clear();
await userChannelsPage.sidebarRight.close();
},
);
/**
* @objective Verify Ctrl/Cmd+. opens and closes the right-hand sidebar.
*/
test(
'MM-T4692 opens and closes the right sidebar with the keyboard shortcut',
{tag: '@keyboard_shortcuts'},
async ({pw}) => {
const {user, team} = await pw.initSetup();
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'town-square');
await channelsPage.toBeVisible();
// # Post a message and open its reply thread in the right sidebar
await channelsPage.centerView.postCreate.postMessage('this post is from today');
const post = await channelsPage.getLastPost();
await post.openAThread();
await channelsPage.sidebarRight.toBeVisible();
// # Press the toggle shortcut to close the right sidebar
await page.keyboard.press('ControlOrMeta+.');
// * Verify the right sidebar is closed
await expect(channelsPage.sidebarRight.container).not.toBeVisible();
// # Press the toggle shortcut again to reopen the right sidebar
await page.keyboard.press('ControlOrMeta+.');
// * Verify the right sidebar is shown again with the reply thread
await channelsPage.sidebarRight.toBeVisible();
await expect(channelsPage.sidebarRight.container.getByText('this post is from today')).toBeVisible();
},
);

View File

@@ -0,0 +1,98 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
/**
* @objective Verify pressing Up arrow in an empty message box opens the edit box for the last post in both
* the center channel and a thread, and that Escape closes the edit box without saving.
*
* MM-T1263 covers the same behavior as MM-T1262 and is covered by this test.
*/
test(
'MM-T1262 MM-T1263 opens and cancels the edit box with Up arrow and Escape',
{tag: '@keyboard_shortcuts'},
async ({pw}) => {
const {user, team} = await pw.initSetup();
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'town-square');
await channelsPage.toBeVisible();
// # Post a message and press Up arrow from the empty center message box
const message = `edit shortcut ${pw.random.id()}`;
await channelsPage.postMessage(message);
await channelsPage.centerView.postCreate.input.focus();
await page.keyboard.press('ArrowUp');
// * Verify the edit box opens with the last message
await channelsPage.centerView.postEdit.toBeVisible();
await expect(channelsPage.centerView.postEdit.input).toHaveValue(message);
// # Close the edit box with Escape
await page.keyboard.press('Escape');
// * Verify the edit box closes and the post remains unchanged
await channelsPage.centerView.postEdit.toNotBeVisible();
await channelsPage.centerView.waitUntilLastPostContains(message);
// # Open a thread on the last post and post a reply
const post = await channelsPage.getLastPost();
await post.reply();
await channelsPage.sidebarRight.toBeVisible();
const reply = `reply shortcut ${pw.random.id()}`;
await channelsPage.sidebarRight.postMessage(reply);
await channelsPage.sidebarRight.toContainText(reply);
// # Press Up arrow from the empty thread reply box
await channelsPage.sidebarRight.postCreate.input.focus();
await page.keyboard.press('ArrowUp');
// * Verify the thread edit box opens with the reply, then closes with Escape
await channelsPage.sidebarRight.postEdit.toBeVisible();
await expect(channelsPage.sidebarRight.postEdit.input).toHaveValue(reply);
await page.keyboard.press('Escape');
await channelsPage.sidebarRight.postEdit.toNotBeVisible();
// * Verify the reply is still present after cancelling the edit
await channelsPage.sidebarRight.toContainText(reply);
},
);
/**
* @objective Verify Shift+Enter adds new lines in the message box to compose a fenced code block, which is
* then posted as a code block.
*
* MM-T1268 covers the same behavior as MM-T1267 and is covered by this test.
*/
test(
'MM-T1267 MM-T1268 composes and posts a code block using Shift+Enter for new lines',
{tag: '@keyboard_shortcuts'},
async ({pw}) => {
const {user, team} = await pw.initSetup();
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'town-square');
await channelsPage.toBeVisible();
// # Compose a fenced code block using Shift+Enter for new lines
const code = `code ${pw.random.id()}`;
const input = channelsPage.centerView.postCreate.input;
await input.focus();
await input.pressSequentially('```');
await page.keyboard.press('Shift+Enter');
await input.pressSequentially(code);
await page.keyboard.press('Shift+Enter');
await input.pressSequentially('```');
// * Verify Shift+Enter produced the multi-line fenced code block in the message box
await expect(input).toHaveValue(`\`\`\`\n${code}\n\`\`\``);
// # Post the code block (Enter inserts a newline inside a fenced block, so use the send button)
await channelsPage.centerView.postCreate.sendMessage();
// * Verify the code block is posted with the code content and the fence markers consumed (rendered as a code block, not literal backticks)
await channelsPage.centerView.waitUntilLastPostContains(code);
const post = await channelsPage.getLastPost();
await post.toContainText(code);
await post.toNotContainText('```');
},
);

View File

@@ -0,0 +1,84 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
/**
* @objective Verify the keyboard shortcuts for moving to the previous and next channel in the sidebar
* switch between channels.
*/
test(
'MM-T1259 moves to the previous and next channel with the keyboard',
{tag: '@keyboard_shortcuts'},
async ({pw}) => {
const {user, team} = await pw.initSetup();
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'off-topic');
await channelsPage.toBeVisible();
await channelsPage.centerView.header.toHaveTitle('Off-Topic');
// # Move to the next channel with the keyboard
await channelsPage.centerView.postCreate.input.focus();
await page.keyboard.press('Alt+ArrowDown');
// * Verify the next channel in the sidebar is shown
await channelsPage.centerView.header.toHaveTitle('Town Square');
// # Move to the previous channel with the keyboard
await page.keyboard.press('Alt+ArrowUp');
// * Verify the original channel is shown again
await channelsPage.centerView.header.toHaveTitle('Off-Topic');
},
);
/**
* @objective Verify Ctrl/Cmd+Shift+K opens the Direct Messages modal, and Escape closes it.
*/
test(
'MM-T1276 opens the Direct Messages modal with the keyboard shortcut',
{tag: '@keyboard_shortcuts'},
async ({pw}) => {
const {user, team} = await pw.initSetup();
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'town-square');
await channelsPage.toBeVisible();
// # Focus the message box and press the Direct Messages shortcut
await channelsPage.centerView.postCreate.input.focus();
await page.keyboard.press('ControlOrMeta+Shift+K');
// * Verify the Direct Messages modal opens
await channelsPage.directChannelsModal.toBeVisible();
// # Close the modal with Escape
await page.keyboard.press('Escape');
// * Verify the Direct Messages modal closes
await expect(channelsPage.directChannelsModal.container).not.toBeVisible();
},
);
/**
* @objective Verify Ctrl/Cmd+Shift+F opens the search box prefilled with the current channel filter.
*
* MM-T4872 covers the same behavior as MM-T1435 and is covered by this test.
*/
test(
'MM-T1435 MM-T4872 prefills the search box with the channel filter using the keyboard shortcut',
{tag: '@keyboard_shortcuts'},
async ({pw}) => {
const {user, team} = await pw.initSetup();
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'off-topic');
await channelsPage.toBeVisible();
// # Focus the message box and press the in-channel search shortcut
await channelsPage.centerView.postCreate.input.focus();
await page.keyboard.press('ControlOrMeta+Shift+F');
// * Verify the search box opens prefilled with the current channel filter
await channelsPage.searchBox.toBeVisible();
await expect(channelsPage.searchBox.searchInput).toHaveValue('in:off-topic ');
},
);

View File

@@ -0,0 +1,66 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
/**
* @objective Verify the keyboard shortcuts modal opens from Ctrl/Cmd+/ and /shortcuts, displays the
* platform-specific upload shortcut, and can be closed by the shortcut, its close button, or Escape.
*/
test('MM-T1239 CTRL/CMD+/ and /shortcuts open keyboard shortcuts', async ({pw}) => {
const {user, team} = await pw.initSetup();
// # Open the keyboard shortcuts modal with the shortcut
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'town-square');
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.input.focus();
await page.keyboard.press('ControlOrMeta+/');
// * Verify the shortcuts modal opens and shows the platform-specific "Upload files" shortcut
const modal = channelsPage.keyboardShortcutsModal;
await expect(modal).toBeVisible();
const filesSection = modal.locator('.subsection').filter({hasText: 'Files'});
await expect(filesSection).toBeVisible();
await expect(filesSection.getByText(process.platform === 'darwin' ? '⌘' : 'Ctrl')).toBeVisible();
await expect(filesSection.getByText('U', {exact: true})).toBeVisible();
// # Close the modal by pressing the same shortcut again
await page.keyboard.press('ControlOrMeta+/');
await expect(modal).not.toBeVisible();
// # Reopen via the slash command and close using the modal's close button
await channelsPage.postMessage('/shortcuts');
await expect(modal).toBeVisible();
await modal.getByRole('button', {name: 'Close'}).click();
await expect(modal).not.toBeVisible();
// # Reopen via the slash command and close by pressing Escape
await channelsPage.postMessage('/shortcuts');
await expect(modal).toBeVisible();
await page.keyboard.press('Escape');
await expect(modal).not.toBeVisible();
});
/**
* @objective Verify Ctrl/Cmd+K channel switch keeps focus so typed characters are not lost.
*/
test('MM-T1242 CTRL/CMD+K typed characters are not lost after switching channels', async ({pw}) => {
const {user, team} = await pw.initSetup();
const message = 'Hello World!';
// # Open quick switcher, select the current channel, and type into the focused page
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'town-square');
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.input.focus();
await page.keyboard.press('ControlOrMeta+K');
await expect(channelsPage.findChannelsModal.input).toBeVisible();
await channelsPage.findChannelsModal.input.fill('off');
await channelsPage.findChannelsModal.selectChannel('off-topic');
await channelsPage.centerView.header.toHaveTitle('Off-Topic');
await page.keyboard.type(message);
// * Verify typed characters land in the post textbox
await expect(channelsPage.centerView.postCreate.input).toHaveValue(message);
});

View File

@@ -0,0 +1,65 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
/**
* @objective Verify Ctrl/Cmd+Up and Ctrl/Cmd+Down cycle through previous messages in the post textbox.
*
* MM-T1256, MM-T1257, and MM-T1258 cover the same behavior as MM-T1254 and are covered by this test.
*/
test('MM-T1254 MM-T1256 MM-T1257 MM-T1258 CTRL/CMD+UP and CTRL/CMD+DOWN cycle previous messages', async ({pw}) => {
const {user, team} = await pw.initSetup();
const messages = ['post 1', 'post 2', 'post 3', 'post 4', 'post 5'];
// # Post several messages and focus the textbox
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'town-square');
await channelsPage.toBeVisible();
for (const message of messages) {
await channelsPage.postMessage(message);
}
await channelsPage.centerView.postCreate.input.focus();
// * Verify Ctrl/Cmd+Up cycles backward through message history
for (const message of [...messages].reverse()) {
await page.keyboard.press('ControlOrMeta+ArrowUp');
await expect(channelsPage.centerView.postCreate.input).toHaveValue(message);
}
// * Verify one extra Ctrl/Cmd+Up past the oldest message does not change the displayed message
await page.keyboard.press('ControlOrMeta+ArrowUp');
await expect(channelsPage.centerView.postCreate.input).toHaveValue(messages[0]);
// * Verify Ctrl/Cmd+Down cycles forward through message history
for (const message of messages.slice(1)) {
await page.keyboard.press('ControlOrMeta+ArrowDown');
await expect(channelsPage.centerView.postCreate.input).toHaveValue(message);
}
});
/**
* @objective Verify Up arrow opens inline edit for the previous message and saving marks the post as edited.
*/
test('MM-T1260 UP arrow edits the previous post', async ({pw}) => {
const {user, team} = await pw.initSetup();
// # Post a message and press Up from the center textbox
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'town-square');
await channelsPage.toBeVisible();
await channelsPage.postMessage('Test');
const postId = await channelsPage.centerView.getLastPostID();
await channelsPage.centerView.postCreate.input.focus();
await page.keyboard.press('ArrowUp');
// # Edit and save the previous message
await channelsPage.centerView.postEdit.toBeVisible();
await channelsPage.centerView.postEdit.writeMessage('Edit Test');
await channelsPage.centerView.postEdit.sendMessage();
// * Verify the post was edited and has the edited marker
const editedPost = await channelsPage.getLastPost();
await editedPost.toContainText('Edit Test');
await expect(channelsPage.centerView.editedPostIcon(postId)).toContainText('Edited');
});

View File

@@ -0,0 +1,78 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
/**
* @objective Verify Ctrl/Cmd+B and Ctrl/Cmd+I wrap the message text in bold and italic markdown and
* that the posted message renders the formatting.
*/
test('MM-T3405 formats text as bold and italic with keyboard shortcuts', {tag: '@keyboard_shortcuts'}, async ({pw}) => {
const {user, team} = await pw.initSetup();
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'town-square');
await channelsPage.toBeVisible();
const postCreate = channelsPage.centerView.postCreate;
// # Type a message, select it, and apply the bold shortcut
await postCreate.input.click();
await postCreate.input.fill('bolded text');
await page.keyboard.press('ControlOrMeta+A');
await page.keyboard.press('ControlOrMeta+B');
// * Verify the text is wrapped in bold markdown
await expect(postCreate.input).toHaveValue('**bolded text**');
// # Post the bold message
await postCreate.sendMessage();
// * Verify the posted message renders as bold without literal markdown characters
const boldPost = await channelsPage.getLastPost();
await expect(boldPost.container.locator('strong')).toHaveText('bolded text');
await boldPost.toNotContainText('**');
// # Type another message, select it, and apply the italic shortcut
await postCreate.input.click();
await postCreate.input.fill('italic text');
await page.keyboard.press('ControlOrMeta+A');
await page.keyboard.press('ControlOrMeta+I');
// * Verify the text is wrapped in italic markdown
await expect(postCreate.input).toHaveValue('*italic text*');
// # Post the italic message
await postCreate.sendMessage();
// * Verify the posted message renders as italic without literal markdown characters
const italicPost = await channelsPage.getLastPost();
await expect(italicPost.container.locator('em')).toHaveText('italic text');
await italicPost.toNotContainText('*');
});
/**
* @objective Verify the Ctrl/Cmd+Shift+\ shortcut opens the emoji picker to react to the last message,
* and that it can be dismissed.
*/
test('MM-T4693 opens the emoji picker with the keyboard shortcut', {tag: '@keyboard_shortcuts'}, async ({pw}) => {
const {user, team} = await pw.initSetup();
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto(team.name, 'town-square');
await channelsPage.toBeVisible();
// # Post a message so there is a last message to react to
await channelsPage.centerView.postCreate.postMessage('this is a test');
await channelsPage.centerView.postCreate.input.focus();
// # Press the emoji picker shortcut
await page.keyboard.press('ControlOrMeta+Shift+\\');
// * Verify the emoji picker opens
await expect(channelsPage.reactionEmojiPicker.container).toBeVisible();
// # Dismiss the emoji picker
await page.keyboard.press('Escape');
// * Verify the emoji picker is closed
await expect(channelsPage.reactionEmojiPicker.container).not.toBeVisible();
});