Migrate Zephyr manual tests to Cypress E2E (#36971)

* MM-T1814: add 'Add a BOT to a team' Cypress E2E test

Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>

* MM-T1335: invite guests to public and private channels

Extend invitePeople() helper to accept multiple channels and add a spec
verifying both a public and a private channel are added to the guest
invite list.

Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>

* MM-T1340: verify guest invite email and join flow (not_cloud)

Add a non-cloud spec covering step 2 of MM-T1340: read the invitation
email, open the join link, complete guest signup, and verify the guest
is added to the team.

Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>

* MM-T1335: assert channel chips via icons and count

Verified against a licensed E2E server: assert exactly two channel chips
with public and private channel icons rather than an exact text match,
which failed due to extra text in the chip label.

Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>

* MM-T1340: accept terms and use valid password in guest signup

Verified against a licensed E2E server: the signup Create account button
is gated on a 14+ char password and the terms/privacy checkbox. Use
newTestPassword(), check the agreement, and assert the app loads.

Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
Maria A Nunez
2026-06-09 10:33:37 +08:00
committed by GitHub
co-authored by Cursor Agent
parent ffd4814940
commit f6e7e71695
4 changed files with 154 additions and 7 deletions
@@ -30,6 +30,21 @@ describe('Managing bots in Teams and Channels', () => {
});
});
it('MM-T1814 Add a BOT to a team', () => {
cy.makeClient().then(async (client) => {
// # Go to channel
const channel = await client.getChannelByName(team.id, 'town-square');
cy.visit(`/${team.name}/channels/${channel.name}`);
// # Invite bot to team (asserts the modal success message)
const bot = await client.createBot(createBotPatch());
cy.uiInviteMemberToCurrentTeam(bot.username);
// * Verify system message in-channel
cy.uiWaitUntilMessagePostedIncludes(`@${bot.username} added to the team by you.`);
});
});
it('MM-T1815 Add a BOT to a team that has email restricted', () => {
cy.makeClient().then(async (client) => {
// # Go to channel
@@ -0,0 +1,106 @@
// 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 @enterprise @guest_account
/**
* Note: This test requires Enterprise license to be uploaded and an SMTP server
* (email retrieval), so it is restricted to non-cloud editions.
*/
import {UserProfile} from '@mattermost/types/users';
import {
changeGuestFeatureSettings,
invitePeople,
verifyInvitationSuccess,
} from './helpers';
import * as TIMEOUTS from '@/fixtures/timeouts';
import {
getJoinEmailTemplate,
getRandomId,
newTestPassword,
reUrl,
verifyEmailBody,
} from '@/utils';
describe('Guest Account - Guest User Invitation Email Flow', () => {
let sysadmin: Cypress.UserProfile;
let testTeam: Cypress.Team;
before(() => {
cy.shouldNotRunOnCloudEdition();
// * Check if server has license for Guest Accounts
cy.apiRequireLicenseForFeature('GuestAccounts');
});
beforeEach(() => {
// # Login as sysadmin
cy.apiAdminLogin().then(({user}: {user: UserProfile}) => {
sysadmin = user;
});
// # Reset Guest Feature settings (Guest Accounts + Email Invitations enabled)
changeGuestFeatureSettings();
cy.apiInitSetup().then(({team}) => {
testTeam = team;
// # Go to town square
cy.visit(`/${team.name}/channels/town-square`);
});
});
it('MM-T1340 Invite Guests - New User not in the system - join via email', () => {
const username = `g${getRandomId()}`; // username has to start with a letter
const email = `${username}@sample.mattermost.com`;
// # Search and add a new guest by email, who is not part of the team
invitePeople(email, 1, email);
// * Verify the invitation was sent (step 1)
verifyInvitationSuccess(email, testTeam, 'An invitation email has been sent.');
// # Open the invitation email and extract the join link (step 2)
cy.getRecentEmail({username, email}).then((data) => {
const {body: actualEmailBody, subject} = data;
// * Verify the email subject is about joining the team as a guest
expect(subject).to.contain(`${sysadmin.username} invited you to join the team ${testTeam.display_name} as a guest`);
// * Verify the email body matches the expected guest invite template
const expectedEmailBody = getJoinEmailTemplate(sysadmin.username, email, testTeam, true);
verifyEmailBody(expectedEmailBody, actualEmailBody);
// # Extract invitation link from the invitation email
const invitationLink = actualEmailBody[3].match(reUrl)[0];
// # Logout as sysadmin and open the invitation link ("Join now")
cy.apiLogout();
cy.visit(invitationLink);
});
// # Create the guest account with email and password (email is pre-filled from the invite)
cy.get('#input_name').type(username);
cy.get('#input_password-input').type(newTestPassword());
// # Agree to the terms and privacy policy, then create the account
cy.findByRole('checkbox', {name: 'Terms and privacy policy checkbox'}).check({force: true});
cy.findByText('Create account').click();
// * Verify the guest is taken into the app and added to the team upon successful signup
cy.get('#SidebarContainer', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
// * Verify a system message indicates the guest has joined
cy.uiWaitUntilMessagePostedIncludes(username);
});
});
@@ -52,6 +52,29 @@ describe('Guest Account - Guest User Invitation Flow', () => {
});
});
it('MM-T1335 Invite Guests - Add Public and Private channels', () => {
// # Create a private channel in the test team
const privateChannelDisplayName = `Private ${getRandomId()}`;
cy.apiCreateChannel(testTeam.id, `private-${getRandomId()}`, privateChannelDisplayName, 'P').then(() => {
// # Invite a new guest by email, adding both a public and a private channel
const email = `temp-${getRandomId()}@mattermost.com`;
invitePeople(email, 1, email, ['Town Square', privateChannelDisplayName], false);
// * Verify both channels are added to the list of channels the guest will be added to
cy.get('.channels-input__control').should('be.visible').within(() => {
cy.get('.channels-input__multi-value').should('have.length', 2);
// * Verify the public channel (Town Square) is added
cy.findByText('Town Square').should('be.visible');
cy.get('.public-channel-icon').should('be.visible');
// * Verify the private channel is added
cy.contains('.channels-input__multi-value', privateChannelDisplayName).should('be.visible');
cy.get('.private-channel-icon').should('be.visible');
});
});
});
it('MM-T4451 Verify UI Elements of Guest User Invitation Flow', () => {
// # Open team menu and click 'Invite People'
cy.uiOpenTeamMenu('Invite people');
@@ -14,7 +14,7 @@ export function changeGuestFeatureSettings(featureFlag = true, emailInvitation =
});
}
export function invitePeople(typeText: string, resultsCount: number, verifyText: string, channelName = 'Town Square', clickInvite = true) {
export function invitePeople(typeText: string, resultsCount: number, verifyText: string, channelName: string | string[] = 'Town Square', clickInvite = true) {
// # Open team menu and click 'Invite People'
cy.uiOpenTeamMenu('Invite people');
@@ -28,13 +28,16 @@ export function invitePeople(typeText: string, resultsCount: number, verifyText:
cy.get('.users-emails-input__menu').
children().should('have.length', resultsCount).eq(0).should('contain', verifyText).click();
// # Search and add a Channel
cy.get('.channels-input__control').should('be.visible').within(() => {
cy.get('input').typeWithForce(channelName);
// # Search and add one or more channels
const channelNames = Array.isArray(channelName) ? channelName : [channelName];
channelNames.forEach((name) => {
cy.get('.channels-input__control').should('be.visible').within(() => {
cy.get('input').typeWithForce(name);
});
cy.get('.channels-input__menu').
children().should('have.length', 1).
eq(0).should('contain', name).click();
});
cy.get('.channels-input__menu').
children().should('have.length', 1).
eq(0).should('contain', channelName).click();
if (clickInvite) {
// # Click Invite Guests Button