mirror of
https://github.com/hanzoai/team.git
synced 2026-10-30 14:49:35 +00:00
* chore(cypress): upgrade to v15.13 and to [email protected] * fix comments and update exposing cypress env
471 lines
22 KiB
JavaScript
471 lines
22 KiB
JavaScript
// 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');
|
||
}
|