Files
sabrilandGitHub 23af2531fa SEC-10098 E2E/Cypress chore(cypress): upgrade to v15.13 and to [email protected] (#36091)
* chore(cypress): upgrade to v15.13 and to [email protected]

* fix comments and update exposing cypress env
2026-04-16 13:35:26 +08:00

471 lines
22 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @not_cloud @interactive_dialog
/**
* Note: This test requires webhook server running. Initiate `npm run start:webhook` to start.
*/
import * as TIMEOUTS from '@/fixtures/timeouts';
const webhookUtils = require('../../../../utils/webhook_utils');
let createdCommandWithDefaults;
let createdCommandClean;
let multiSelectDialogWithDefaults;
let multiSelectDialogClean;
describe('Interactive Dialog - Multiselect', () => {
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.requireWebhookServer();
// # Ensure that teammate name display setting is set to default 'username'
cy.apiSaveTeammateNameDisplayPreference('username');
// # Create new team and create commands on it
cy.apiCreateTeam('test-team', 'Test Team').then(({team}) => {
cy.visit(`/${team.name}`);
const webhookBaseUrl = Cypress.expose().webhookBaseUrl;
// Create command for tests with defaults
const commandWithDefaults = {
auto_complete: false,
description: 'Test for multiselect dialog elements with defaults',
display_name: 'Multiselect Dialog Test (Defaults)',
icon_url: '',
method: 'P',
team_id: team.id,
trigger: 'multiselect_dialog_defaults',
url: `${webhookBaseUrl}/multiselect_dialog_request?includeDefaults=true`,
username: '',
};
// Create command for clean tests (no defaults)
const commandClean = {
auto_complete: false,
description: 'Test for multiselect dialog elements (clean)',
display_name: 'Multiselect Dialog Test (Clean)',
icon_url: '',
method: 'P',
team_id: team.id,
trigger: 'multiselect_dialog_clean',
url: `${webhookBaseUrl}/multiselect_dialog_request?includeDefaults=false`,
username: '',
};
cy.apiCreateCommand(commandWithDefaults).then(({data}) => {
createdCommandWithDefaults = data;
multiSelectDialogWithDefaults = webhookUtils.getMultiSelectDialog(createdCommandWithDefaults.id, webhookBaseUrl, true);
});
cy.apiCreateCommand(commandClean).then(({data}) => {
createdCommandClean = data;
multiSelectDialogClean = webhookUtils.getMultiSelectDialog(createdCommandClean.id, webhookBaseUrl, false);
});
});
});
afterEach(() => {
// # Reload current page after each test to close any dialogs left open
cy.reload();
});
it('MM-T2510A - Multiselect default values verification', () => {
// # Post a slash command (with defaults)
cy.postMessage(`/${createdCommandWithDefaults.trigger} `);
// * Verify that the apps form modal opens up
cy.get('#appsModal').should('be.visible').within(() => {
// * Verify that the header contains correct title
cy.get('.modal-header').should('be.visible').within(() => {
cy.get('#appsModalLabel').should('be.visible').and('have.text', multiSelectDialogWithDefaults.dialog.title);
});
// * Verify default values are preselected correctly
cy.get('.modal-body').should('be.visible').children('.form-group').each(($elForm, index) => {
const element = multiSelectDialogWithDefaults.dialog.elements[index];
if (!element) {
return;
}
cy.wrap($elForm).within(() => {
if (element.name === 'multiselect_options') {
// * Verify multiselect options have default values (opt1,opt3 = Engineering, Marketing)
cy.get('.react-select__multi-value').should('have.length', 2);
cy.get('.react-select__multi-value').eq(0).should('contain', 'Engineering');
cy.get('.react-select__multi-value').eq(1).should('contain', 'Marketing');
} else if (element.name === 'multiselect_users') {
// * Verify multiselect users field has no default values
cy.get('.react-select__multi-value').should('not.exist');
} else if (element.name === 'single_select_options') {
// * Verify single select has default value (single2 = Single Option 2)
cy.get('.react-select__single-value').should('contain', 'Single Option 2');
}
});
});
closeAppsFormModal();
});
});
it('MM-T2510B - Multiselect UI and functionality (clean)', () => {
// # Post a slash command (clean, no defaults)
cy.postMessage(`/${createdCommandClean.trigger} `);
// * Verify that the apps form modal opens up
cy.get('#appsModal').should('be.visible').within(() => {
// * Verify that the header of modal contains icon URL, title and close button
cy.get('.modal-header').should('be.visible').within(($elForm) => {
cy.get('#appsModalIconUrl').should('be.visible').and('have.attr', 'src').and('not.be.empty');
cy.get('#appsModalLabel').should('be.visible').and('have.text', multiSelectDialogClean.dialog.title);
cy.wrap($elForm).find('button.close').should('be.visible').and('contain', '×').and('contain', 'Close');
});
// * Verify that the body contains all multiselect elements
cy.get('.modal-body').should('be.visible').children('.form-group').should('have.length', 3).each(($elForm, index) => {
const element = multiSelectDialogClean.dialog.elements[index];
// Skip if element is undefined (more DOM elements than expected)
if (!element) {
return;
}
cy.wrap($elForm).within(() => {
// Verify the label text includes display name
cy.get('label').first().scrollIntoView().should('be.visible').and('contain', element.display_name);
if (element.name === 'multiselect_options') {
// * Verify multiselect options field starts empty
cy.get('[id^=\'MultiInput_\']').should('be.visible');
cy.get('.react-select__multi-value').should('not.exist');
// * Test adding multiple options
cy.get('[id^=\'MultiInput_\']').click();
cy.document().then((doc) => {
cy.wrap(doc).find('.react-select__option').contains('Engineering').click();
});
cy.get('[id^=\'MultiInput_\']').click();
cy.document().then((doc) => {
cy.wrap(doc).find('.react-select__option').contains('Sales').click();
});
// * Verify two options are now selected
cy.get('.react-select__multi-value').should('have.length', 2);
cy.get('.react-select__multi-value').eq(0).should('contain', 'Engineering');
cy.get('.react-select__multi-value').eq(1).should('contain', 'Sales');
// * Test removing an option
cy.get('.react-select__multi-value').eq(0).find('.react-select__multi-value__remove').click();
// * Verify only one option remains
cy.get('.react-select__multi-value').should('have.length', 1);
cy.get('.react-select__multi-value').eq(0).should('contain', 'Sales');
} else if (element.name === 'multiselect_users') {
// * Verify multiselect users field starts empty
cy.get('[id^=\'MultiInput_\']').should('be.visible');
cy.get('.react-select__multi-value').should('not.exist');
// * Test selecting multiple users
cy.get('[id^=\'MultiInput_\']').click();
cy.document().then((doc) => {
cy.wrap(doc).find('.react-select__option').should('have.length.at.least', 1);
cy.wrap(doc).find('.react-select__option').first().click();
});
cy.get('[id^=\'MultiInput_\']').click();
cy.document().then((doc) => {
cy.wrap(doc).find('.react-select__option').eq(1).click();
});
// * Verify two users are selected
cy.get('.react-select__multi-value').should('have.length', 2);
} else if (element.name === 'single_select_options') {
// * Verify single select field starts empty
cy.get('[id^=\'MultiInput_\']').should('be.visible');
// * Test selecting a single option
cy.get('[id^=\'MultiInput_\']').click();
cy.document().then((doc) => {
cy.wrap(doc).find('.react-select__option').contains('Single Option 3').click();
});
// * Verify single value was selected
cy.get('.react-select__single-value').should('contain', 'Single Option 3');
}
// * Verify help text if present
if (element.help_text) {
cy.get('.help-text').should('be.visible').and('contain', element.help_text);
}
});
});
// * Verify that the footer contains cancel and submit buttons
cy.get('.modal-footer').should('be.visible').within(($elForm) => {
cy.wrap($elForm).find('#appsModalCancel').should('be.visible').and('have.text', 'Cancel');
cy.wrap($elForm).find('#appsModalSubmit').should('be.visible').and('have.text', multiSelectDialogClean.dialog.submit_label);
});
closeAppsFormModal();
});
});
it('MM-T2511A - Multiselect form submission with defaults', () => {
// # Post a slash command (with defaults)
cy.postMessage(`/${createdCommandWithDefaults.trigger} `);
// * Verify that the apps form modal opens up
cy.get('#appsModal').should('be.visible').within(() => {
// # Select additional option in the multiselect field (defaults already present)
cy.get('.form-group').eq(0).within(() => {
// Keep default selections and add one more
cy.get('[id^=\'MultiInput_\']').click();
});
cy.document().then((doc) => {
cy.wrap(doc).find('.react-select__option').contains('Support').click();
});
// # Select multiple users
cy.get('.form-group').eq(1).within(() => {
cy.get('[id^=\'MultiInput_\']').click();
});
cy.document().then((doc) => {
cy.wrap(doc).find('.react-select__option').first().click();
});
cy.get('.form-group').eq(1).within(() => {
cy.get('[id^=\'MultiInput_\']').click();
});
cy.document().then((doc) => {
cy.wrap(doc).find('.react-select__option').eq(1).click();
});
// # Submit the form
cy.intercept('/api/v4/actions/dialogs/submit').as('submitAction');
cy.get('#appsModalSubmit').click();
});
// * Verify that the apps form modal is closed
cy.get('#appsModal').should('not.exist');
// * Verify that submitted values contain array values for multiselect
cy.wait('@submitAction').should('include.all.keys', ['request', 'response']).then((result) => {
const {submission} = result.request.body;
// * Verify multiselect options submitted as array with defaults + added option
expect(submission.multiselect_options).to.be.an('array');
expect(submission.multiselect_options).to.include.members(['opt1', 'opt3', 'opt4']); // Engineering, Marketing, Support
// * Verify multiselect users submitted as array
expect(submission.multiselect_users).to.be.an('array');
expect(submission.multiselect_users).to.have.length(2);
// * Verify single select submitted as string with default value
expect(submission.single_select_options).to.be.a('string');
expect(submission.single_select_options).to.equal('single2'); // Default value
});
// * Verify success message
cy.getLastPost().should('contain', 'Dialog submitted');
});
it('MM-T2511B - Multiselect form submission (clean)', () => {
// # Post a slash command (clean, no defaults)
cy.postMessage(`/${createdCommandClean.trigger} `);
// * Verify that the apps form modal opens up
cy.get('#appsModal').should('be.visible').within(() => {
// # Select multiple options in the multiselect field (starting from empty)
cy.get('.form-group').eq(0).within(() => {
cy.get('[id^=\'MultiInput_\']').click();
});
cy.document().then((doc) => {
cy.wrap(doc).find('.react-select__option').contains('Engineering').click();
});
cy.get('.form-group').eq(0).within(() => {
cy.get('[id^=\'MultiInput_\']').click();
});
cy.document().then((doc) => {
cy.wrap(doc).find('.react-select__option').contains('Sales').click();
});
// # Select multiple users
cy.get('.form-group').eq(1).within(() => {
cy.get('[id^=\'MultiInput_\']').click();
});
cy.document().then((doc) => {
cy.wrap(doc).find('.react-select__option').first().click();
});
cy.get('.form-group').eq(1).within(() => {
cy.get('[id^=\'MultiInput_\']').click();
});
cy.document().then((doc) => {
cy.wrap(doc).find('.react-select__option').eq(1).click();
});
// # Select single option
cy.get('.form-group').eq(2).within(() => {
cy.get('[id^=\'MultiInput_\']').click();
});
cy.document().then((doc) => {
cy.wrap(doc).find('.react-select__option').contains('Single Option 1').click();
});
// # Submit the form
cy.intercept('/api/v4/actions/dialogs/submit').as('submitAction');
cy.get('#appsModalSubmit').click();
});
// * Verify that the apps form modal is closed
cy.get('#appsModal').should('not.exist');
// * Verify that submitted values contain array values for multiselect
cy.wait('@submitAction').should('include.all.keys', ['request', 'response']).then((result) => {
const {submission} = result.request.body;
// * Verify multiselect options submitted as array
expect(submission.multiselect_options).to.be.an('array');
expect(submission.multiselect_options).to.include.members(['opt1', 'opt2']); // Engineering, Sales
// * Verify multiselect users submitted as array
expect(submission.multiselect_users).to.be.an('array');
expect(submission.multiselect_users).to.have.length(2);
// * Verify single select submitted as string
expect(submission.single_select_options).to.be.a('string');
expect(submission.single_select_options).to.equal('single1'); // Selected value
});
// * Verify success message
cy.getLastPost().should('contain', 'Dialog submitted');
});
it('MM-T2512 - Multiselect validation error handling', () => {
// # Post a slash command (clean, no defaults)
cy.postMessage(`/${createdCommandClean.trigger} `);
// * Verify that the apps form modal opens up
cy.get('#appsModal').should('be.visible').within(() => {
// # Required multiselect field (multiselect_options) starts empty in clean dialog
cy.get('.form-group').eq(0).within(() => {
// Verify the field shows required indicator (*) or field name
cy.get('label').should('contain', 'Multi Option Selector');
// Verify field starts empty (no clearing needed in clean dialog)
cy.get('.react-select__multi-value').should('not.exist');
});
cy.wait(TIMEOUTS.HALF_SEC); // Wait for potential validation to appear
// # Try to submit the form with empty required multiselect
cy.get('#appsModalSubmit').click();
});
// * Verify that the apps form modal is still open (validation failed)
cy.get('#appsModal').should('be.visible');
// * Verify error message appears for required field
cy.get('#appsModal').within(() => {
cy.get('.form-group').eq(0).within(() => {
cy.get('.error-text').should('be.visible').and('contain', 'This field is required');
});
});
closeAppsFormModal();
});
it('MM-T2513 - Multiselect with keyboard navigation', () => {
// # Post a slash command (clean, no defaults)
cy.postMessage(`/${createdCommandClean.trigger} `);
// * Verify that the apps form modal opens up
cy.get('#appsModal').should('be.visible').within(() => {
// # Test keyboard navigation in multiselect (starts clean, no defaults)
cy.get('.form-group').eq(0).within(() => {
// First selection: Use keyboard to select Sales option
cy.get('[id^=\'MultiInput_\']').click().type('{downarrow}{downarrow}{enter}'); // Select Sales option
// * Verify Sales was added
cy.get('.react-select__multi-value').should('have.length', 1);
cy.get('.react-select__multi-value').should('contain', 'Sales');
// # Test typing to filter options (dropdown closes after selection, so click again)
cy.get('[id^=\'MultiInput_\']').click().type('Prod{enter}'); // Type "Prod" to filter and select Product
// * Verify Product was added
cy.get('.react-select__multi-value').should('have.length', 2);
cy.get('.react-select__multi-value').should('contain', 'Product');
cy.wait(TIMEOUTS.HALF_SEC); // Wait for potential dropdown rendering
// # Test removing option (click remove button)
cy.get('.react-select__multi-value').contains('Product').parent().within(() => {
cy.get('.react-select__multi-value__remove').click();
});
// * Verify Product was removed
cy.get('.react-select__multi-value').should('have.length', 1);
cy.get('.react-select__multi-value').should('not.contain', 'Product');
});
});
// * Verify dropdown is closed (no options visible)
cy.get('body').then(($body) => {
if ($body.find('.react-select__menu').length > 0) {
cy.get('.react-select__menu').should('not.be.visible');
}
});
closeAppsFormModal();
});
it('MM-T2514 - Multiselect accessibility check', () => {
// # Post a slash command (clean, no defaults)
cy.postMessage(`/${createdCommandClean.trigger} `);
// * Verify that the apps form modal opens up
cy.get('#appsModal').should('be.visible').within(() => {
// * Verify multiselect has basic accessibility elements (uses clean dialog)
cy.get('.form-group').eq(0).within(() => {
// * Verify label exists and is visible
cy.get('label').should('be.visible').and('contain', 'Multi Option Selector');
// * Verify multiselect input is present and accessible
cy.get('[id^=\'MultiInput_\']').should('be.visible');
// * Verify help text exists and is visible
cy.get('.help-text').should('be.visible').and('contain', 'You can select multiple options');
// * Test basic interaction accessibility - click to open/close
cy.get('[id^=\'MultiInput_\']').click();
// * Verify dropdown opens (options become available)
cy.document().then((doc) => {
cy.wrap(doc).find('.react-select__option').should('have.length.at.least', 1);
});
});
// * Close dropdown by clicking elsewhere in the modal (force since dropdown options may cover the area)
cy.get('.modal-body').click({force: true});
});
closeAppsFormModal();
});
});
function closeAppsFormModal() {
cy.get('.modal-header').should('be.visible').within(($elForm) => {
cy.wrap($elForm).find('button.close').should('be.visible').click();
});
cy.get('#appsModal').should('not.exist');
}