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 Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (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 Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
59
e2e-tests/cypress/tests/support/ui/account_settings_modal.d.ts
vendored
Normal file
59
e2e-tests/cypress/tests/support/ui/account_settings_modal.d.ts
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiOpenProfileModal`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Open the profile settings modal
|
||||
* @param {string} section - such as `'General'`, `'Security'`, `'Notifications'`, `'Display'`, `'Sidebar'` and `'Advanced'`
|
||||
* @return the "#accountSettingsModal"
|
||||
*
|
||||
* @example
|
||||
* cy.uiOpenProfileModal('Profile Settings').within(() => {
|
||||
* // Do something here
|
||||
* });
|
||||
*/
|
||||
uiOpenProfileModal(section: string): Chainable<JQuery<HTMLElement>>;
|
||||
|
||||
/**
|
||||
* Close the profile settings modal given that the modal itself is opened.
|
||||
*
|
||||
* @example
|
||||
* cy.uiCloseAccountSettingsModal();
|
||||
*/
|
||||
uiCloseAccountSettingsModal(): Chainable;
|
||||
|
||||
/**
|
||||
* Navigate to profile settings and verify the user's first, last name
|
||||
* @param {String} firstname - expected user firstname
|
||||
* @param {String} lastname - expected user lastname
|
||||
*/
|
||||
verifyAccountNameSettings(firstname: string, lastname: string): Chainable;
|
||||
|
||||
/**
|
||||
* Navigate to account display settings and change collapsed reply threads setting
|
||||
* @param {String} setting - ON or OFF
|
||||
*/
|
||||
uiChangeCRTDisplaySetting(setting: string): Chainable;
|
||||
|
||||
/**
|
||||
* Navigate to account display settings and change message display setting
|
||||
* @param {String} setting - COMPACT or STANDARD
|
||||
*/
|
||||
uiChangeMessageDisplaySetting(setting: string): Chainable;
|
||||
}
|
||||
}
|
||||
60
e2e-tests/cypress/tests/support/ui/account_settings_modal.js
Normal file
60
e2e-tests/cypress/tests/support/ui/account_settings_modal.js
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
Cypress.Commands.add('uiOpenProfileModal', (section = '') => {
|
||||
// # Open profile settings modal
|
||||
cy.uiOpenUserMenu('Profile');
|
||||
|
||||
const profileSettingsModal = () => cy.findByRole('dialog', {name: 'Profile'}).should('be.visible');
|
||||
|
||||
if (!section) {
|
||||
return profileSettingsModal();
|
||||
}
|
||||
|
||||
// # Click on a particular section
|
||||
cy.findByRoleExtended('tab', {name: section}).should('be.visible').click();
|
||||
|
||||
return profileSettingsModal();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('verifyAccountNameSettings', (firstname, lastname) => {
|
||||
// # Go to Profile
|
||||
cy.uiOpenProfileModal('Profile Settings');
|
||||
|
||||
// * Check name value
|
||||
cy.get('#nameDesc').should('have.text', `${firstname} ${lastname}`);
|
||||
cy.uiClose();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiChangeGenericDisplaySetting', (setting, option) => {
|
||||
cy.uiOpenSettingsModal('Display');
|
||||
cy.get(setting).scrollIntoView();
|
||||
cy.get(setting).click();
|
||||
cy.get('.section-max').scrollIntoView();
|
||||
|
||||
cy.get(option).check().should('be.checked');
|
||||
|
||||
cy.uiSaveAndClose();
|
||||
});
|
||||
|
||||
/*
|
||||
* Change the message display setting
|
||||
* @param {String} setting - as 'STANDARD' or 'COMPACT'
|
||||
*/
|
||||
Cypress.Commands.add('uiChangeMessageDisplaySetting', (setting = 'STANDARD') => {
|
||||
const SETTINGS = {STANDARD: '#message_displayFormatA', COMPACT: '#message_displayFormatB'};
|
||||
cy.uiChangeGenericDisplaySetting('#message_displayTitle', SETTINGS[setting]);
|
||||
});
|
||||
|
||||
/*
|
||||
* Change the collapsed reply threads display setting
|
||||
* @param {String} setting - as 'OFF' or 'ON'
|
||||
*/
|
||||
Cypress.Commands.add('uiChangeCRTDisplaySetting', (setting = 'OFF') => {
|
||||
const SETTINGS = {
|
||||
ON: '#collapsed_reply_threadsFormatA',
|
||||
OFF: '#collapsed_reply_threadsFormatB',
|
||||
};
|
||||
|
||||
cy.uiChangeGenericDisplaySetting('#collapsed_reply_threadsTitle', SETTINGS[setting]);
|
||||
});
|
||||
28
e2e-tests/cypress/tests/support/ui/announcement_bar.d.ts
vendored
Normal file
28
e2e-tests/cypress/tests/support/ui/announcement_bar.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiCloseAnnouncementBar`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Close the announcement bar if shown in the UI
|
||||
*
|
||||
* @example
|
||||
* cy.uiCloseAnnouncementBar();
|
||||
*/
|
||||
uiCloseAnnouncementBar(): Chainable;
|
||||
}
|
||||
}
|
||||
11
e2e-tests/cypress/tests/support/ui/announcement_bar.js
Normal file
11
e2e-tests/cypress/tests/support/ui/announcement_bar.js
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
Cypress.Commands.add('uiCloseAnnouncementBar', () => {
|
||||
cy.document().then((doc) => {
|
||||
const announcementBar = doc.getElementsByClassName('announcement-bar')[0];
|
||||
if (announcementBar) {
|
||||
cy.get('.announcement-bar__close').click();
|
||||
}
|
||||
});
|
||||
});
|
||||
56
e2e-tests/cypress/tests/support/ui/boards.d.ts
vendored
Normal file
56
e2e-tests/cypress/tests/support/ui/boards.d.ts
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiCreateEmptyBoard`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Create a board on a given menu item.
|
||||
*
|
||||
* @param {string} item - one of the template menu options, ex. 'Empty board'
|
||||
*/
|
||||
uiCreateBoard(item: string): Chainable;
|
||||
|
||||
/**
|
||||
* Create an empty board.
|
||||
* @example
|
||||
* cy.uiCreateEmptyBoard();
|
||||
*/
|
||||
uiCreateEmptyBoard(): Chainable;
|
||||
|
||||
/**
|
||||
* Create a board with the given title
|
||||
*
|
||||
* @param {string} title - title of the new board
|
||||
*/
|
||||
uiCreateNewBoard: (title?: string) => Chainable;
|
||||
|
||||
/**
|
||||
* Create a new group with the given name
|
||||
*
|
||||
* @param {string} name - name of the new group
|
||||
*/
|
||||
uiAddNewGroup: (name?: string) => Chainable;
|
||||
|
||||
/**
|
||||
* Create a card with the given title
|
||||
*
|
||||
* @param {string} title - title of the new card
|
||||
* @param {string} columnIndex - the column index to create the card
|
||||
*/
|
||||
uiAddNewCard: (title?: string, columnIndex?: number) => Chainable;
|
||||
}
|
||||
}
|
||||
65
e2e-tests/cypress/tests/support/ui/boards.js
Normal file
65
e2e-tests/cypress/tests/support/ui/boards.js
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import timeouts from '../../fixtures/timeouts';
|
||||
|
||||
Cypress.Commands.add('uiCreateBoard', (item) => {
|
||||
cy.log(`Create new board: ${item}`);
|
||||
|
||||
cy.uiAddBoard('Create new board');
|
||||
cy.contains(item).click();
|
||||
cy.contains('Use this template').click({force: true}).wait(timeouts.ONE_SEC);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiCreateEmptyBoard', () => {
|
||||
cy.log('Create new empty board');
|
||||
|
||||
cy.contains('Create an empty board').click({force: true}).wait(timeouts.ONE_SEC);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiAddBoard', (item) => {
|
||||
cy.get('.add-board-icon').should('be.visible').click();
|
||||
cy.get('.menu-contents').should('be.visible');
|
||||
|
||||
if (item) {
|
||||
cy.findByRole('button', {name: item}).click();
|
||||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiCreateNewBoard', (title) => {
|
||||
cy.log('**Create new empty board**');
|
||||
cy.uiCreateEmptyBoard();
|
||||
|
||||
cy.findByPlaceholderText('Untitled board').should('be.visible');
|
||||
cy.wait(timeouts.QUARTER_SEC);
|
||||
if (title) {
|
||||
cy.log('**Rename board**');
|
||||
cy.findByPlaceholderText('Untitled board').type(`${title}{enter}`);
|
||||
cy.findByRole('textbox', {name: title}).should('exist');
|
||||
}
|
||||
cy.wait(timeouts.HALF_SEC);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiAddNewGroup', (name) => {
|
||||
cy.log('**Add a new group**');
|
||||
cy.findByRole('button', {name: '+ Add a group'}).click();
|
||||
cy.findByRole('textbox', {name: 'New group'}).should('exist');
|
||||
|
||||
if (name) {
|
||||
cy.log('**Rename group**');
|
||||
cy.findByRole('textbox', {name: 'New group'}).type(`{selectall}${name}{enter}`);
|
||||
cy.findByRole('textbox', {name}).should('exist');
|
||||
}
|
||||
cy.wait(timeouts.HALF_SEC);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiAddNewCard', (title, columnIndex) => {
|
||||
cy.log('**Add a new card**');
|
||||
cy.findByRole('button', {name: '+ New'}).eq(columnIndex || 0).click();
|
||||
cy.findByRole('dialog').should('exist');
|
||||
|
||||
if (title) {
|
||||
cy.log('**Change card title**');
|
||||
cy.findByPlaceholderText('Untitled').type(title);
|
||||
}
|
||||
});
|
||||
77
e2e-tests/cypress/tests/support/ui/channel.d.ts
vendored
Normal file
77
e2e-tests/cypress/tests/support/ui/channel.d.ts
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiCreateChannel`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Create a new channel in the current team.
|
||||
* @param {string} options.prefix - Prefix for the name of the channel, it will be added a random string ot it.
|
||||
* @param {boolean} options.isPrivate - is the channel private or public (default)?
|
||||
* @param {string} options.purpose - Channel's purpose
|
||||
* @param {string} options.header - Channel's header
|
||||
* @param {boolean} options.isNewSidebar) - the new sidebar has a different ui flow, set this setting to true to use that. Defaults to false.
|
||||
* @param {string} options.createBoard) - Board template to create
|
||||
*
|
||||
* @example
|
||||
* cy.uiCreateChannel({prefix: 'private-channel-', isPrivate: true, purpose: 'my private channel', header: 'my private header', isNewSidebar: false});
|
||||
*/
|
||||
uiCreateChannel(options: Record<string, unknown>): Chainable;
|
||||
|
||||
/**
|
||||
* Add users to the current channel.
|
||||
* @param {string[]} usernameList - list of userids to add to the channel
|
||||
*
|
||||
* @example
|
||||
* cy.uiAddUsersToCurrentChannel(['user1', 'user2']);
|
||||
*/
|
||||
uiAddUsersToCurrentChannel(usernameList: string[]);
|
||||
|
||||
/**
|
||||
* Invite users to the current channel.
|
||||
* @param {string[]} usernameList - list of userids to be invited to the channel
|
||||
*
|
||||
* @example
|
||||
* cy.uiInviteUsersToCurrentChannel(['user1', 'user2']);
|
||||
*/
|
||||
uiInviteUsersToCurrentChannel(usernameList: string[]);
|
||||
|
||||
/**
|
||||
* Archive the current channel.
|
||||
*
|
||||
* @example
|
||||
* cy.uiArchiveChannel();
|
||||
*/
|
||||
uiArchiveChannel();
|
||||
|
||||
/**
|
||||
* Unarchive the current channel.
|
||||
*
|
||||
* @example
|
||||
* cy.uiUnarchiveChannel();
|
||||
*/
|
||||
uiUnarchiveChannel();
|
||||
|
||||
/**
|
||||
* Leave the current channel.
|
||||
* @param {boolean} isPrivate - is the channel private or public (default)?
|
||||
*
|
||||
* @example
|
||||
* cy.uiLeaveChannel(true);
|
||||
*/
|
||||
uiLeaveChannel(isPrivate?: boolean);
|
||||
}
|
||||
}
|
||||
108
e2e-tests/cypress/tests/support/ui/channel.js
Normal file
108
e2e-tests/cypress/tests/support/ui/channel.js
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {getRandomId} from '../../utils';
|
||||
import * as TIMEOUTS from '../../fixtures/timeouts';
|
||||
|
||||
Cypress.Commands.add('uiCreateChannel', ({
|
||||
prefix = 'channel-',
|
||||
isPrivate = false,
|
||||
purpose = '',
|
||||
name = '',
|
||||
createBoard = '',
|
||||
}) => {
|
||||
cy.uiBrowseOrCreateChannel('Create new channel');
|
||||
|
||||
cy.get('#new-channel-modal').should('be.visible');
|
||||
if (isPrivate) {
|
||||
cy.get('#public-private-selector-button-P').click().wait(TIMEOUTS.HALF_SEC);
|
||||
} else {
|
||||
cy.get('#public-private-selector-button-O').click().wait(TIMEOUTS.HALF_SEC);
|
||||
}
|
||||
const channelName = name || `${prefix}${getRandomId()}`;
|
||||
cy.get('#input_new-channel-modal-name').should('be.visible').clear().type(channelName);
|
||||
if (purpose) {
|
||||
cy.get('#new-channel-modal-purpose').clear().type(purpose);
|
||||
}
|
||||
|
||||
if (createBoard) {
|
||||
cy.get('#add-board-to-channel').should('be.visible');
|
||||
cy.findByTestId('add-board-to-channel-check').then((el) => {
|
||||
if (el && !el.hasClass('checked')) {
|
||||
el.click();
|
||||
cy.get('.templates-selector').find('input').click({force: true});
|
||||
cy.findByText(createBoard).scrollIntoView().should('be.visible').click({force: true});
|
||||
}
|
||||
});
|
||||
}
|
||||
cy.findByText('Create channel').click();
|
||||
cy.get('#new-channel-modal').should('not.exist');
|
||||
cy.get('#channelIntro').should('be.visible');
|
||||
return cy.wrap({name: channelName});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiAddUsersToCurrentChannel', (usernameList) => {
|
||||
if (usernameList.length) {
|
||||
cy.get('#channelHeaderTitle').click();
|
||||
cy.get('#channelMembers').click();
|
||||
cy.uiGetButton('Add').click();
|
||||
cy.get('#addUsersToChannelModal').should('be.visible');
|
||||
usernameList.forEach((username) => {
|
||||
cy.get('#selectItems input').typeWithForce(`@${username}{enter}`);
|
||||
});
|
||||
cy.get('#saveItems').click();
|
||||
cy.get('#addUsersToChannelModal').should('not.exist');
|
||||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiInviteUsersToCurrentChannel', (usernameList) => {
|
||||
if (usernameList.length) {
|
||||
cy.get('#channelHeaderTitle').click();
|
||||
cy.get('#channelMembers').click();
|
||||
cy.uiGetButton('Add').click();
|
||||
cy.get('#addUsersToChannelModal').should('be.visible');
|
||||
usernameList.forEach((username) => {
|
||||
cy.get('#selectItems input').typeWithForce(`@${username}{enter}`);
|
||||
});
|
||||
cy.get('#saveItems').click();
|
||||
cy.get('#addUsersToChannelModal').should('not.exist');
|
||||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiArchiveChannel', () => {
|
||||
cy.get('#channelHeaderTitle').click();
|
||||
cy.get('#channelArchiveChannel').click();
|
||||
return cy.get('#deleteChannelModalDeleteButton').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiUnarchiveChannel', () => {
|
||||
cy.get('#channelHeaderTitle').should('be.visible').click();
|
||||
cy.get('#channelUnarchiveChannel').should('be.visible').click();
|
||||
return cy.get('#unarchiveChannelModalDeleteButton').should('be.visible').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiLeaveChannel', (isPrivate = false) => {
|
||||
cy.get('#channelHeaderTitle').click();
|
||||
|
||||
if (isPrivate) {
|
||||
cy.get('#channelLeaveChannel').click();
|
||||
return cy.get('#confirmModalButton').click();
|
||||
}
|
||||
|
||||
return cy.get('#channelLeaveChannel').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('goToDm', (username) => {
|
||||
cy.uiAddDirectMessage().click({force: true});
|
||||
|
||||
// # Start typing part of a username that matches previously created users
|
||||
cy.get('#selectItems input').typeWithForce(username);
|
||||
cy.findByRole('dialog', {name: 'Direct Messages'}).should('be.visible').wait(TIMEOUTS.ONE_SEC);
|
||||
cy.findByRole('combobox', {name: 'Search for people'}).
|
||||
typeWithForce(username).
|
||||
wait(TIMEOUTS.ONE_SEC).
|
||||
typeWithForce('{enter}');
|
||||
|
||||
// # Save the selected item
|
||||
return cy.get('#saveItems').click().wait(TIMEOUTS.HALF_SEC);
|
||||
});
|
||||
86
e2e-tests/cypress/tests/support/ui/channel_header.d.ts
vendored
Normal file
86
e2e-tests/cypress/tests/support/ui/channel_header.d.ts
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiGetChannelFavoriteButton`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get channel header button.
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetChannelHeaderButton().click();
|
||||
*/
|
||||
uiGetChannelHeaderButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get favorite button from channel header.
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetChannelFavoriteButton().click();
|
||||
*/
|
||||
uiGetChannelFavoriteButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get mute button from channel header.
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetMuteButton().click();
|
||||
*/
|
||||
uiGetMuteButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get member button from channel header.
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetChannelMemberButton().click();
|
||||
*/
|
||||
uiGetChannelMemberButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get pin button from channel header.
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetChannelPinButton().click();
|
||||
*/
|
||||
uiGetChannelPinButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get files button from channel header.
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetChannelFileButton().click();
|
||||
*/
|
||||
uiGetChannelFileButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get channel menu
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetChannelMenu();
|
||||
*/
|
||||
uiGetChannelMenu(): Chainable;
|
||||
|
||||
/**
|
||||
* Open channel menu
|
||||
* @param {string} [menu] - such as `'View Info'`, `'Notification Preferences'`, `'Team Settings'` and other items in the main menu.
|
||||
* @return the channel menu
|
||||
*
|
||||
* @example
|
||||
* cy.uiOpenChannelMenu();
|
||||
*/
|
||||
uiOpenChannelMenu(menu?: string): Chainable;
|
||||
}
|
||||
}
|
||||
56
e2e-tests/cypress/tests/support/ui/channel_header.js
Normal file
56
e2e-tests/cypress/tests/support/ui/channel_header.js
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// Buttons
|
||||
|
||||
Cypress.Commands.add('uiGetChannelHeaderButton', () => {
|
||||
return cy.get('#channelHeaderDropdownButton').should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetChannelFavoriteButton', () => {
|
||||
return cy.get('#toggleFavorite').should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetMuteButton', () => {
|
||||
return cy.get('#toggleMute').should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetChannelMemberButton', () => {
|
||||
return cy.get('#member_rhs').should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetChannelPinButton', () => {
|
||||
return cy.get('#channelHeaderPinButton').should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetChannelFileButton', () => {
|
||||
return cy.get('#channelHeaderFilesButton').should('be.visible');
|
||||
});
|
||||
|
||||
// Menus
|
||||
|
||||
Cypress.Commands.add('uiGetChannelMenu', (options = {exist: true}) => {
|
||||
if (options.exist) {
|
||||
return cy.get('#channelHeaderDropdownMenu').
|
||||
should('be.visible');
|
||||
}
|
||||
|
||||
return cy.get('#channelHeaderDropdownMenu').should('not.exist');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiOpenChannelMenu', (item = '') => {
|
||||
// # Click on channel header button
|
||||
cy.uiGetChannelHeaderButton().click();
|
||||
|
||||
if (!item) {
|
||||
// # Return the menu if no item is passed
|
||||
return cy.uiGetChannelMenu();
|
||||
}
|
||||
|
||||
// # Click on a particular item
|
||||
return cy.uiGetChannelMenu().
|
||||
findByText(item).
|
||||
scrollIntoView().
|
||||
should('be.visible').
|
||||
click();
|
||||
});
|
||||
84
e2e-tests/cypress/tests/support/ui/channel_sidebar.ts
Normal file
84
e2e-tests/cypress/tests/support/ui/channel_sidebar.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {getRandomId} from '../../utils';
|
||||
|
||||
import type {ChainableT} from '@/types';
|
||||
|
||||
/**
|
||||
* Create a new category
|
||||
*
|
||||
* @param [categoryName] category's name
|
||||
*
|
||||
* @example
|
||||
* cy.uiCreateSidebarCategory();
|
||||
*/
|
||||
function uiCreateSidebarCategory(categoryName: string = `category-${getRandomId()}`): ChainableT<any> {
|
||||
// # Click on the sidebar menu dropdown and select Create Category
|
||||
cy.uiBrowseOrCreateChannel('Create new category');
|
||||
|
||||
cy.findByRole('dialog', {name: 'Create New Category'}).should('be.visible').within(() => {
|
||||
// # Fill in the category name and click 'Create'
|
||||
cy.findByRole('textbox').should('be.visible').typeWithForce(categoryName).
|
||||
invoke('val').should('equal', categoryName);
|
||||
cy.findByRole('button', {name: 'Create'}).should('be.enabled').click();
|
||||
});
|
||||
|
||||
// * Wait for the category to appear in the sidebar
|
||||
cy.contains('.SidebarChannelGroup', categoryName, {matchCase: false});
|
||||
|
||||
return cy.wrap({displayName: categoryName});
|
||||
}
|
||||
|
||||
Cypress.Commands.add('uiCreateSidebarCategory', uiCreateSidebarCategory);
|
||||
|
||||
/**
|
||||
* Move a channel to a category.
|
||||
* Open the channel menu, select Move to, and click either New Category or on the category.
|
||||
*
|
||||
* @param channelName channel's name
|
||||
* @param categoryName category's name
|
||||
* @param [newCategory=false] create a new category to move into
|
||||
* @param [isChannelId=false] whether channelName is a channel ID
|
||||
*
|
||||
* @example
|
||||
* cy.uiMoveChannelToCategory('Town Square', 'Favorites');
|
||||
*/
|
||||
function uiMoveChannelToCategory(channelName: string, categoryName: string, newCategory = false, isChannelId = false): ChainableT<any> {
|
||||
// # Open the channel menu, select Move to
|
||||
cy.uiGetChannelSidebarMenu(channelName, isChannelId).within(() => {
|
||||
cy.findByText('Move to...').should('be.visible').trigger('mouseover');
|
||||
});
|
||||
|
||||
// # Select the move to category
|
||||
cy.findAllByRole('menu', {name: 'Move to submenu'}).should('be.visible').within(() => {
|
||||
if (newCategory) {
|
||||
cy.findByText('New Category').should('be.visible').click({force: true});
|
||||
} else {
|
||||
cy.findByText(categoryName).should('be.visible').click({force: true});
|
||||
}
|
||||
});
|
||||
|
||||
if (newCategory) {
|
||||
cy.findByRole('dialog', {name: 'Create New Category'}).should('be.visible').within(() => {
|
||||
// # Fill in the category name and click 'Create'
|
||||
cy.findByRole('textbox').should('be.visible').typeWithForce(categoryName).
|
||||
invoke('val').should('equal', categoryName);
|
||||
cy.findByRole('button', {name: 'Create'}).should('be.enabled').click();
|
||||
});
|
||||
}
|
||||
|
||||
return cy.wrap({displayName: categoryName});
|
||||
}
|
||||
|
||||
Cypress.Commands.add('uiMoveChannelToCategory', uiMoveChannelToCategory);
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
uiCreateSidebarCategory: typeof uiCreateSidebarCategory;
|
||||
uiMoveChannelToCategory: typeof uiMoveChannelToCategory;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
e2e-tests/cypress/tests/support/ui/cloud_billing.d.ts
vendored
Normal file
27
e2e-tests/cypress/tests/support/ui/cloud_billing.d.ts
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Iframe element in Stripe
|
||||
*
|
||||
* @example
|
||||
* cy.getIframeBody();
|
||||
*/
|
||||
uiGetPaymentCardInput(): Chainable;
|
||||
}
|
||||
}
|
||||
9
e2e-tests/cypress/tests/support/ui/cloud_billing.js
Normal file
9
e2e-tests/cypress/tests/support/ui/cloud_billing.js
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
Cypress.Commands.add('uiGetPaymentCardInput', () => {
|
||||
return cy.
|
||||
get('.__PrivateStripeElement > iframe').
|
||||
its('0.contentDocument.body').should('not.be.empty').
|
||||
then(cy.wrap);
|
||||
});
|
||||
114
e2e-tests/cypress/tests/support/ui/common.d.ts
vendored
Normal file
114
e2e-tests/cypress/tests/support/ui/common.d.ts
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiSave`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Click 'Save' button
|
||||
*
|
||||
* @example
|
||||
* cy.uiSave();
|
||||
*/
|
||||
uiSave(): Chainable;
|
||||
|
||||
/**
|
||||
* Click 'Cancel' button
|
||||
*
|
||||
* @example
|
||||
* cy.uiCancel();
|
||||
*/
|
||||
uiCancel(): Chainable;
|
||||
|
||||
/**
|
||||
* Click 'Close' button
|
||||
*
|
||||
* @example
|
||||
* cy.uiClose();
|
||||
*/
|
||||
uiClose(): Chainable;
|
||||
|
||||
/**
|
||||
* Click Save then Close buttons
|
||||
*
|
||||
* @example
|
||||
* cy.uiSaveAndClose();
|
||||
*/
|
||||
uiSaveAndClose(): Chainable;
|
||||
|
||||
/**
|
||||
* Get a button by its text using "cy.findByRole"
|
||||
*
|
||||
* @param {String} label - Button text
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetButton('Save');
|
||||
*/
|
||||
uiGetButton(label: string): Chainable;
|
||||
|
||||
/**
|
||||
* Get save button
|
||||
*
|
||||
* @example
|
||||
* cy.uiSaveButton();
|
||||
*/
|
||||
uiSaveButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get cancel button
|
||||
*
|
||||
* @example
|
||||
* cy.uiCancelButton();
|
||||
*/
|
||||
uiCancelButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get close button
|
||||
*
|
||||
* @example
|
||||
* cy.uiCloseButton();
|
||||
*/
|
||||
uiCloseButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get a radio button by its text using "cy.findByRole"
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetRadioButton('Custom Theme');
|
||||
*/
|
||||
uiGetRadioButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get a heading by its text using "cy.findByRole"
|
||||
*
|
||||
* @param {string} headingText - Heading text
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetHeading('General Settings');
|
||||
*/
|
||||
uiGetHeading(headingText: string): Chainable;
|
||||
|
||||
/**
|
||||
* Get a textbox by its text using "cy.findByRole"
|
||||
*
|
||||
* @param {string} text - Textbox label
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetTextbox('Nickname');
|
||||
*/
|
||||
uiGetTextbox(text: string): Chainable;
|
||||
}
|
||||
}
|
||||
55
e2e-tests/cypress/tests/support/ui/common.js
Normal file
55
e2e-tests/cypress/tests/support/ui/common.js
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
Cypress.Commands.add('uiSave', () => {
|
||||
return cy.findByRole('button', {name: 'Save'}).scrollIntoView().click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiCancel', () => {
|
||||
return cy.findByRole('button', {name: 'Cancel'}).click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiClose', () => {
|
||||
return cy.findAllByRole('button', {name: 'Close'}).eq(0).click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiSaveAndClose', () => {
|
||||
cy.uiSave();
|
||||
cy.uiClose();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetButton', (name) => {
|
||||
return cy.findByRole('button', {name});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiSaveButton', () => {
|
||||
return cy.uiGetButton('Save');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiCancelButton', () => {
|
||||
return cy.uiGetButton('Cancel');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiCloseButton', () => {
|
||||
return cy.uiGetButton('Close');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetRadioButton', (name) => {
|
||||
return cy.findByRole('radio', {name}).should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetHeading', (name) => {
|
||||
return cy.findByRole('heading', {name}).should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetTextbox', (name) => {
|
||||
return cy.findByRole('textbox', {name}).should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiCloseOnboardingTaskList', () => {
|
||||
cy.get('[data-cy=onboarding-task-list-action-button]').then(($btn) => {
|
||||
if ($btn.find('i.icon-close').length) {
|
||||
$btn.trigger('click');
|
||||
}
|
||||
});
|
||||
});
|
||||
39
e2e-tests/cypress/tests/support/ui/compliance_export.d.ts
vendored
Normal file
39
e2e-tests/cypress/tests/support/ui/compliance_export.d.ts
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Select compliance export format
|
||||
* @param {string} exportFormat - compliance export format
|
||||
*
|
||||
* @example
|
||||
* const EXPORTFORMAT = "Actiance XML";
|
||||
* cy.uiEnableComplianceExport(Compliance Export Format);
|
||||
*/
|
||||
uiEnableComplianceExport(exportFormat: string): Chainable;
|
||||
|
||||
/**
|
||||
* Go to Compliance Page
|
||||
*/
|
||||
uiGoToCompliancePage(): Chainable;
|
||||
|
||||
/**
|
||||
* Click Run Export Compliance and wait for Success status
|
||||
*/
|
||||
uiExportCompliance(): Chainable;
|
||||
}
|
||||
}
|
||||
53
e2e-tests/cypress/tests/support/ui/compliance_export.js
Normal file
53
e2e-tests/cypress/tests/support/ui/compliance_export.js
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import * as TIMEOUTS from '../../fixtures/timeouts';
|
||||
|
||||
Cypress.Commands.add('uiEnableComplianceExport', (exportFormat = 'csv') => {
|
||||
// * Verify that the page is loaded
|
||||
cy.findByText('Enable Compliance Export:').should('be.visible');
|
||||
cy.findByText('Compliance Export Time:').should('be.visible');
|
||||
cy.findByText('Export Format:').should('be.visible');
|
||||
|
||||
// # Enable compliance export
|
||||
cy.findByRole('radio', {name: /false/i}).click();
|
||||
cy.findByRole('radio', {name: /true/i}).click();
|
||||
|
||||
// # Change export format
|
||||
cy.findByTestId('exportFormatdropdown').should('be.visible').select(exportFormat);
|
||||
|
||||
// # Save settings
|
||||
cy.uiSaveConfig({confirm: true});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGoToCompliancePage', () => {
|
||||
cy.visit('/admin_console/compliance/export');
|
||||
cy.get('.admin-console__header', {timeout: TIMEOUTS.TWO_MIN}).should('be.visible').invoke('text').should('include', 'Compliance Export');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiExportCompliance', () => {
|
||||
// # Click the export job button
|
||||
cy.findByRole('button', {name: /run compliance export job now/i}).click();
|
||||
|
||||
// # Small wait to ensure new row is add
|
||||
cy.wait(TIMEOUTS.THREE_SEC);
|
||||
|
||||
// # Get the first row
|
||||
cy.get('.job-table__table').find('tbody > tr').eq(0).as('firstRow');
|
||||
|
||||
// # Get the first table header
|
||||
cy.get('.job-table__table').find('thead > tr').as('firstheader');
|
||||
|
||||
// # Wait until export is finished
|
||||
cy.waitUntil(() => {
|
||||
return cy.get('@firstRow').find('td:eq(0)').then((el) => {
|
||||
return el[0].innerText.trim() === 'Success';
|
||||
});
|
||||
},
|
||||
{
|
||||
timeout: TIMEOUTS.FIVE_MIN,
|
||||
interval: TIMEOUTS.ONE_SEC,
|
||||
errorMsg: 'Compliance export did not finish in time',
|
||||
});
|
||||
});
|
||||
|
||||
86
e2e-tests/cypress/tests/support/ui/data_retention.d.ts
vendored
Normal file
86
e2e-tests/cypress/tests/support/ui/data_retention.d.ts
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Go to Data Retention page
|
||||
*/
|
||||
uiGoToDataRetentionPage(): Chainable;
|
||||
|
||||
/**
|
||||
* Click create policy button
|
||||
*/
|
||||
uiClickCreatePolicy(): Chainable;
|
||||
|
||||
/**
|
||||
* Fill out custom policy form fields
|
||||
* @param {string} name - policy name
|
||||
* @param {string} durationDropdown - duration dropdown value (days, years, forever)
|
||||
* @param {string?} durationText - duration text
|
||||
*/
|
||||
uiFillOutCustomPolicyFields(name: string, durationDropdown: string, durationText?: string): Chainable;
|
||||
|
||||
/**
|
||||
* Search and add teams to custom policy
|
||||
* @param {string[]} teamNames - array of team names
|
||||
*/
|
||||
uiAddTeamsToCustomPolicy(teamNames: string[]): Chainable;
|
||||
|
||||
/**
|
||||
* Search and add channels to custom policy
|
||||
* @param {string[]} channelNames - array of channel names
|
||||
*/
|
||||
uiAddChannelsToCustomPolicy(channelNames: string[]): Chainable;
|
||||
|
||||
/**
|
||||
* Add teams to a custom policy
|
||||
* @param {number} numberOfTeams - number of teams to add to the policy
|
||||
*/
|
||||
uiAddRandomTeamToCustomPolicy(numberOfTeams?: number): Chainable;
|
||||
|
||||
/**
|
||||
* Add channels to a custom policy
|
||||
* @param {number} numberOfTeams - number of teams to add to the policy
|
||||
*/
|
||||
uiAddRandomChannelToCustomPolicy(numberOfChannels?: number): Chainable;
|
||||
|
||||
/**
|
||||
* Verify custom policy UI information
|
||||
* @param {string} policyId - Custom Policy ID
|
||||
* @param {string} description - The name of the policy
|
||||
* @param {string} duration - How long messages last in the policy
|
||||
* @param {string} appliedTo - Teams and channels the policy apples to
|
||||
*/
|
||||
uiVerifyCustomPolicyRow(policyId: string, description: string, duration: string, appliedTo: string): Chainable;
|
||||
|
||||
/**
|
||||
* Click edit custom policy
|
||||
* @param {string} policyId - Custom Policy ID
|
||||
*/
|
||||
uiClickEditCustomPolicyRow(policyId: string): Chainable;
|
||||
|
||||
/**
|
||||
* Verify custom create policy response
|
||||
* @param body - Response body
|
||||
* @param {number} teamCount - Number of teams the policy applies to
|
||||
* @param {number} channelCount - Number of channels the policy applies to
|
||||
* @param {number} duration - How long messages last in the policy
|
||||
* @param {string} displayName - The name of the policy
|
||||
*/
|
||||
uiVerifyPolicyResponse(body, teamCount: number, channelCount: number, duration: number, displayName: string): Chainable;
|
||||
}
|
||||
}
|
||||
105
e2e-tests/cypress/tests/support/ui/data_retention.js
Normal file
105
e2e-tests/cypress/tests/support/ui/data_retention.js
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import * as TIMEOUTS from '../../fixtures/timeouts';
|
||||
|
||||
Cypress.Commands.add('uiGoToDataRetentionPage', () => {
|
||||
cy.visit('/admin_console/compliance/data_retention_settings');
|
||||
cy.get('.DataRetentionSettings .admin-console__header', {timeout: TIMEOUTS.TWO_MIN}).should('be.visible').invoke('text').should('include', 'Data Retention Policies');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiClickCreatePolicy', () => {
|
||||
cy.uiGetButton('Add policy').click();
|
||||
cy.get('.DataRetentionSettings .admin-console__header', {timeout: TIMEOUTS.TWO_MIN}).should('be.visible').invoke('text').should('include', 'Custom Retention Policy');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiFillOutCustomPolicyFields', (name, durationDropdown, durationText = '') => {
|
||||
// # Type policy name
|
||||
cy.uiGetTextbox('Policy name').clear().type(name);
|
||||
|
||||
// # Add message retention values
|
||||
cy.get('.CustomPolicy__fields #DropdownInput_message_retention').should('be.visible').click();
|
||||
cy.get(`.message_retention__menu .message_retention__option span.option_${durationDropdown}`).should('be.visible').click();
|
||||
if (durationText) {
|
||||
cy.get('.CustomPolicy__fields input#message_retention_input').clear().type(durationText);
|
||||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiAddTeamsToCustomPolicy', (teamNames) => {
|
||||
cy.uiGetButton('Add teams').click();
|
||||
teamNames.forEach((teamName) => {
|
||||
cy.findByRole('combobox', {name: 'Search and add teams'}).typeWithForce(teamName);
|
||||
cy.get('.team-info-block').then((el) => {
|
||||
el.click();
|
||||
});
|
||||
});
|
||||
cy.uiGetButton('Add').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiAddChannelsToCustomPolicy', (channelNames) => {
|
||||
cy.uiGetButton('Add channels').click();
|
||||
channelNames.forEach((channelName) => {
|
||||
cy.findByRole('combobox', {name: 'Search and add channels'}).typeWithForce(channelName);
|
||||
cy.wait(TIMEOUTS.ONE_SEC);
|
||||
cy.get('.channel-info-block').then((el) => {
|
||||
el.click();
|
||||
});
|
||||
});
|
||||
cy.uiGetButton('Add').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiAddRandomTeamToCustomPolicy', (numberOfTeams = 1) => {
|
||||
cy.uiGetButton('Add teams').click();
|
||||
for (let i = 0; i < numberOfTeams; i++) {
|
||||
cy.get('.team-info-block').first().then((el) => {
|
||||
el.click();
|
||||
});
|
||||
}
|
||||
cy.uiGetButton('Add').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiAddRandomChannelToCustomPolicy', (numberOfChannels = 1) => {
|
||||
cy.uiGetButton('Add channels').click();
|
||||
for (let i = 0; i < numberOfChannels; i++) {
|
||||
cy.get('.channel-info-block').first().then((el) => {
|
||||
el.click();
|
||||
});
|
||||
}
|
||||
cy.uiGetButton('Add').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiVerifyCustomPolicyRow', (policyId, description, duration, appliedTo) => {
|
||||
// * Assert row has correct description
|
||||
cy.get(`#customDescription-${policyId}`).should('include.text', description);
|
||||
|
||||
// * Assert row has correct duration
|
||||
cy.get(`#customDuration-${policyId}`).should('include.text', duration);
|
||||
|
||||
// * Assert row has correct team/channel counts
|
||||
cy.get(`#customAppliedTo-${policyId}`).should('include.text', appliedTo);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiClickEditCustomPolicyRow', (policyId) => {
|
||||
cy.get(`#customWrapper-${policyId}`).trigger('mouseover').click();
|
||||
cy.findByRole('button', {name: /edit/i}).should('be.visible').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiVerifyPolicyResponse', (body, teamCount, channelCount, duration, displayName) => {
|
||||
// * Assert response body exists
|
||||
assert.isNotNull(body);
|
||||
|
||||
// * Assert response body contains an ID
|
||||
assert.isNotNull(body.id);
|
||||
|
||||
// * Assert response body team_count matches supplied value
|
||||
expect(body.team_count).to.equal(teamCount);
|
||||
|
||||
// * Assert response body channel_count matches supplied value
|
||||
expect(body.channel_count).to.equal(channelCount);
|
||||
|
||||
// * Assert response body duration matches supplied value
|
||||
expect(body.post_duration).to.equal(duration);
|
||||
|
||||
// * Assert response body display_name matches supplied value
|
||||
expect(body.display_name).to.equal(displayName);
|
||||
});
|
||||
59
e2e-tests/cypress/tests/support/ui/emoji.ts
Normal file
59
e2e-tests/cypress/tests/support/ui/emoji.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {ChainableT} from '@/types';
|
||||
|
||||
Cypress.Commands.add('uiGetEmojiPicker', (): ChainableT<JQuery> => {
|
||||
return cy.get('#emojiPicker').should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiOpenEmojiPicker', (): ChainableT<JQuery> => {
|
||||
cy.get('body').then(($body) => {
|
||||
if ($body.find('#emojiPicker:visible').length) {
|
||||
cy.get('body').type('{esc}');
|
||||
}
|
||||
});
|
||||
cy.get('#emojiPicker').should('not.exist');
|
||||
cy.findByRole('button', {name: 'select an emoji'}).should('be.visible').click();
|
||||
return cy.get('#emojiPicker').should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiOpenCustomEmoji', () => {
|
||||
cy.uiOpenEmojiPicker();
|
||||
cy.findByText('Custom Emoji').should('be.visible').click();
|
||||
|
||||
cy.url().should('include', '/emoji');
|
||||
cy.get('.backstage-header').should('be.visible').and('contain', 'Custom Emoji');
|
||||
});
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Open custom emoji
|
||||
*
|
||||
* @example
|
||||
* cy.uiOpenCustomEmoji();
|
||||
*/
|
||||
uiGetEmojiPicker(): Chainable;
|
||||
|
||||
/**
|
||||
* Open custom emoji
|
||||
*
|
||||
* @example
|
||||
* cy.uiOpenCustomEmoji();
|
||||
*/
|
||||
uiOpenCustomEmoji(): Chainable;
|
||||
|
||||
/**
|
||||
* Open emoji picker
|
||||
*
|
||||
* @example
|
||||
* cy.uiOpenEmojiPicker();
|
||||
*/
|
||||
uiOpenEmojiPicker(): Chainable;
|
||||
}
|
||||
}
|
||||
}
|
||||
30
e2e-tests/cypress/tests/support/ui/extend_testing_library.d.ts
vendored
Normal file
30
e2e-tests/cypress/tests/support/ui/extend_testing_library.d.ts
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of the Testing Library commands
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Extends `findByRole` by matching case to `name` as insensitive but sensitive to `text` value
|
||||
* @param {string} role - button, input, textbox, etc.
|
||||
* @param {Object} option - text value of the target element
|
||||
*
|
||||
* @example
|
||||
* cy.findByRoleExtended('button', {name: 'Advanced'}).should('be.visible').click();
|
||||
*/
|
||||
findByRoleExtended(role: string, option: {name: string}): Chainable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
Cypress.Commands.add('findByRoleExtended', (role, {name}) => {
|
||||
const re = RegExp(name, 'i');
|
||||
return cy.findByRole(role, {name: re}).should('have.text', name);
|
||||
});
|
||||
124
e2e-tests/cypress/tests/support/ui/file_preview.d.ts
vendored
Normal file
124
e2e-tests/cypress/tests/support/ui/file_preview.d.ts
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiOpenFilePreviewModal`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get file thumbnail from a post
|
||||
*
|
||||
* @param {string} filename
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetFileThumbnail('image.png');
|
||||
*/
|
||||
uiGetFileThumbnail(filename: string): Chainable;
|
||||
|
||||
/**
|
||||
* Get file upload preview located below post textbox
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetFileUploadPreview();
|
||||
*/
|
||||
uiGetFileUploadPreview(): Chainable;
|
||||
|
||||
/**
|
||||
* Wait for file upload preview located below post textbox
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetFileUploadPreview();
|
||||
*/
|
||||
uiGetFileUploadPreview(): Chainable;
|
||||
|
||||
/**
|
||||
* Get file preview modal
|
||||
*
|
||||
* @param {bool} option.exist - Set to false to not verify if the element exists. Otherwise, true (default) to check existence.
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetFilePreviewModal();
|
||||
*/
|
||||
uiGetFilePreviewModal(option: Record<string, boolean>): Chainable;
|
||||
|
||||
/**
|
||||
* Get Public Link
|
||||
*
|
||||
* @param {bool} option.exist - Set to false to not verify if the element exists. Otherwise, true (default) to check existence.
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetPublicLink();
|
||||
*/
|
||||
uiGetPublicLink(option: Record<string, boolean>): Chainable;
|
||||
|
||||
/**
|
||||
* Open file preview modal
|
||||
*
|
||||
* @param {string} filename
|
||||
*
|
||||
* @example
|
||||
* cy.uiOpenFilePreviewModal('image.png');
|
||||
*/
|
||||
uiOpenFilePreviewModal(filename: string): Chainable;
|
||||
|
||||
/**
|
||||
* Close file preview modal
|
||||
*
|
||||
* @example
|
||||
* cy.uiCloseFilePreviewModal();
|
||||
*/
|
||||
uiCloseFilePreviewModal(): Chainable;
|
||||
|
||||
/**
|
||||
* Get main content of file preview modal
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetContentFilePreviewModal();
|
||||
*/
|
||||
uiGetContentFilePreviewModal(): Chainable;
|
||||
|
||||
/**
|
||||
* Get download link button from file preview modal
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetDownloadLinkFilePreviewModal();
|
||||
*/
|
||||
uiGetDownloadLinkFilePreviewModal(): Chainable;
|
||||
|
||||
/**
|
||||
* Get download button from file preview modal
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetDownloadFilePreviewModal();
|
||||
*/
|
||||
uiGetDownloadFilePreviewModal(): Chainable;
|
||||
|
||||
/**
|
||||
* Get arrow left button from file preview modal
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetArrowLeftFilePreviewModal();
|
||||
*/
|
||||
uiGetArrowLeftFilePreviewModal(): Chainable;
|
||||
|
||||
/**
|
||||
* Get arrow right button from file preview modal
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetArrowRightFilePreviewModal();
|
||||
*/
|
||||
uiGetArrowRightFilePreviewModal(): Chainable;
|
||||
}
|
||||
}
|
||||
80
e2e-tests/cypress/tests/support/ui/file_preview.js
Normal file
80
e2e-tests/cypress/tests/support/ui/file_preview.js
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
Cypress.Commands.add('uiGetFileThumbnail', (filename) => {
|
||||
// Gallery tiles preserve original filename casing on data-file-name,
|
||||
// while the legacy thumbnail aria-label is always lowercased
|
||||
// (see file_preview.tsx). Match each accordingly.
|
||||
const tileMatches = Cypress.$('[data-testid="media-gallery-tile"]').filter((_, el) => {
|
||||
return el.getAttribute('data-file-name')?.toLowerCase() === filename.toLowerCase();
|
||||
});
|
||||
if (tileMatches.length) {
|
||||
return cy.wrap(tileMatches);
|
||||
}
|
||||
return cy.findByLabelText(`file thumbnail ${filename.toLowerCase()}`);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetFileUploadPreview', () => {
|
||||
return cy.get('.file-preview__container');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiWaitForFileUploadPreview', () => {
|
||||
cy.waitUntil(() => cy.uiGetFileUploadPreview().then((el) => {
|
||||
return el.find('.post-image__thumbnail').length > 0;
|
||||
}));
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetFilePreviewModal', (options = {exist: true}) => {
|
||||
if (options.exist) {
|
||||
return cy.get('.file-preview-modal').should('be.visible');
|
||||
}
|
||||
|
||||
return cy.get('.file-preview-modal').should('not.exist');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetPublicLink', (options = {exist: true}) => {
|
||||
if (options.exist) {
|
||||
return cy.get('.icon-link-variant').should('be.visible');
|
||||
}
|
||||
return cy.get('.icon-link-variant').should('not.exist');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetHeaderFilePreviewModal', () => {
|
||||
return cy.uiGetFilePreviewModal().find('.file-preview-modal-header').should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiOpenFilePreviewModal', (filename) => {
|
||||
if (filename) {
|
||||
cy.uiGetFileThumbnail(filename).click();
|
||||
return;
|
||||
}
|
||||
if (Cypress.$('[data-testid="media-gallery-tile"]').length) {
|
||||
cy.get('[data-testid="media-gallery-tile"]').first().click();
|
||||
} else {
|
||||
cy.findByTestId('fileAttachmentList').children().first().click();
|
||||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiCloseFilePreviewModal', () => {
|
||||
return cy.uiGetFilePreviewModal().find('.icon-close').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetContentFilePreviewModal', () => {
|
||||
return cy.uiGetFilePreviewModal().find('.file-preview-modal__content');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetDownloadLinkFilePreviewModal', () => {
|
||||
return cy.uiGetFilePreviewModal().find('.icon-link-variant');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetDownloadFilePreviewModal', () => {
|
||||
return cy.uiGetFilePreviewModal().find('.icon-download-outline');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetArrowLeftFilePreviewModal', () => {
|
||||
return cy.uiGetFilePreviewModal().find('.icon-chevron-left');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetArrowRightFilePreviewModal', () => {
|
||||
return cy.uiGetFilePreviewModal().find('.icon-chevron-right');
|
||||
});
|
||||
189
e2e-tests/cypress/tests/support/ui/global_header.d.ts
vendored
Normal file
189
e2e-tests/cypress/tests/support/ui/global_header.d.ts
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiGetProductMenuButton`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get product switch button
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetProductMenuButton().click();
|
||||
*/
|
||||
uiGetProductMenuButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get product switch menu
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetProductMenu().click();
|
||||
*/
|
||||
uiGetProductMenu(): Chainable;
|
||||
|
||||
/**
|
||||
* Open product switch menu
|
||||
*
|
||||
* @param {string} item - menu item ex. System Console, Integrations, etc.
|
||||
*
|
||||
* @example
|
||||
* cy.uiOpenProductMenu().click();
|
||||
*/
|
||||
uiOpenProductMenu(item: string = ''): Chainable;
|
||||
|
||||
/**
|
||||
* Get set status button
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetSetStatusButton().click();
|
||||
*/
|
||||
uiGetSetStatusButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get profile header
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetProfileHeader();
|
||||
*/
|
||||
uiGetProfileHeader(): Chainable;
|
||||
|
||||
/**
|
||||
* Get status menu container
|
||||
*
|
||||
* @param {bool} option.exist - Set to false to not verify if the element exists. Otherwise, true (default) to check existence.
|
||||
* @example
|
||||
* cy.uiGetStatusMenuContainer({exist: false});
|
||||
*/
|
||||
uiGetStatusMenuContainer(option?: Record<string, boolean>): Chainable;
|
||||
|
||||
/**
|
||||
* Get user menu
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetStatusMenu();
|
||||
*/
|
||||
uiGetStatusMenu(): Chainable;
|
||||
|
||||
/**
|
||||
* Open help menu
|
||||
*
|
||||
* @param {string} item - menu item ex. Ask the community, Help resources, etc.
|
||||
*
|
||||
* @example
|
||||
* cy.uiOpenHelpMenu();
|
||||
*/
|
||||
uiOpenHelpMenu(item: string): Chainable;
|
||||
|
||||
/**
|
||||
* Get help button
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetHelpButton();
|
||||
*/
|
||||
uiGetHelpButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get help menu
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetHelpMenu();
|
||||
*/
|
||||
uiGetHelpMenu(): Chainable;
|
||||
|
||||
/**
|
||||
* Open user menu
|
||||
*
|
||||
* @param {string} [item] - menu item ex. Profile, Logout, etc.
|
||||
*
|
||||
* @example
|
||||
* cy.uiOpenUserMenu();
|
||||
*/
|
||||
uiOpenUserMenu(item?: string): Chainable;
|
||||
|
||||
/**
|
||||
* Get search form container
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetSearchContainer();
|
||||
*/
|
||||
uiGetSearchContainer(): Chainable;
|
||||
|
||||
/**
|
||||
* Get search box
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetSearchBox();
|
||||
*/
|
||||
uiGetSearchBox(): Chainable;
|
||||
|
||||
/**
|
||||
* Get at-mention button
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetRecentMentionButton();
|
||||
*/
|
||||
uiGetRecentMentionButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get saved posts button
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetSavedPostButton();
|
||||
*/
|
||||
uiGetSavedPostButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get settings button
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetSettingsButton();
|
||||
*/
|
||||
uiGetSettingsButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get settings modal
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetSettingsModal();
|
||||
*/
|
||||
uiGetSettingsModal(): Chainable;
|
||||
|
||||
/**
|
||||
* Get channel info button
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetChannelInfoButton();
|
||||
*/
|
||||
uiGetChannelInfoButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Open settings modal
|
||||
*
|
||||
* @param {string} section - ex. Display, Sidebar, etc.
|
||||
*
|
||||
* @example
|
||||
* cy.uiOpenSettingsModal();
|
||||
*/
|
||||
uiOpenSettingsModal(section: string): Chainable;
|
||||
|
||||
/**
|
||||
* User log out via user menu
|
||||
*
|
||||
* @example
|
||||
* cy.uiLogout();
|
||||
*/
|
||||
uiLogout(): Chainable;
|
||||
}
|
||||
}
|
||||
152
e2e-tests/cypress/tests/support/ui/global_header.js
Normal file
152
e2e-tests/cypress/tests/support/ui/global_header.js
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
Cypress.Commands.add('uiGetProductMenuButton', () => {
|
||||
return cy.findByRole('button', {name: 'Product switch menu'}).should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetProductMenu', () => {
|
||||
return cy.get('.product-switcher-menu').should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiOpenProductMenu', (item = '') => {
|
||||
// # Click on product switch button
|
||||
cy.uiGetProductMenuButton().click();
|
||||
|
||||
if (!item) {
|
||||
// # Return the menu if no item is passed
|
||||
return cy.uiGetProductMenu();
|
||||
}
|
||||
|
||||
// # Click on a particular item
|
||||
return cy.uiGetProductMenu().
|
||||
findByText(item).
|
||||
scrollIntoView().
|
||||
should('be.visible').
|
||||
click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetSetStatusButton', () => {
|
||||
// # Get set status button
|
||||
return cy.get('#userAccountMenuButton').should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetProfileHeader', () => {
|
||||
return cy.uiGetSetStatusButton().parent();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetStatusMenuContainer', (options = {exist: true}) => {
|
||||
if (options.exist) {
|
||||
return cy.get('#userAccountMenu').should('exist').and('be.visible');
|
||||
}
|
||||
|
||||
return cy.get('#userAccountMenu').should('not.exist');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetStatusMenu', (options = {visible: true}) => {
|
||||
if (options.visible) {
|
||||
return cy.get('#userAccountMenu').should('exist').and('be.visible');
|
||||
}
|
||||
|
||||
return cy.get('#userAccountMenu').should('not.exist');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiOpenHelpMenu', (item = '') => {
|
||||
// # Click on help status button
|
||||
cy.uiGetHelpButton().click();
|
||||
|
||||
if (!item) {
|
||||
// # Return the menu if no item is passed
|
||||
return cy.uiGetHelpMenu();
|
||||
}
|
||||
|
||||
// # Click on a particular item
|
||||
return cy.uiGetHelpMenu().
|
||||
findByText(item).
|
||||
scrollIntoView().
|
||||
should('be.visible').
|
||||
click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetHelpButton', () => {
|
||||
return cy.findByRole('button', {name: 'Help'}).should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetHelpMenu', (options = {visible: true}) => {
|
||||
const dropdown = () => cy.get('#helpMenuPortal').find('.dropdown-menu');
|
||||
|
||||
if (options.visible) {
|
||||
return dropdown().should('be.visible');
|
||||
}
|
||||
|
||||
return dropdown().should('not.be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiOpenUserMenu', (item = '') => {
|
||||
// # Click on user status button
|
||||
cy.uiGetSetStatusButton().click();
|
||||
|
||||
if (!item) {
|
||||
// # Return the menu if no item is passed
|
||||
return cy.uiGetStatusMenu();
|
||||
}
|
||||
|
||||
// # Click on a particular item
|
||||
return cy.uiGetStatusMenu().
|
||||
findByText(item).
|
||||
scrollIntoView().
|
||||
should('be.visible').
|
||||
click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetSearchContainer', () => {
|
||||
return cy.get('#searchFormContainer').should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetSearchBox', () => {
|
||||
return cy.findByRole('searchbox').should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetRecentMentionButton', () => {
|
||||
return cy.findByRole('button', {name: 'Recent mentions'}).should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetSavedPostButton', () => {
|
||||
return cy.findByRole('button', {name: 'Saved messages'}).should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetSettingsButton', () => {
|
||||
return cy.findByRole('button', {name: 'Settings'}).should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetChannelInfoButton', () => {
|
||||
return cy.findByRole('button', {name: 'View Info'}).should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetSettingsModal', () => {
|
||||
// # Get settings modal
|
||||
return cy.findByRole('dialog', {name: 'Settings'});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiOpenSettingsModal', (section = '') => {
|
||||
// # Open settings modal
|
||||
cy.uiGetSettingsButton().click();
|
||||
|
||||
if (!section) {
|
||||
return cy.uiGetSettingsModal();
|
||||
}
|
||||
|
||||
// # Click on a particular section
|
||||
cy.findByRoleExtended('tab', {name: section}).should('be.visible').click();
|
||||
|
||||
return cy.uiGetSettingsModal();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiLogout', () => {
|
||||
// # Click logout via user menu
|
||||
cy.uiOpenUserMenu('Log out');
|
||||
|
||||
cy.url().should('include', '/login');
|
||||
cy.get('.login-body-message').should('be.visible');
|
||||
cy.get('.login-body-card').should('be.visible');
|
||||
});
|
||||
30
e2e-tests/cypress/tests/support/ui/index.js
Normal file
30
e2e-tests/cypress/tests/support/ui/index.js
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import './account_settings_modal';
|
||||
import './announcement_bar';
|
||||
import './boards';
|
||||
import './channel';
|
||||
import './channel_header';
|
||||
import './channel_sidebar';
|
||||
import './cloud_billing';
|
||||
import './common';
|
||||
import './compliance_export';
|
||||
import './data_retention';
|
||||
import './extend_testing_library';
|
||||
import './global_header';
|
||||
import './emoji';
|
||||
import './file_preview';
|
||||
import './login';
|
||||
import './menu';
|
||||
import './mfa';
|
||||
import './modal';
|
||||
import './post';
|
||||
import './post_dropdown_menu';
|
||||
import './search';
|
||||
import './sidebar_left';
|
||||
import './sidebar_right';
|
||||
import './suggestion_list';
|
||||
import './system';
|
||||
import './team';
|
||||
import './tooltip';
|
||||
30
e2e-tests/cypress/tests/support/ui/login.d.ts
vendored
Normal file
30
e2e-tests/cypress/tests/support/ui/login.d.ts
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Login vi UI at login page
|
||||
*
|
||||
* @param {UserProfile} user - user with username and password
|
||||
*
|
||||
* @example
|
||||
* cy.uiLogin(user);
|
||||
*/
|
||||
uiLogin(user: UserProfile): Chainable;
|
||||
}
|
||||
}
|
||||
11
e2e-tests/cypress/tests/support/ui/login.js
Normal file
11
e2e-tests/cypress/tests/support/ui/login.js
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
Cypress.Commands.add('uiLogin', (user = {}) => {
|
||||
cy.url().should('include', '/login');
|
||||
|
||||
// # Type email and password, then Sign in
|
||||
cy.get('#input_loginId').should('be.visible').type(user.email);
|
||||
cy.get('#input_password-input').should('be.visible').type(user.password);
|
||||
cy.get('#saveSetting').should('not.be.disabled').click();
|
||||
});
|
||||
46
e2e-tests/cypress/tests/support/ui/menu.d.ts
vendored
Normal file
46
e2e-tests/cypress/tests/support/ui/menu.d.ts
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiOpenSystemConsoleMainMenu`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Open main menu at system console
|
||||
* @param {string} item - such as `'Switch to [Team Name]'`, `'Administrator's Guide'`, `'Troubleshooting Forum'`, `'Commercial Support'`, `'About Mattermost'` and `'Log Out'`.
|
||||
* @return the main menu
|
||||
*
|
||||
* @example
|
||||
* cy.uiOpenSystemConsoleMainMenu();
|
||||
*/
|
||||
uiOpenSystemConsoleMainMenu(): Chainable;
|
||||
|
||||
/**
|
||||
* Close main menu at system console
|
||||
*
|
||||
* @example
|
||||
* cy.uiCloseSystemConsoleMainMenu();
|
||||
*/
|
||||
uiCloseSystemConsoleMainMenu(): Chainable;
|
||||
|
||||
/**
|
||||
* Get main menu at system console
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetSystemConsoleMainMenu();
|
||||
*/
|
||||
uiGetSystemConsoleMainMenu(): Chainable;
|
||||
}
|
||||
}
|
||||
40
e2e-tests/cypress/tests/support/ui/menu.js
Normal file
40
e2e-tests/cypress/tests/support/ui/menu.js
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
const SYSTEM_CONSOLE_MAIN_MENU = 'Menu Icon';
|
||||
|
||||
function openMenu(name, item) {
|
||||
const menu = () => cy.findByRole('button', {name}).should('be.visible');
|
||||
|
||||
// # Open the menu
|
||||
menu().should('be.visible').click();
|
||||
|
||||
if (!item) {
|
||||
return menu();
|
||||
}
|
||||
|
||||
// # Click on a particular item
|
||||
return cy.findByRole('menu').findByText(item).scrollIntoView().should('be.visible').click();
|
||||
}
|
||||
|
||||
function getMenu(name) {
|
||||
return cy.findByRole('button', {name}).should('be.visible');
|
||||
}
|
||||
|
||||
Cypress.Commands.add('uiOpenSystemConsoleMainMenu', (item = '') => {
|
||||
return openMenu(SYSTEM_CONSOLE_MAIN_MENU, item);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiCloseSystemConsoleMainMenu', () => {
|
||||
return cy.uiGetSystemConsoleMainMenu().click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetSystemConsoleMainMenu', () => {
|
||||
return getMenu(SYSTEM_CONSOLE_MAIN_MENU);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiOpenDndStatusSubMenuAndClick30Mins', () => {
|
||||
cy.uiOpenUserMenu().findByText('Do not disturb').should('be.visible').trigger('mouseover');
|
||||
|
||||
cy.findByText('30 mins').should('be.visible').click({force: true});
|
||||
});
|
||||
34
e2e-tests/cypress/tests/support/ui/mfa.d.ts
vendored
Normal file
34
e2e-tests/cypress/tests/support/ui/mfa.d.ts
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiGetMFASecret`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get MFA secret of a given user
|
||||
* @param {string} userId - ID of user
|
||||
*
|
||||
* @returns {string} `secret` - MFA secret
|
||||
*
|
||||
* @example
|
||||
* const headerLabel = 'What\'s New';
|
||||
* cy.uiGetMFASecret('user-id').then((secret) => {
|
||||
* // do something with the secret
|
||||
* });
|
||||
*/
|
||||
uiGetMFASecret(userId: string): Chainable<string>;
|
||||
}
|
||||
}
|
||||
32
e2e-tests/cypress/tests/support/ui/mfa.js
Normal file
32
e2e-tests/cypress/tests/support/ui/mfa.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import authenticator from 'authenticator';
|
||||
|
||||
import * as TIMEOUTS from '../../fixtures/timeouts';
|
||||
|
||||
Cypress.Commands.add('uiGetMFASecret', (userId) => {
|
||||
return cy.url().then((url) => {
|
||||
if (url.includes('mfa/setup')) {
|
||||
// # Complete MFA setup if we are on token setup page /mfa/setup
|
||||
return cy.get('#mfa').wait(TIMEOUTS.HALF_SEC).find('.col-sm-12').then((p) => {
|
||||
const secretp = p.text();
|
||||
const secret = secretp.split(' ')[1];
|
||||
|
||||
const token = authenticator.generateToken(secret);
|
||||
cy.findByPlaceholderText('MFA Code').type(token);
|
||||
cy.findByText('Save').click();
|
||||
|
||||
cy.wait(TIMEOUTS.HALF_SEC);
|
||||
cy.findByText('Okay').click();
|
||||
|
||||
return cy.wrap(secret);
|
||||
});
|
||||
}
|
||||
|
||||
// # If the user already has MFA enabled, reset the secret.
|
||||
return cy.apiGenerateMfaSecret(userId).then((res) => {
|
||||
return cy.wrap(res.code.secret);
|
||||
});
|
||||
});
|
||||
});
|
||||
30
e2e-tests/cypress/tests/support/ui/modal.d.ts
vendored
Normal file
30
e2e-tests/cypress/tests/support/ui/modal.d.ts
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiCloseModal`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Close modal with header label
|
||||
* @param {string} headerLabel - the header label
|
||||
*
|
||||
* @example
|
||||
* const headerLabel = 'What\'s New';
|
||||
* cy.uiCloseModal(headerLabel);
|
||||
*/
|
||||
uiCloseModal(headerLabel: string): Chainable;
|
||||
}
|
||||
}
|
||||
9
e2e-tests/cypress/tests/support/ui/modal.js
Normal file
9
e2e-tests/cypress/tests/support/ui/modal.js
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import * as TIMEOUTS from '../../fixtures/timeouts';
|
||||
|
||||
Cypress.Commands.add('uiCloseModal', (headerLabel) => {
|
||||
// # Close modal with modal label
|
||||
cy.get('#genericModalLabel', {timeout: TIMEOUTS.HALF_MIN}).should('have.text', headerLabel).parents().find('.modal-dialog').findByLabelText('Close').click();
|
||||
});
|
||||
325
e2e-tests/cypress/tests/support/ui/post.ts
Normal file
325
e2e-tests/cypress/tests/support/ui/post.ts
Normal file
@@ -0,0 +1,325 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {ChainableT} from '../../types';
|
||||
|
||||
function uiGetPostTextBox(option = {exist: true}): ChainableT<JQuery> {
|
||||
if (option.exist) {
|
||||
return cy.get('#post_textbox').should('be.visible');
|
||||
}
|
||||
|
||||
return cy.get('#post_textbox').should('not.exist');
|
||||
}
|
||||
Cypress.Commands.add('uiGetPostTextBox', uiGetPostTextBox);
|
||||
|
||||
function uiGetReplyTextBox(option = {exist: true}): ChainableT<JQuery> {
|
||||
if (option.exist) {
|
||||
return cy.get('#reply_textbox').should('be.visible');
|
||||
}
|
||||
|
||||
return cy.get('#reply_textbox').should('not.exist');
|
||||
}
|
||||
Cypress.Commands.add('uiGetReplyTextBox', uiGetReplyTextBox);
|
||||
|
||||
function uiGetPostProfileImage(postId: string): ChainableT<JQuery> {
|
||||
return getPost(postId).within(() => {
|
||||
return cy.get('.post__img').should('be.visible');
|
||||
});
|
||||
}
|
||||
Cypress.Commands.add('uiGetPostProfileImage', uiGetPostProfileImage);
|
||||
|
||||
function uiGetPostHeader(postId: string): ChainableT<JQuery> {
|
||||
return getPost(postId).within(() => {
|
||||
return cy.get('.post__header').should('be.visible');
|
||||
});
|
||||
}
|
||||
Cypress.Commands.add('uiGetPostHeader', uiGetPostHeader);
|
||||
|
||||
function uiGetPostBody(postId: string): ChainableT<JQuery> {
|
||||
return getPost(postId).within(() => {
|
||||
return cy.get('.post__body').should('be.visible');
|
||||
});
|
||||
}
|
||||
Cypress.Commands.add('uiGetPostBody', uiGetPostBody);
|
||||
|
||||
function uiGetPostThreadFooter(postId: string): ChainableT<JQuery> {
|
||||
return getPost(postId).find('.ThreadFooter');
|
||||
}
|
||||
Cypress.Commands.add('uiGetPostThreadFooter', uiGetPostThreadFooter);
|
||||
|
||||
function uiGetPostEmbedContainer(postId: string): ChainableT<JQuery> {
|
||||
return cy.uiGetPostBody(postId).
|
||||
find('.file-preview__button').
|
||||
should('be.visible');
|
||||
}
|
||||
Cypress.Commands.add('uiGetPostEmbedContainer', uiGetPostEmbedContainer);
|
||||
|
||||
function getCenterPostById(postId: string): ChainableT<JQuery> {
|
||||
// Prefer postView rows: virtualized lists may detach #post_<id> from a bare cy.get() query.
|
||||
return cy.findAllByTestId('postView', {timeout: 20000}).
|
||||
filter(`#post_${postId}`).
|
||||
should('have.length.at.least', 1).
|
||||
first().
|
||||
scrollIntoView().
|
||||
should('be.visible');
|
||||
}
|
||||
|
||||
function getPost(postId: string): ChainableT<JQuery> {
|
||||
if (postId) {
|
||||
return getCenterPostById(postId);
|
||||
}
|
||||
|
||||
return cy.getLastPost();
|
||||
}
|
||||
Cypress.Commands.add('getPost', getPost);
|
||||
|
||||
function getPostMmBlocks(postId: string): ChainableT<JQuery> {
|
||||
return getCenterPostById(postId).find('.mm-blocks').should('exist');
|
||||
}
|
||||
Cypress.Commands.add('getPostMmBlocks', getPostMmBlocks);
|
||||
|
||||
function getRhsPostMmBlocks(postId: string): ChainableT<JQuery> {
|
||||
return cy.findAllByTestId('rhsPostView', {timeout: 20000}).
|
||||
filter(`#rhsPost_${postId}`).
|
||||
should('have.length.at.least', 1).
|
||||
first().
|
||||
find('.mm-blocks').
|
||||
should('exist');
|
||||
}
|
||||
Cypress.Commands.add('getRhsPostMmBlocks', getRhsPostMmBlocks);
|
||||
|
||||
function editLastPostWithNewMessage(message: string) {
|
||||
cy.uiGetPostTextBox().type('{uparrow}');
|
||||
|
||||
// * Edit Post Input should appear
|
||||
cy.get('#edit_textbox').should('be.visible');
|
||||
|
||||
// # Update the post message and click Save
|
||||
cy.get('#edit_textbox').clear().type(message);
|
||||
cy.get('#create_post').findByText('Save').scrollIntoView().click();
|
||||
}
|
||||
Cypress.Commands.add('editLastPostWithNewMessage', editLastPostWithNewMessage);
|
||||
|
||||
export function verifySavedPost(postId: string, message: string) {
|
||||
// * Check that the center save icon has been updated correctly
|
||||
cy.get(`#post_${postId}`).trigger('mouseover', {force: true});
|
||||
cy.get(`#CENTER_flagIcon_${postId}`).
|
||||
should('have.class', 'post-menu__item').
|
||||
and('have.attr', 'aria-label', 'remove from saved');
|
||||
|
||||
// # Open the post-dotmenu
|
||||
cy.clickPostDotMenu(postId, 'CENTER');
|
||||
|
||||
// * Check that the dotmenu item is changed accordingly
|
||||
cy.findAllByTestId(`post-menu-${postId}`).eq(0).should('be.visible');
|
||||
cy.findByText('Remove from Saved').scrollIntoView().should('be.visible');
|
||||
cy.get('body').type('{esc}');
|
||||
|
||||
cy.get('#postListContent').within(() => {
|
||||
// * Check that the post is highlighted
|
||||
cy.get(`#post_${postId}`).should('have.class', 'post--pinned-or-flagged');
|
||||
|
||||
// * Check that the post pre-header is visible
|
||||
cy.get('div.post-pre-header').should('be.visible');
|
||||
|
||||
// * Check that the post pre-header has the saved icon
|
||||
cy.get('span.icon--post-pre-header').
|
||||
should('be.visible').
|
||||
within(() => {
|
||||
cy.get('svg').should('have.attr', 'aria-label', 'Saved Icon');
|
||||
});
|
||||
|
||||
// * Check that the post pre-header has the saved post link
|
||||
cy.get('div.post-pre-header__text-container').
|
||||
should('be.visible').
|
||||
and('have.text', 'Saved').
|
||||
within(() => {
|
||||
cy.get('a').as('savedLink').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
// * Check that the saved posts list is not open in RHS before clicking the link in the post pre-header
|
||||
cy.get('#searchContainer').should('not.exist');
|
||||
|
||||
// # Click the link
|
||||
cy.get('@savedLink').click();
|
||||
|
||||
// * Check that the saved posts list is open in RHS
|
||||
cy.get('#searchContainer').should('be.visible').within(() => {
|
||||
cy.get('.sidebar--right__title').
|
||||
should('be.visible').
|
||||
and('have.text', 'Saved messages');
|
||||
|
||||
// * Check that the post pre-header is not shown for the saved message in RHS
|
||||
cy.get(`#searchResult_${postId}`).within(() => {
|
||||
cy.get(`#rhsPostMessageText_${postId}`).contains(message);
|
||||
cy.get('div.post-pre-header').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
// # Close the RHS
|
||||
cy.get('#searchResultsCloseButton').should('be.visible').click();
|
||||
}
|
||||
|
||||
export function verifyUnsavedPost(postId: string) {
|
||||
// * Check that the center save icon has been updated correctly
|
||||
cy.get(`#post_${postId}`).trigger('mouseover', {force: true});
|
||||
cy.get(`#CENTER_flagIcon_${postId}`).
|
||||
should('have.class', 'post-menu__item').
|
||||
and('have.attr', 'aria-label', 'save message');
|
||||
|
||||
// # Open the post-dotmenu
|
||||
cy.clickPostDotMenu(postId, 'CENTER');
|
||||
|
||||
// * Check that the dotmenu item is changed accordingly
|
||||
cy.findAllByTestId(`post-menu-${postId}`).eq(0).should('be.visible');
|
||||
cy.findByText('Save Message').scrollIntoView().should('be.visible');
|
||||
cy.get('body').type('{esc}');
|
||||
|
||||
cy.get('#postListContent').within(() => {
|
||||
// * Check that the post is not highlighted
|
||||
cy.get(`#post_${postId}`).should('not.have.class', 'post--pinned-or-flagged');
|
||||
|
||||
// * Check that the post pre-header is not visible
|
||||
cy.get('div.post-pre-header').should('not.exist');
|
||||
|
||||
// * Check that the post pre-header does not have the saved icon
|
||||
cy.get('span.icon--post-pre-header').
|
||||
should('not.exist');
|
||||
|
||||
// * Check that the post pre-header does not have the saved post link
|
||||
cy.get('div.post-pre-header__text-container').
|
||||
should('not.exist');
|
||||
});
|
||||
|
||||
// * Check that the saved posts list is not open in RHS before clicking the link in the post pre-header
|
||||
cy.get('#searchContainer').should('not.exist');
|
||||
|
||||
// # Click the link
|
||||
cy.uiGetSavedPostButton().click();
|
||||
|
||||
// * Check that the saved posts list is open in RHS
|
||||
cy.get('#searchContainer').should('be.visible').within(() => {
|
||||
cy.get('.sidebar--right__title').
|
||||
should('be.visible').
|
||||
and('have.text', 'Saved messages');
|
||||
|
||||
// * Check that the post pre-header is not shown for the saved message in RHS
|
||||
cy.get('#search-items-container').within(() => {
|
||||
cy.get(`#rhsPostMessageText_${postId}`).should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
// # Close the RHS
|
||||
cy.get('#searchResultsCloseButton').should('be.visible').click();
|
||||
}
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Edit last post with a new message
|
||||
*
|
||||
* @param {string} - message
|
||||
*
|
||||
* @example
|
||||
* cy.editLastPostWithNewMessage('new message');
|
||||
*/
|
||||
editLastPostWithNewMessage: typeof editLastPostWithNewMessage;
|
||||
|
||||
/**
|
||||
* Get post profile image of a given post ID or the last post if post ID is not given
|
||||
*
|
||||
* @param {string} - postId (optional)
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetPostProfileImage();
|
||||
*/
|
||||
uiGetPostProfileImage: typeof uiGetPostProfileImage;
|
||||
|
||||
/**
|
||||
* Get post header of a given post ID or the last post if post ID is not given
|
||||
*
|
||||
* @param {string} - postId (optional)
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetPostHeader();
|
||||
*/
|
||||
uiGetPostHeader: typeof uiGetPostHeader;
|
||||
|
||||
/**
|
||||
* Get post body of a given post ID or the last post if post ID is not given
|
||||
*
|
||||
* @param {string} - postId (optional)
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetPostBody();
|
||||
*/
|
||||
uiGetPostBody: typeof uiGetPostBody;
|
||||
|
||||
/**
|
||||
* Get post thread footer of a given post ID or the last post if post ID is not given
|
||||
*
|
||||
* @param {string} - postId (optional)
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetPostThreadFooter();
|
||||
*/
|
||||
uiGetPostThreadFooter: typeof uiGetPostThreadFooter;
|
||||
|
||||
/**
|
||||
* Get post embed container of a given post ID or the last post if post ID is not given
|
||||
*
|
||||
* @param {string} - postId (optional)
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetPostEmbedContainer();
|
||||
*/
|
||||
uiGetPostEmbedContainer: typeof uiGetPostEmbedContainer;
|
||||
|
||||
/**
|
||||
* Get post textbox
|
||||
*
|
||||
* @param {bool} option.exist - Set to false to check whether element should not exist. Otherwise, true (default) to check visibility.
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetPostTextBox();
|
||||
*/
|
||||
uiGetPostTextBox: typeof uiGetPostTextBox;
|
||||
|
||||
/**
|
||||
* Get reply textbox
|
||||
*
|
||||
* @param {bool} option.exist - Set to false to check whether element should not exist. Otherwise, true (default) to check visibility.
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetReplyTextBox();
|
||||
*/
|
||||
uiGetReplyTextBox: typeof uiGetReplyTextBox;
|
||||
|
||||
getPost: typeof getPost;
|
||||
|
||||
/**
|
||||
* Get mm_blocks root within a center-channel post.
|
||||
*
|
||||
* @param {string} postId
|
||||
*
|
||||
* @example
|
||||
* cy.getPostMmBlocks(postId).within(() => { ... });
|
||||
*/
|
||||
getPostMmBlocks: typeof getPostMmBlocks;
|
||||
|
||||
/**
|
||||
* Get mm_blocks root within an RHS/thread post row.
|
||||
*
|
||||
* @param {string} postId
|
||||
*
|
||||
* @example
|
||||
* cy.getRhsPostMmBlocks(postId).within(() => { ... });
|
||||
*/
|
||||
getRhsPostMmBlocks: typeof getRhsPostMmBlocks;
|
||||
}
|
||||
}
|
||||
}
|
||||
39
e2e-tests/cypress/tests/support/ui/post_dropdown_menu.d.ts
vendored
Normal file
39
e2e-tests/cypress/tests/support/ui/post_dropdown_menu.d.ts
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiClickCopyLink`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Click on "Copy Link" of post dropdown menu and verifies if the link is copied into the clipboard
|
||||
* Created user has an option to log in after all are setup.
|
||||
* @param {string} permalink - permalink to verify if copied into the clipboard
|
||||
*
|
||||
* @example
|
||||
* const permalink = 'http://localhost:8065/team-name/pl/post-id';
|
||||
* cy.uiClickCopyLink(permalink);
|
||||
*/
|
||||
uiClickCopyLink(permalink: string, postId: string): Chainable;
|
||||
|
||||
/**
|
||||
* Click dropdown menu of a post by post ID.
|
||||
* @param {String} postId - post ID
|
||||
* @param {String} menuItem - e.g. "Pin to channel"
|
||||
* @param {String} location - 'CENTER' (default), 'SEARCH', RHS_ROOT, RHS_COMMENT
|
||||
*/
|
||||
uiClickPostDropdownMenu(postId: string, menuItem: string, location?: string): Chainable;
|
||||
}
|
||||
}
|
||||
32
e2e-tests/cypress/tests/support/ui/post_dropdown_menu.js
Normal file
32
e2e-tests/cypress/tests/support/ui/post_dropdown_menu.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {stubClipboard} from '../../utils';
|
||||
|
||||
Cypress.Commands.add('uiClickCopyLink', (permalink, postId) => {
|
||||
stubClipboard().as('clipboard');
|
||||
|
||||
// * Verify initial state
|
||||
cy.get('@clipboard').its('contents').should('eq', '');
|
||||
|
||||
// # Click on "Copy Link"
|
||||
cy.get(`#CENTER_dropdown_${postId}`).should('be.visible').within(() => {
|
||||
cy.findByText('Copy Link').scrollIntoView().should('be.visible').click();
|
||||
|
||||
// * Verify if it's called with correct link value
|
||||
cy.get('@clipboard').its('wasCalled').should('eq', true);
|
||||
cy.get('@clipboard').its('contents').should('eq', permalink);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiClickPostDropdownMenu', (postId, menuItem, location = 'CENTER') => {
|
||||
cy.clickPostDotMenu(postId, location);
|
||||
cy.findAllByTestId(`unread_post_${postId}`).eq(0).should('be.visible');
|
||||
cy.findByText(menuItem).scrollIntoView().should('be.visible').click({force: true});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiPostDropdownMenuShortcut', (postId, menuText, shortcutKey, location = 'CENTER') => {
|
||||
cy.clickPostDotMenu(postId, location);
|
||||
cy.findByText(menuText).scrollIntoView().should('be.visible');
|
||||
cy.get('body').type(shortcutKey);
|
||||
});
|
||||
39
e2e-tests/cypress/tests/support/ui/search.ts
Normal file
39
e2e-tests/cypress/tests/support/ui/search.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {ChainableT} from '@/types';
|
||||
|
||||
function uiSearchPosts(searchTerm: string) {
|
||||
// # Enter the search terms and hit enter to start the search
|
||||
cy.get('#searchFormContainer').should('be.visible').click();
|
||||
cy.get('#searchBox').find('input').clear().type(searchTerm).type('{enter}');
|
||||
|
||||
// * Wait for the RHS to open and the search results to appear
|
||||
cy.contains('.sidebar--right__header', 'Search Results').should('be.visible');
|
||||
cy.get('#searchContainer .LoadingSpinner').should('not.exist');
|
||||
}
|
||||
|
||||
Cypress.Commands.add('uiSearchPosts', uiSearchPosts);
|
||||
|
||||
function uiJumpToSearchResult(postId: string) {
|
||||
// # Find the post in the search results and click Jump
|
||||
cy.get(`#searchResult_${postId}`).contains('a', 'Jump').click();
|
||||
|
||||
// * Verify the URL changes to the permalink URL
|
||||
cy.url().should((url) => url.endsWith(`/${postId}`));
|
||||
|
||||
// * Verify that the permalinked post is highlighted in the center channel
|
||||
cy.get(`#post_${postId}.post--highlight`).should('be.visible');
|
||||
}
|
||||
|
||||
Cypress.Commands.add('uiJumpToSearchResult', uiJumpToSearchResult);
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
uiSearchPosts(searchTerm: string): ChainableT<void>;
|
||||
uiJumpToSearchResult(postId: string): ChainableT<void>;
|
||||
}
|
||||
}
|
||||
}
|
||||
273
e2e-tests/cypress/tests/support/ui/sidebar_left.ts
Normal file
273
e2e-tests/cypress/tests/support/ui/sidebar_left.ts
Normal file
@@ -0,0 +1,273 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {ChainableT} from '../../types';
|
||||
|
||||
Cypress.Commands.add('uiGetLHS', () => {
|
||||
return cy.get('#SidebarContainer').should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetLHSHeader', () => {
|
||||
return cy.uiGetLHS().
|
||||
find('#sidebarTeamMenuButton').
|
||||
should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiOpenTeamMenu', (item = '') => {
|
||||
// # Click on LHS header
|
||||
cy.uiGetLHSHeader().click();
|
||||
|
||||
if (!item) {
|
||||
// # Return the menu if no item is passed
|
||||
return cy.uiGetLHSTeamMenu();
|
||||
}
|
||||
|
||||
// # Click on a particular item
|
||||
return cy.uiGetLHSTeamMenu().
|
||||
findByText(item).
|
||||
scrollIntoView().
|
||||
should('be.visible').
|
||||
click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetLHSAddChannelButton', () => {
|
||||
return cy.uiGetLHS().findByRole('button', {name: 'Browse or create channels'});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetLHSTeamMenu', () => {
|
||||
return cy.get('#sidebarTeamMenu');
|
||||
});
|
||||
|
||||
function uiOpenSystemConsoleMenu(item = ''): ChainableT<JQuery> {
|
||||
// # Click on LHS header button
|
||||
cy.uiGetSystemConsoleButton().click();
|
||||
|
||||
if (!item) {
|
||||
// # Return the menu if no item is passed
|
||||
return cy.uiGetSystemConsoleMenu();
|
||||
}
|
||||
|
||||
// # Click on a particular item
|
||||
return cy.uiGetSystemConsoleMenu().
|
||||
findByText(item).
|
||||
scrollIntoView().
|
||||
should('be.visible').
|
||||
click();
|
||||
}
|
||||
|
||||
Cypress.Commands.add('uiOpenSystemConsoleMenu', uiOpenSystemConsoleMenu);
|
||||
|
||||
function uiGetSystemConsoleButton(): ChainableT<JQuery> {
|
||||
return cy.get('.admin-sidebar').
|
||||
findByRole('button', {name: 'Menu Icon'});
|
||||
}
|
||||
|
||||
Cypress.Commands.add('uiGetSystemConsoleButton', uiGetSystemConsoleButton);
|
||||
|
||||
function uiGetSystemConsoleMenu(): ChainableT<JQuery> {
|
||||
return cy.get('.admin-sidebar').
|
||||
find('.dropdown-menu').
|
||||
should('be.visible');
|
||||
}
|
||||
|
||||
Cypress.Commands.add('uiGetSystemConsoleMenu', uiGetSystemConsoleMenu);
|
||||
|
||||
Cypress.Commands.add('uiGetLhsSection', (section) => {
|
||||
if (section === 'UNREADS') {
|
||||
return cy.findByText(section).
|
||||
parent().
|
||||
parent().
|
||||
parent();
|
||||
}
|
||||
|
||||
return cy.findAllByRole('button', {name: section}).
|
||||
first().
|
||||
parent().
|
||||
parent().
|
||||
parent();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiBrowseOrCreateChannel', (menuitem) => {
|
||||
cy.uiGetLHSAddChannelButton().should('be.visible').click();
|
||||
|
||||
cy.get('#browserOrAddChannelMenu').
|
||||
should('exist').and('be.visible').
|
||||
within(() => {
|
||||
if (menuitem) {
|
||||
cy.findByText(menuitem).should('exist').click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiAddDirectMessage', () => {
|
||||
return cy.findByRole('button', {name: 'Write a direct message'});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetFindChannels', () => {
|
||||
return cy.get('#lhsNavigator').findByRole('button', {name: 'Find Channels'});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiOpenFindChannels', () => {
|
||||
cy.uiGetFindChannels().click();
|
||||
});
|
||||
|
||||
function uiGetSidebarThreadsButton(): ChainableT<JQuery> {
|
||||
return cy.get('#sidebar-threads-button').should('be.visible');
|
||||
}
|
||||
Cypress.Commands.add('uiGetSidebarThreadsButton', uiGetSidebarThreadsButton);
|
||||
|
||||
Cypress.Commands.add('uiGetChannelSidebarMenu', (channelName, isChannelId = false) => {
|
||||
cy.uiGetLHS().within(() => {
|
||||
if (isChannelId) {
|
||||
cy.get(`#sidebarItem_${channelName}`).should('be.visible').find('button').should('exist').click({force: true});
|
||||
} else {
|
||||
cy.findByText(channelName).should('be.visible').parents('a').find('button').should('exist').click({force: true});
|
||||
}
|
||||
});
|
||||
|
||||
return cy.findByRole('menu', {name: 'Edit channel menu'}).should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiClickSidebarItem', (name) => {
|
||||
cy.uiGetSidebarItem(name).click({force: true});
|
||||
|
||||
if (name === 'threads') {
|
||||
cy.get('body').then((body) => {
|
||||
if (body.find('#genericModalLabel').length > 0) {
|
||||
cy.uiCloseModal('A new way to view and follow threads');
|
||||
}
|
||||
});
|
||||
cy.get('#tutorial-threads-mobile-header span.Button_label').contains('Followed threads');
|
||||
} else {
|
||||
cy.findAllByTestId('postView').last().scrollIntoView().should('be.visible');
|
||||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetSidebarItem', (channelName) => {
|
||||
return cy.get(`#sidebarItem_${channelName}`);
|
||||
});
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get LHS
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetLHS();
|
||||
*/
|
||||
uiGetLHS(): Chainable;
|
||||
|
||||
/**
|
||||
* Get LHS header
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetLHSHeader().click();
|
||||
*/
|
||||
uiGetLHSHeader(): Chainable;
|
||||
|
||||
/**
|
||||
* Open team menu
|
||||
*
|
||||
* @param {string} item - ex. 'Invite People', 'Team Settings', etc.
|
||||
*
|
||||
* @example
|
||||
* cy.uiOpenTeamMenu();
|
||||
*/
|
||||
uiOpenTeamMenu(item?: string): Chainable;
|
||||
|
||||
/**
|
||||
* Get LHS add channel button
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetLHSAddChannelButton().click();
|
||||
*/
|
||||
uiGetLHSAddChannelButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get LHS team menu
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetLHSTeamMenu().should('not.exist);
|
||||
*/
|
||||
uiGetLHSTeamMenu(): Chainable;
|
||||
|
||||
/**
|
||||
* Get LHS section
|
||||
* @param {string} section - section such as UNREADS, CHANNELS, FAVORITES, DIRECT MESSAGES and other custom category
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetLhsSection('CHANNELS');
|
||||
*/
|
||||
uiGetLhsSection(section: string): Chainable;
|
||||
|
||||
/**
|
||||
* Open menu to browse or create channel
|
||||
* @param {string} item - dropdown menu. If set, it will do click action.
|
||||
*
|
||||
* @example
|
||||
* cy.uiBrowseOrCreateChannel('Browse channels');
|
||||
*/
|
||||
uiBrowseOrCreateChannel(item: string): Chainable;
|
||||
|
||||
/**
|
||||
* Get "+" button to write a direct message
|
||||
* @example
|
||||
* cy.uiAddDirectMessage();
|
||||
*/
|
||||
uiAddDirectMessage(): Chainable;
|
||||
|
||||
/**
|
||||
* Get find channels button
|
||||
* @example
|
||||
* cy.uiGetFindChannels();
|
||||
*/
|
||||
uiGetFindChannels(): Chainable;
|
||||
|
||||
/**
|
||||
* Open find channels
|
||||
* @example
|
||||
* cy.uiOpenFindChannels();
|
||||
*/
|
||||
uiOpenFindChannels(): Chainable;
|
||||
|
||||
/**
|
||||
* Open menu of a channel in the sidebar
|
||||
* @param {string} channelName - name of channel, ex. 'town-square'
|
||||
* @param {boolean} isChannelId - default false. If true, it will use channel id instead of channel name
|
||||
* @example
|
||||
* cy.uiGetChannelSidebarMenu('Town Square');
|
||||
* cy.uiGetChannelSidebarMenu('user1212__user333', true);
|
||||
*/
|
||||
uiGetChannelSidebarMenu(channelName: string, isChannelId?: boolean): Chainable;
|
||||
|
||||
/**
|
||||
* Click sidebar item by channel or thread name
|
||||
* @param {string} name - channel name for channels, and threads for Global Threads
|
||||
*
|
||||
* @example
|
||||
* cy.uiClickSidebarItem('town-square');
|
||||
*/
|
||||
uiClickSidebarItem(name: string): Chainable;
|
||||
|
||||
/**
|
||||
* Get sidebar item by channel or thread name
|
||||
* @param {string} name - channel name for channels, and threads for Global Threads
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetSidebarItem('town-square').find('.badge').should('be.visible');
|
||||
*/
|
||||
uiGetSidebarItem(name: string): Chainable;
|
||||
|
||||
uiOpenSystemConsoleMenu: typeof uiOpenSystemConsoleMenu;
|
||||
|
||||
uiGetSystemConsoleButton: typeof uiGetSystemConsoleButton;
|
||||
|
||||
uiGetSystemConsoleMenu: typeof uiGetSystemConsoleMenu;
|
||||
|
||||
uiGetSidebarThreadsButton: typeof uiGetSidebarThreadsButton;
|
||||
}
|
||||
}
|
||||
}
|
||||
108
e2e-tests/cypress/tests/support/ui/sidebar_right.d.ts
vendored
Normal file
108
e2e-tests/cypress/tests/support/ui/sidebar_right.d.ts
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiGetRHS`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get RHS container
|
||||
*
|
||||
* @param {bool} option.visible - Set to false to check whether RHS is not visible. Otherwise, true (default) to check visibility.
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetRHS();
|
||||
*/
|
||||
uiGetRHS(option?: Record<string, boolean>): Chainable;
|
||||
|
||||
/**
|
||||
* Close RHS
|
||||
*
|
||||
* @example
|
||||
* cy.uiCloseRHS();
|
||||
*/
|
||||
uiCloseRHS(): Chainable;
|
||||
|
||||
/**
|
||||
* Expand RHS
|
||||
*
|
||||
* @example
|
||||
* cy.uiExpandRHS();
|
||||
*/
|
||||
uiExpandRHS(): Chainable;
|
||||
|
||||
/**
|
||||
* Verify if RHS is expanded
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetRHS().isExpanded();
|
||||
*/
|
||||
isExpanded(): Chainable;
|
||||
|
||||
/**
|
||||
* Get "Reply" button
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetReply();
|
||||
*/
|
||||
uiGetReply(): Chainable;
|
||||
|
||||
/**
|
||||
* Reply by clicking "Reply" button
|
||||
*
|
||||
* @example
|
||||
* cy.uiReply();
|
||||
*/
|
||||
uiReply(): Chainable;
|
||||
|
||||
/**
|
||||
* Get RHS container
|
||||
*
|
||||
* @param {bool} option.visible - Set to false to check whether Search container at RHS is not visible. Otherwise, true (default) to check visibility.
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetRHSSearchContainer();
|
||||
*/
|
||||
uiGetRHSSearchContainer(option: Record<string, boolean>): Chainable;
|
||||
|
||||
/**
|
||||
* Get file filter button from RHS.
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetFileFilterButton().click();
|
||||
*/
|
||||
uiGetFileFilterButton(): Chainable;
|
||||
|
||||
/**
|
||||
* Get file filter menu from RHS
|
||||
*
|
||||
* @param {bool} option.exist - Set to false to check whether file filter menu should not exist at RHS. Otherwise, true (default) to check visibility.
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetFileFilterMenu();
|
||||
*/
|
||||
uiGetFileFilterMenu(): Chainable;
|
||||
|
||||
/**
|
||||
* Open file filter menu from RHS
|
||||
* @param {string} item - such as `'Documents'`, `'Spreadsheets'`, `'Presentations'`, `'Code'`, `'Images'`, `'Audio'` and `'Videos'`.
|
||||
* @return the file filter menu
|
||||
*
|
||||
* @example
|
||||
* cy.uiOpenFileFilterMenu();
|
||||
*/
|
||||
uiOpenFileFilterMenu(): Chainable;
|
||||
}
|
||||
}
|
||||
79
e2e-tests/cypress/tests/support/ui/sidebar_right.js
Normal file
79
e2e-tests/cypress/tests/support/ui/sidebar_right.js
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
Cypress.Commands.add('uiGetRHS', (options = {visible: true}) => {
|
||||
if (options.visible) {
|
||||
return cy.get('#sidebar-right').should('be.visible');
|
||||
}
|
||||
|
||||
return cy.get('#sidebar-right').should('not.be.exist');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiCloseRHS', () => {
|
||||
cy.document().then((doc) => {
|
||||
const closeButton = doc.querySelector('[aria-label="Close Sidebar Icon"]');
|
||||
if (closeButton) {
|
||||
closeButton.click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiExpandRHS', () => {
|
||||
cy.findByLabelText('Expand Sidebar Icon').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('isExpanded', {prevSubject: true}, (subject) => {
|
||||
return cy.get(subject).should('have.class', 'sidebar--right--expanded');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetReply', () => {
|
||||
return cy.get('#sidebar-right').findByTestId('SendMessageButton');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiReply', () => {
|
||||
cy.uiGetReply().click();
|
||||
});
|
||||
|
||||
// Sidebar search container
|
||||
|
||||
Cypress.Commands.add('uiGetRHSSearchContainer', (options = {visible: true}) => {
|
||||
if (options.visible) {
|
||||
return cy.get('#searchContainer').should('be.visible');
|
||||
}
|
||||
|
||||
return cy.get('#searchContainer').should('not.exist');
|
||||
});
|
||||
|
||||
// Sidebar files search
|
||||
|
||||
Cypress.Commands.add('uiGetFileFilterButton', () => {
|
||||
return cy.get('.FilesFilterMenu').should('be.visible');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiGetFileFilterMenu', (option = {exist: true}) => {
|
||||
if (option.exist) {
|
||||
return cy.get('.FilesFilterMenu').
|
||||
find('.dropdown-menu').
|
||||
should('be.visible');
|
||||
}
|
||||
|
||||
return cy.get('.FilesFilterMenu').
|
||||
find('.dropdown-menu').
|
||||
should('not.exist');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiOpenFileFilterMenu', (item = '') => {
|
||||
// # Click on file filter button
|
||||
cy.uiGetFileFilterButton().click();
|
||||
|
||||
if (!item) {
|
||||
// # Return the menu if no item is passed
|
||||
return cy.uiGetFileFilterMenu();
|
||||
}
|
||||
|
||||
// # Click on a particular item
|
||||
return cy.uiGetFileFilterMenu().
|
||||
findByText(item).
|
||||
should('be.visible').
|
||||
click();
|
||||
});
|
||||
69
e2e-tests/cypress/tests/support/ui/suggestion_list.ts
Normal file
69
e2e-tests/cypress/tests/support/ui/suggestion_list.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {SimpleUser} from '@/integration/channels/autocomplete/helpers';
|
||||
import type {ChainableT} from '@/types';
|
||||
|
||||
/**
|
||||
* Verify user's at-mention in the suggestion list
|
||||
* @param {SimpleUser} user - user object
|
||||
* @param {boolean} isSelected - check if user is selected with false as default
|
||||
* @param {string} sectionDividerName - name of the section in suggestion list, ex. "Channel Members"
|
||||
*
|
||||
* @example
|
||||
* cy.uiVerifyAtMentionInSuggestionList(user, true, 'Channel Members');
|
||||
*/
|
||||
function uiVerifyAtMentionInSuggestionList(user: SimpleUser, isSelected = false, sectionDividerName: string | null = null): ChainableT {
|
||||
// * Verify that the suggestion list is open and visible
|
||||
return cy.get('#suggestionList').should('be.visible').within(() => {
|
||||
if (sectionDividerName) {
|
||||
// * Verify the section name is as expected
|
||||
cy.get('.suggestion-list__divider').findByText(sectionDividerName).should('be.visible');
|
||||
cy.get('.suggestion-list__divider').next().findByTestId(`mentionSuggestion_${user.username}`).should('be.visible');
|
||||
}
|
||||
|
||||
// * Verify that the user is selected
|
||||
return cy.uiVerifyAtMentionSuggestion(user, isSelected);
|
||||
});
|
||||
}
|
||||
Cypress.Commands.add('uiVerifyAtMentionInSuggestionList', uiVerifyAtMentionInSuggestionList);
|
||||
|
||||
/**
|
||||
* Verify user's at-mention suggestion
|
||||
* @param {SimpleUser} user - user object
|
||||
* @param {boolean} isSelected - check if user is selected with false as default
|
||||
*
|
||||
* @example
|
||||
* cy.uiVerifyAtMentionSuggestion(user, true);
|
||||
*/
|
||||
function uiVerifyAtMentionSuggestion(user: SimpleUser, isSelected = false): ChainableT {
|
||||
const {
|
||||
username,
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
nickname,
|
||||
} = user;
|
||||
|
||||
// * Verify that the user is selected
|
||||
cy.findByTestId(`mentionSuggestion_${username}`).as('selectedMentionSuggestion').should('be.visible');
|
||||
if (isSelected) {
|
||||
cy.get('@selectedMentionSuggestion').should('have.class', 'suggestion--selected');
|
||||
}
|
||||
|
||||
cy.get('@selectedMentionSuggestion').findByText(`@${username}`).should('be.visible');
|
||||
cy.get('@selectedMentionSuggestion').findByText(`${firstName} ${lastName} (${nickname})`).should('be.visible');
|
||||
|
||||
return cy.findByTestId(`mentionSuggestion_${username}`);
|
||||
}
|
||||
|
||||
Cypress.Commands.add('uiVerifyAtMentionSuggestion', uiVerifyAtMentionSuggestion);
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
uiVerifyAtMentionInSuggestionList: typeof uiVerifyAtMentionInSuggestionList;
|
||||
uiVerifyAtMentionSuggestion: typeof uiVerifyAtMentionSuggestion;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
e2e-tests/cypress/tests/support/ui/system.d.ts
vendored
Normal file
44
e2e-tests/cypress/tests/support/ui/system.d.ts
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiCheckLicenseExists`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Verify license exists via admin console.
|
||||
*
|
||||
* @example
|
||||
* cy.uiCheckLicenseExists();
|
||||
*/
|
||||
uiCheckLicenseExists(): Chainable;
|
||||
|
||||
/**
|
||||
* Reset system scheme permissions via System Console
|
||||
*
|
||||
* @example
|
||||
* cy.uiResetPermissionsToDefault();
|
||||
*/
|
||||
uiResetPermissionsToDefault(): Chainable;
|
||||
|
||||
/**
|
||||
* Save settings located in System Console
|
||||
*
|
||||
* @example
|
||||
* cy.uiSaveConfig();
|
||||
*/
|
||||
uiSaveConfig(): Chainable;
|
||||
}
|
||||
}
|
||||
40
e2e-tests/cypress/tests/support/ui/system.js
Normal file
40
e2e-tests/cypress/tests/support/ui/system.js
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import * as TIMEOUTS from '../../fixtures/timeouts';
|
||||
|
||||
Cypress.Commands.add('uiCheckLicenseExists', () => {
|
||||
// # Go to system admin then verify admin console URL, header, and content
|
||||
cy.visit('/admin_console/about/license');
|
||||
cy.url().should('include', '/admin_console/about/license');
|
||||
cy.get('.admin-console', {timeout: TIMEOUTS.HALF_MIN}).should('be.visible').within(() => {
|
||||
cy.get('.admin-console__header').should('be.visible').and('have.text', 'Edition and License');
|
||||
cy.get('.admin-console__content').should('be.visible').and('not.contain', 'undefined').and('not.contain', 'Invalid');
|
||||
cy.get('#remove-button').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiResetPermissionsToDefault', () => {
|
||||
// # Navigate to system scheme page
|
||||
cy.visit('/admin_console/user_management/permissions/system_scheme');
|
||||
|
||||
// # Click reset to defaults and confirm
|
||||
cy.findByTestId('resetPermissionsToDefault', {timeout: TIMEOUTS.HALF_MIN}).click();
|
||||
cy.get('#confirmModalButton').click();
|
||||
cy.uiSaveConfig();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('uiSaveConfig', ({confirm = true} = {}) => {
|
||||
// # Save settings
|
||||
cy.get('#saveSetting').should('be.enabled').click();
|
||||
cy.wait(TIMEOUTS.HALF_SEC);
|
||||
|
||||
if (confirm) {
|
||||
// # Wait until the UI shows the saving is done and revert the text to "Save"
|
||||
cy.waitUntil(() => cy.get('#saveSetting').then((el) => {
|
||||
return el[0].innerText === 'Save';
|
||||
}));
|
||||
} else {
|
||||
cy.wait(TIMEOUTS.HALF_SEC);
|
||||
}
|
||||
});
|
||||
42
e2e-tests/cypress/tests/support/ui/team.ts
Normal file
42
e2e-tests/cypress/tests/support/ui/team.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/**
|
||||
* Invites a member to the current team
|
||||
* @param {string} username - the username
|
||||
*/
|
||||
function uiInviteMemberToCurrentTeam(username: string) {
|
||||
// # Open member invite screen
|
||||
cy.uiOpenTeamMenu('Invite people');
|
||||
|
||||
// # Open members section if licensed for guest accounts
|
||||
cy.findByTestId('invitationModal').
|
||||
then((container) => container.find('[data-testid="inviteMembersLink"]')).
|
||||
then((link) => link?.click());
|
||||
|
||||
// # Enter bot username and submit
|
||||
cy.get('.users-emails-input__control input').typeWithForce(username).as('input');
|
||||
cy.get('.users-emails-input__option ').contains(`@${username}`);
|
||||
cy.get('@input').typeWithForce('{enter}');
|
||||
cy.findByTestId('inviteButton').click();
|
||||
|
||||
// * Verify user invited to team
|
||||
cy.get('.invitation-modal-confirm--sent .InviteResultRow').
|
||||
should('contain.text', `@${username}`).
|
||||
and('contain.text', 'This member has been added to the team.');
|
||||
|
||||
// # Close, return
|
||||
cy.findByTestId('confirm-done').click();
|
||||
}
|
||||
Cypress.Commands.add('uiInviteMemberToCurrentTeam', uiInviteMemberToCurrentTeam);
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
uiInviteMemberToCurrentTeam: typeof uiInviteMemberToCurrentTeam;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
30
e2e-tests/cypress/tests/support/ui/tooltip.d.ts
vendored
Normal file
30
e2e-tests/cypress/tests/support/ui/tooltip.d.ts
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `ui` prefix, e.g. `uiGetToolTip`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get tooltip
|
||||
*
|
||||
* @param {string} text of the tooltip
|
||||
*
|
||||
* @example
|
||||
* cy.uiGetToolTip('text');
|
||||
*/
|
||||
uiGetToolTip(text: string): Chainable;
|
||||
}
|
||||
}
|
||||
6
e2e-tests/cypress/tests/support/ui/tooltip.js
Normal file
6
e2e-tests/cypress/tests/support/ui/tooltip.js
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
Cypress.Commands.add('uiGetToolTip', (text) => {
|
||||
cy.findByRole('tooltip').should('exist').and('contain', text);
|
||||
});
|
||||
Reference in New Issue
Block a user