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
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:
@@ -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();
|
||||
});
|
||||
@@ -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();
|
||||
},
|
||||
);
|
||||
@@ -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('```');
|
||||
},
|
||||
);
|
||||
@@ -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 ');
|
||||
},
|
||||
);
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
Reference in New Issue
Block a user