Move /e2e -> /e2e-tests
Этот коммит содержится в:
@@ -0,0 +1,181 @@
|
||||
// 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 @collapsed_reply_threads
|
||||
|
||||
import {spyNotificationAs} from '../../../support/notification';
|
||||
|
||||
describe('CRT Desktop notifications', () => {
|
||||
let testTeam;
|
||||
let testChannelUrl;
|
||||
let testChannelId;
|
||||
let testChannelName;
|
||||
let receiver;
|
||||
let sender;
|
||||
|
||||
before(() => {
|
||||
cy.apiUpdateConfig({
|
||||
ServiceSettings: {
|
||||
ThreadAutoFollow: true,
|
||||
CollapsedThreads: 'default_on',
|
||||
},
|
||||
});
|
||||
|
||||
cy.apiCreateUser().then(({user}) => {
|
||||
sender = user;
|
||||
});
|
||||
|
||||
cy.apiInitSetup().then(({team, channel, user, channelUrl}) => {
|
||||
testTeam = team;
|
||||
receiver = user;
|
||||
testChannelUrl = channelUrl;
|
||||
testChannelId = channel.id;
|
||||
testChannelName = channel.display_name;
|
||||
|
||||
cy.apiAddUserToTeam(testTeam.id, sender.id).then(() => {
|
||||
cy.apiAddUserToChannel(testChannelId, sender.id);
|
||||
});
|
||||
|
||||
// # Login as receiver
|
||||
cy.apiLogin(receiver);
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T4417_1 Trigger notifications on all replies when channel setting is checked', () => {
|
||||
// # Visit channel
|
||||
cy.visit(testChannelUrl);
|
||||
|
||||
// Setup notification spy
|
||||
spyNotificationAs('notifySpy', 'granted');
|
||||
|
||||
// # Set users notification settings
|
||||
setCRTDesktopNotification('ALL');
|
||||
|
||||
// # Post a root message as other user
|
||||
cy.postMessageAs({sender, message: 'This is a not followed root message', channelId: testChannelId, rootId: ''}).then(({id: postId}) => {
|
||||
// # Switch to town-square so that unread notifications in test channel may be triggered
|
||||
cy.uiClickSidebarItem('town-square');
|
||||
|
||||
// # Post a message in unfollowed thread as another user
|
||||
cy.postMessageAs({sender, message: 'This is a reply to the unfollowed thread', channelId: testChannelId, rootId: postId});
|
||||
|
||||
// * Verify stub was not called for unfollowed thread
|
||||
cy.get('@notifySpy').should('not.be.called');
|
||||
});
|
||||
|
||||
// # Visit channel
|
||||
cy.visit(testChannelUrl);
|
||||
|
||||
// Setup notification spy
|
||||
spyNotificationAs('notifySpy', 'granted');
|
||||
|
||||
// # Post a message
|
||||
cy.postMessage('Hi there, this is a root message');
|
||||
|
||||
// # Get post id of message
|
||||
cy.getLastPostId().then((postId) => {
|
||||
// # Switch to town-square so that unread notifications in test channel may be triggered
|
||||
cy.uiClickSidebarItem('town-square');
|
||||
|
||||
// # Post a message in original thread as another user
|
||||
const message = 'This is a reply to the root post';
|
||||
cy.postMessageAs({sender, message, channelId: testChannelId, rootId: postId});
|
||||
|
||||
// * Verify stub was called with correct title and body
|
||||
cy.get('@notifySpy').should('have.been.calledWithMatch', `Reply in ${testChannelName}`, (args) => {
|
||||
expect(args.body, `Notification body: "${args.body}" should match: "${message}"`).to.equal(`@${sender.username}: ${message}`);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T4417_2 Trigger notifications only on mention replies when channel setting is unchecked', () => {
|
||||
cy.visit(testChannelUrl);
|
||||
|
||||
// Setup notification spy
|
||||
spyNotificationAs('notifySpy', 'granted');
|
||||
|
||||
// # Set users notification settings
|
||||
setCRTDesktopNotification('MENTION');
|
||||
|
||||
// # Post a root message as other user
|
||||
cy.postMessageAs({sender, message: 'This is a not followed root message', channelId: testChannelId, rootId: ''}).then(({id: postId}) => {
|
||||
// # Switch to town-square so that unread notifications in test channel may be triggered
|
||||
cy.uiClickSidebarItem('town-square');
|
||||
|
||||
// # Post a message in unfollowed thread as another user
|
||||
cy.postMessageAs({sender, message: 'This is a reply to the unfollowed thread', channelId: testChannelId, rootId: postId});
|
||||
|
||||
// * Verify stub was not called for unfollowed thread
|
||||
cy.get('@notifySpy').should('not.be.called');
|
||||
});
|
||||
|
||||
// # Visit channel
|
||||
cy.visit(testChannelUrl);
|
||||
|
||||
// Setup notification spy
|
||||
spyNotificationAs('notifySpy', 'granted');
|
||||
|
||||
// # Post a message
|
||||
cy.postMessage('Hi there, this is a root message');
|
||||
|
||||
// # Get post id of message
|
||||
cy.getLastPostId().then((postId) => {
|
||||
// # Switch to town-square so that unread notifications in test channel may be triggered
|
||||
cy.uiClickSidebarItem('town-square');
|
||||
|
||||
// # Post a message in original thread as another user
|
||||
cy.postMessageAs({sender, message: 'This is a reply to the root post', channelId: testChannelId, rootId: postId});
|
||||
|
||||
// * Verify stub was not called
|
||||
cy.get('@notifySpy').should('not.be.called');
|
||||
|
||||
// # Post a mention message in original thread as another user
|
||||
const message = `@${receiver.username} this is a mention to receiver`;
|
||||
|
||||
cy.postMessageAs({sender, message, channelId: testChannelId, rootId: postId});
|
||||
|
||||
// * Verify stub was called with correct title and body
|
||||
cy.get('@notifySpy').should('have.been.calledWithMatch', `Reply in ${testChannelName}`, (args) => {
|
||||
expect(args.body, `Notification body: "${args.body}" should match: "${message}"`).to.equal(`@${sender.username}: ${message}`);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function setCRTDesktopNotification(type) {
|
||||
if (['ALL', 'MENTION'].indexOf(type) === -1) {
|
||||
throw new Error(`${type} is invalid`);
|
||||
}
|
||||
|
||||
// # Open settings modal
|
||||
cy.uiOpenChannelMenu('Notification Preferences');
|
||||
|
||||
// # Click "Desktop Notifications"
|
||||
cy.get('#desktopTitle').
|
||||
scrollIntoView().
|
||||
should('be.visible').
|
||||
and('contain', 'Send desktop notifications').click();
|
||||
|
||||
// # Select mentions category for messages.
|
||||
cy.get('#channelNotificationMentions').scrollIntoView().check();
|
||||
|
||||
if (type === 'ALL') {
|
||||
// # Check notify for all replies.
|
||||
cy.get('#desktopThreadsNotificationAllActivity').scrollIntoView().check().should('be.checked');
|
||||
} else if (type === 'MENTION') {
|
||||
// # Check notify only for mentions.
|
||||
cy.get('#desktopThreadsNotificationAllActivity').scrollIntoView().uncheck().should('not.be.checked');
|
||||
}
|
||||
|
||||
// # Click "Save" and close the modal
|
||||
cy.uiSaveAndClose();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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 @collapsed_reply_threads
|
||||
|
||||
describe('Collapsed Reply Threads', () => {
|
||||
let testTeam;
|
||||
|
||||
before(() => {
|
||||
cy.apiUpdateConfig({
|
||||
ServiceSettings: {
|
||||
ThreadAutoFollow: true,
|
||||
CollapsedThreads: 'default_off',
|
||||
},
|
||||
});
|
||||
|
||||
// # Create new channel and visit channel
|
||||
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
|
||||
testTeam = team;
|
||||
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T4140 should be able to toggle CRT on/off', () => {
|
||||
// # Set CRT to off
|
||||
cy.uiChangeCRTDisplaySetting('OFF');
|
||||
|
||||
// * Threads menu in sidebar should not be visible
|
||||
cy.get('.SidebarGlobalThreads').should('not.exist');
|
||||
|
||||
// # Set CRT to on
|
||||
cy.uiChangeCRTDisplaySetting('ON');
|
||||
|
||||
// * Threads menu in sidebar should be visible
|
||||
cy.get('.SidebarGlobalThreads').should('exist');
|
||||
|
||||
// # Visit global threads
|
||||
cy.visit(`/${testTeam.name}/threads`);
|
||||
|
||||
// * should see followed threads in H2 title
|
||||
cy.get('h2').should('have.text', 'Followed threads');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,389 @@
|
||||
// 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 @collapsed_reply_threads
|
||||
|
||||
describe('Collapsed Reply Threads', () => {
|
||||
let testTeam;
|
||||
let otherUser;
|
||||
let testChannel;
|
||||
let rootPost;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.apiUpdateConfig({
|
||||
ServiceSettings: {
|
||||
ThreadAutoFollow: true,
|
||||
CollapsedThreads: 'default_off',
|
||||
},
|
||||
});
|
||||
|
||||
// # Log in as user that hasn't had CRT enabled before
|
||||
cy.apiInitSetup({loginAfter: true, promoteNewUserAsAdmin: true, userPrefix: 'tipbutton'}).then(({team, channel}) => {
|
||||
testTeam = team;
|
||||
testChannel = channel;
|
||||
|
||||
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
|
||||
otherUser = user1;
|
||||
|
||||
cy.apiAddUserToTeam(testTeam.id, otherUser.id).then(() => {
|
||||
cy.apiAddUserToChannel(testChannel.id, otherUser.id);
|
||||
|
||||
// # Post a message as other user
|
||||
cy.postMessageAs({sender: otherUser, message: 'Root post', channelId: testChannel.id}).then((post) => {
|
||||
rootPost = post;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// # Visit the channel
|
||||
cy.visit(`/${team.name}/channels/${channel.name}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T4684 CRT - CRT tour - full', () => {
|
||||
// # Go to Settings>Display>Display Settings>Collapsed Reply Threads (Beta), select ON and Save
|
||||
// # Dismiss "You're accessing an early beta of CRT" modal
|
||||
cy.uiChangeCRTDisplaySetting('ON');
|
||||
|
||||
// # Open any post on RHS
|
||||
cy.uiGetNthPost(1).click();
|
||||
|
||||
// * Verify green dot on Thread title on the thread header
|
||||
cy.get('.sidebar--right__header').find('#tipButton').should('be.visible');
|
||||
|
||||
// * Verify 1st tutorial tip points is present
|
||||
cy.get('[data-testid="current_tutorial_tip"]').findByText('Viewing a thread in the sidebar').should('be.visible');
|
||||
|
||||
// * Verify "Got it" button is present
|
||||
cy.findByText('Got it').should('be.visible');
|
||||
|
||||
// # Click on "Got it" button
|
||||
cy.findByText('Got it').click();
|
||||
|
||||
// * Verify tutorial tip is dismissed
|
||||
cy.get('[data-testid="current_tutorial_tip"]').should('not.exist');
|
||||
|
||||
// * Verify green dot is is no longer present
|
||||
cy.get('.sidebar--right__header').find('#tipButton').should('not.exist');
|
||||
|
||||
// # Follow an existing thread or receive replies to root post you started
|
||||
cy.get('#rhsContainer').find('.FollowButton').click();
|
||||
cy.postMessageAs({sender: otherUser, message: 'other reply!', channelId: testChannel.id, rootId: rootPost.id});
|
||||
|
||||
// * Verify green pulsing dot on global threads
|
||||
cy.get('#sidebar-threads-button').find('[data-testid="pulsating_dot"]').should('be.visible');
|
||||
|
||||
// # Click on global Threads sidebar item
|
||||
cy.uiGetSidebarItem('threads').click();
|
||||
|
||||
// * Verify "A new way to view and follow thread" modal is present
|
||||
cy.get('#collapsed_reply_threads_modal').should('be.visible').as('crtModal');
|
||||
cy.get('@crtModal').findByText('A new way to view and follow threads').should('be.visible');
|
||||
|
||||
// * Verify "Take the Tour" button is present
|
||||
cy.get('@crtModal').findByText('Take the Tour').should('be.visible');
|
||||
|
||||
// * Verify "Skip Tour" button is present
|
||||
cy.get('@crtModal').findByText('Skip Tour').should('be.visible');
|
||||
|
||||
// # Click on Take the tour button
|
||||
cy.get('@crtModal').findByText('Take the Tour').click();
|
||||
|
||||
// * Verify "Welcome to the Threads view!" tour tip
|
||||
cy.get('[data-testid="current_tutorial_tip"]').findByText('Welcome to the Threads view!').should('be.visible');
|
||||
|
||||
// * Verify Next button is present
|
||||
cy.findByText('Next').should('be.visible');
|
||||
|
||||
// * Verify 3 radio buttons on the bottom, with the far left button active
|
||||
cy.get('.tour-tip__dot-ctr').should('be.visible').children().as('crtTourTip');
|
||||
cy.get('@crtTourTip').eq(0).find('a').should('have.class', 'tour-tip__dot active');
|
||||
cy.get('@crtTourTip').eq(1).find('a').should('have.class', 'tour-tip__dot');
|
||||
cy.get('@crtTourTip').eq(2).find('a').should('have.class', 'tour-tip__dot');
|
||||
|
||||
// # Click on the Next button
|
||||
cy.findByText('Next').click();
|
||||
|
||||
// * Verify green dot on first thread
|
||||
cy.get('[data-testid="threads_list"]').find('#tipButton').should('be.visible');
|
||||
|
||||
// * Verify "Threads List" tutorial tip
|
||||
cy.get('[data-testid="current_tutorial_tip"]').should('be.visible').within(() => {
|
||||
cy.findByText('Threads List').should('be.visible');
|
||||
});
|
||||
|
||||
// * Verify "Previous" button is present
|
||||
cy.findByText('Previous').should('be.visible');
|
||||
|
||||
// * Verify "Next" button is present
|
||||
cy.findByText('Next').should('be.visible');
|
||||
|
||||
// * Verify middle radio button is active
|
||||
cy.get('.tour-tip__dot-ctr').should('be.visible').children().as('crtTourTip');
|
||||
cy.get('@crtTourTip').eq(0).find('a').should('have.class', 'tour-tip__dot');
|
||||
cy.get('@crtTourTip').eq(1).find('a').should('have.class', 'tour-tip__dot active');
|
||||
cy.get('@crtTourTip').eq(2).find('a').should('have.class', 'tour-tip__dot');
|
||||
|
||||
// # Click on Next
|
||||
cy.findByText('Next').click();
|
||||
|
||||
// * Verify green dot on Unreads tab
|
||||
cy.get('#threads-list-unread-button').find('#tipButton').should('be.visible');
|
||||
|
||||
// * Verify "Unread threads" tutorial tip
|
||||
cy.get('[data-testid="current_tutorial_tip"]').should('be.visible').findByText('Unread threads').should('be.visible');
|
||||
|
||||
// * Verify "Previous" button is present
|
||||
cy.findByText('Previous').should('be.visible');
|
||||
|
||||
// * Verify "Done" button is present
|
||||
cy.findByText('Done').should('be.visible');
|
||||
|
||||
// * Verify far right radio button is active
|
||||
// * Verify 3 radio buttons on the bottom, with the far left button active
|
||||
cy.get('.tour-tip__dot-ctr').should('be.visible').children().as('crtTourTip');
|
||||
cy.get('@crtTourTip').eq(0).find('a').should('have.class', 'tour-tip__dot');
|
||||
cy.get('@crtTourTip').eq(1).find('a').should('have.class', 'tour-tip__dot');
|
||||
cy.get('@crtTourTip').eq(2).find('a').should('have.class', 'tour-tip__dot active');
|
||||
|
||||
// # Click on "Done" button
|
||||
cy.findByText('Done').click();
|
||||
|
||||
// * Verify tour is dismissed
|
||||
cy.get('[data-testid="current_tutorial_tip"]').should('not.exist');
|
||||
cy.get('[data-testid="threads_list"]').find('#tipButton').should('not.exist');
|
||||
cy.get('#threads-list-unread-button').find('#tipButton').should('not.exist');
|
||||
cy.get('#sidebar-threads-button').find('[data-testid="pulsating_dot"]').should('not.exist');
|
||||
});
|
||||
|
||||
it('MM-T4694 CRT - Skip the tour', () => {
|
||||
// # Go to Settings>Display>Display Settings>Collapsed Reply Threads (Beta), select ON and Save
|
||||
// # Dismiss "You're accessing an early beta of CRT" modal
|
||||
cy.uiChangeCRTDisplaySetting('ON');
|
||||
|
||||
// # Open any post on RHS
|
||||
cy.uiGetNthPost(1).click();
|
||||
|
||||
// * Verify green dot on Thread title on the thread header
|
||||
cy.get('.sidebar--right__header').find('#tipButton').should('be.visible');
|
||||
|
||||
// * Verify 1st tutorial tip points is present
|
||||
cy.get('[data-testid="current_tutorial_tip"]').findByText('Viewing a thread in the sidebar').should('be.visible');
|
||||
|
||||
// * Verify "Got it" button is present
|
||||
cy.findByText('Got it').should('be.visible');
|
||||
|
||||
// # Click on "x" button to dismiss the tour point
|
||||
cy.get('[data-testid="close_tutorial_tip"]').click();
|
||||
|
||||
// * Verify tutorial tip is dismissed
|
||||
cy.get('[data-testid="current_tutorial_tip"]').should('not.exist');
|
||||
|
||||
// * Verify green dot is is no longer present
|
||||
cy.get('.sidebar--right__header').find('#tipButton').should('not.exist');
|
||||
|
||||
// # Follow an existing thread or receive replies to root post you started
|
||||
cy.get('#rhsContainer').find('.FollowButton').click();
|
||||
cy.postMessageAs({sender: otherUser, message: 'other reply!', channelId: testChannel.id, rootId: rootPost.id});
|
||||
|
||||
// * Verify green pulsing dot on global threads
|
||||
cy.get('#sidebar-threads-button').find('[data-testid="pulsating_dot"]').should('be.visible');
|
||||
|
||||
// # Click on global Threads sidebar item
|
||||
cy.uiGetSidebarItem('threads').click();
|
||||
|
||||
// * Verify "A new way to view and follow thread" modal is present
|
||||
cy.get('#collapsed_reply_threads_modal').should('be.visible').within(() => {
|
||||
cy.findByText('A new way to view and follow threads').should('be.visible');
|
||||
|
||||
// * Verify "Take the Tour" button is present
|
||||
cy.findByText('Take the Tour').should('be.visible');
|
||||
|
||||
// * Verify "Skip Tour" button is present
|
||||
cy.findByText('Skip Tour').should('be.visible');
|
||||
|
||||
// # Click on "x" button to dismiss the tour point
|
||||
cy.get('.close').click();
|
||||
});
|
||||
|
||||
// * Verify tutorial modal is dismissed
|
||||
cy.get('[data-testid="current_tutorial_tip"]').should('not.exist');
|
||||
|
||||
// * Verify green dot is still present
|
||||
cy.get('#sidebar-threads-button').find('[data-testid="pulsating_dot"]').should('be.visible');
|
||||
|
||||
// # Click on global Threads sidebar item
|
||||
cy.uiGetSidebarItem('threads').click();
|
||||
|
||||
// # Click on "Skip tour" button
|
||||
cy.get('#collapsed_reply_threads_modal').should('be.visible').findByText('Skip Tour').click();
|
||||
|
||||
// * Verify modal is dismissed and tour is no longer available
|
||||
cy.get('[data-testid="current_tutorial_tip"]').should('not.exist');
|
||||
cy.get('[data-testid="threads_list"]').find('#tipButton').should('not.exist');
|
||||
cy.get('#threads-list-unread-button').find('#tipButton').should('not.exist');
|
||||
|
||||
// * Verify no pulsing green dot on global threads item
|
||||
cy.get('#sidebar-threads-button').find('[data-testid="pulsating_dot"]').should('not.exist');
|
||||
});
|
||||
|
||||
it('MM-T4695 CRT - cancel any tour point by using x', () => {
|
||||
// # Go to Settings>Display>Display Settings>Collapsed Reply Threads (Beta), select ON and Save
|
||||
// # Dismiss "You're accessing an early beta of CRT" modal
|
||||
cy.uiChangeCRTDisplaySetting('ON');
|
||||
|
||||
// # Follow an existing thread or receive replies to root post you started
|
||||
cy.postMessageAs({sender: otherUser, message: 'other reply!', channelId: testChannel.id, rootId: rootPost.id});
|
||||
cy.get(`#post_${rootPost.id}`).find('.FollowButton').click();
|
||||
|
||||
// * Verify green pulsing dot on global threads
|
||||
cy.get('#sidebar-threads-button').find('[data-testid="pulsating_dot"]').should('be.visible');
|
||||
|
||||
// # Click on global Threads sidebar item
|
||||
cy.uiGetSidebarItem('threads').click();
|
||||
|
||||
// * Verify "A new way to view and follow thread" modal is present
|
||||
cy.get('#collapsed_reply_threads_modal').should('be.visible').within(() => {
|
||||
cy.findByText('A new way to view and follow threads').should('be.visible');
|
||||
|
||||
// * Verify "Take the Tour" button is present
|
||||
cy.findByText('Take the Tour').should('be.visible');
|
||||
|
||||
// * Verify "Skip Tour" button is present
|
||||
cy.findByText('Skip Tour').should('be.visible');
|
||||
|
||||
// # Click on Take the tour button
|
||||
cy.findByText('Take the Tour').click();
|
||||
});
|
||||
|
||||
// * Verify "Welcome to the Threads view!" tutorial tip
|
||||
cy.get('[data-testid="current_tutorial_tip"]').should('be.visible').within(() => {
|
||||
cy.findByText('Welcome to the Threads view!').should('be.visible');
|
||||
});
|
||||
|
||||
// * Verify Next button is present
|
||||
cy.findByText('Next').should('be.visible');
|
||||
|
||||
// * Verify 3 radio buttons on the bottom, with the far left button active
|
||||
cy.get('.tour-tip__dot-ctr').should('be.visible').children().as('crtTourTip');
|
||||
cy.get('@crtTourTip').eq(0).find('a').should('have.class', 'tour-tip__dot active');
|
||||
cy.get('@crtTourTip').eq(1).find('a').should('have.class', 'tour-tip__dot');
|
||||
cy.get('@crtTourTip').eq(2).find('a').should('have.class', 'tour-tip__dot');
|
||||
|
||||
// # Click on the `x` to dismiss the tip
|
||||
cy.get('[data-testid="close_tutorial_tip"]').click();
|
||||
|
||||
// * Verify green dot on Threads sidebar item
|
||||
cy.get('#SidebarContainer').find('#tipButton').should('be.visible').click();
|
||||
|
||||
cy.findByText('Next').click();
|
||||
|
||||
// # Click on the `x` to dismiss the tip
|
||||
cy.get('[data-testid="close_tutorial_tip"]').click();
|
||||
|
||||
// * Verify green pulsing dot on the first, top, thread
|
||||
cy.get('[data-testid="threads_list"]').within(() => {
|
||||
cy.get('#tipButton').should('be.visible');
|
||||
|
||||
// # Click on the green dot
|
||||
cy.get('#tipButton').click();
|
||||
});
|
||||
|
||||
// * Verify green dot on first thread
|
||||
cy.get('[data-testid="threads_list"]').find('#tipButton').should('be.visible');
|
||||
|
||||
// * Verify "Threads List" tutorial tip
|
||||
cy.get('[data-testid="current_tutorial_tip"]').should('be.visible').within(() => {
|
||||
cy.findByText('Threads List').should('be.visible');
|
||||
});
|
||||
|
||||
// * Verify "Previous" button is present
|
||||
cy.findByText('Previous').should('be.visible');
|
||||
|
||||
// * Verify "Next" button is present
|
||||
cy.findByText('Next').should('be.visible');
|
||||
|
||||
// * Verify middle radio button is active
|
||||
cy.get('.tour-tip__dot-ctr').should('be.visible').children().as('crtTourTip');
|
||||
cy.get('@crtTourTip').eq(0).find('a').should('have.class', 'tour-tip__dot');
|
||||
cy.get('@crtTourTip').eq(1).find('a').should('have.class', 'tour-tip__dot active');
|
||||
cy.get('@crtTourTip').eq(2).find('a').should('have.class', 'tour-tip__dot');
|
||||
|
||||
// # Click on the Previous button
|
||||
cy.findByText('Previous').click();
|
||||
|
||||
// * Verify previous tutorial tip opens
|
||||
cy.get('[data-testid="current_tutorial_tip"]').should('be.visible').within(() => {
|
||||
cy.findByText('Welcome to the Threads view!').should('be.visible');
|
||||
});
|
||||
|
||||
// * Verify far left radio button is active
|
||||
cy.get('.tour-tip__dot-ctr').should('be.visible').children().as('crtTourTip');
|
||||
cy.get('@crtTourTip').eq(0).find('a').should('have.class', 'tour-tip__dot active');
|
||||
cy.get('@crtTourTip').eq(1).find('a').should('have.class', 'tour-tip__dot');
|
||||
cy.get('@crtTourTip').eq(2).find('a').should('have.class', 'tour-tip__dot');
|
||||
|
||||
// # Click Next and ...
|
||||
cy.findByText('Next').click();
|
||||
|
||||
// # ... then x to dismiss the Threads List tip
|
||||
cy.get('[data-testid="current_tutorial_tip"]').should('be.visible').within(() => {
|
||||
cy.findByText('Threads List').should('be.visible');
|
||||
cy.get('[data-testid="close_tutorial_tip"]').click();
|
||||
});
|
||||
|
||||
// * Verify tip is dismissed
|
||||
cy.get('[data-testid="current_tutorial_tip"]').should('not.exist');
|
||||
|
||||
cy.get('[data-testid="threads_list"]').find('#tipButton').should('be.visible').click();
|
||||
|
||||
// # Click Next and ...
|
||||
cy.findByText('Next').click();
|
||||
|
||||
cy.get('[data-testid="close_tutorial_tip"]').click();
|
||||
|
||||
// * Verify green dot on Unreads tab
|
||||
cy.get('#threads-list-unread-button').within(() => {
|
||||
cy.get('#tipButton').should('be.visible');
|
||||
});
|
||||
|
||||
// # Click on the green dot on Unreads tab
|
||||
cy.get('#tipButton').click();
|
||||
|
||||
// * Verify "Unread threads" tutorial tip
|
||||
cy.get('[data-testid="current_tutorial_tip"]').should('be.visible').within(() => {
|
||||
cy.findByText('Unread threads').should('be.visible');
|
||||
});
|
||||
|
||||
// * Verify "Previous" button is present
|
||||
cy.findByText('Previous').should('be.visible');
|
||||
|
||||
// * Verify "Finish tour" button is present
|
||||
cy.findByText('Done').should('be.visible');
|
||||
|
||||
// * Verify far right radio button is active
|
||||
cy.get('.tour-tip__dot-ctr').should('be.visible').children().as('crtTourTip');
|
||||
cy.get('@crtTourTip').eq(0).find('a').should('have.class', 'tour-tip__dot');
|
||||
cy.get('@crtTourTip').eq(1).find('a').should('have.class', 'tour-tip__dot');
|
||||
cy.get('@crtTourTip').eq(2).find('a').should('have.class', 'tour-tip__dot active');
|
||||
|
||||
// # Click on `x` to dismiss tutorial tip
|
||||
cy.get('[data-testid="close_tutorial_tip"]').click();
|
||||
|
||||
// * Verify tour is dismissed
|
||||
cy.get('#threads-list-unread-button').find('#tipButton').should('be.visible').click();
|
||||
cy.findByText('Done').should('be.visible').click();
|
||||
cy.get('#threads-list-unread-button').find('#tipButton').should('not.exist');
|
||||
|
||||
cy.get('[data-testid="current_tutorial_tip"]').should('not.exist');
|
||||
cy.get('[data-testid="threads_list"]').find('#tipButton').should('not.exist');
|
||||
cy.get('#sidebar-threads-button').find('[data-testid="pulsating_dot"]').should('not.exist');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,98 @@
|
||||
// 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 @collapsed_reply_threads @not_cloud
|
||||
|
||||
import * as MESSAGES from '../../../fixtures/messages';
|
||||
import {matterpollPlugin} from '../../../utils/plugins';
|
||||
import {interceptFileUpload} from '../files_and_attachments/helpers';
|
||||
|
||||
describe('Collapsed Reply Threads', () => {
|
||||
let testTeam;
|
||||
let testChannel;
|
||||
let user1;
|
||||
|
||||
before(() => {
|
||||
cy.apiUpdateConfig({
|
||||
PluginSettings: {
|
||||
Enable: true,
|
||||
},
|
||||
ServiceSettings: {
|
||||
ThreadAutoFollow: true,
|
||||
CollapsedThreads: 'default_off',
|
||||
},
|
||||
});
|
||||
|
||||
// # Create new channel and other user, and add other user to channel
|
||||
cy.apiInitSetup({loginAfter: true, promoteNewUserAsAdmin: true}).then(({team, channel, user}) => {
|
||||
testTeam = team;
|
||||
user1 = user;
|
||||
testChannel = channel;
|
||||
|
||||
cy.apiSaveCRTPreference(user1.id, 'on');
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// # Visit the channel
|
||||
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
|
||||
interceptFileUpload();
|
||||
});
|
||||
|
||||
it('MM-T4776 should display poll text without Markdown in the threads list', () => {
|
||||
cy.shouldNotRunOnCloudEdition();
|
||||
cy.shouldHavePluginUploadEnabled();
|
||||
|
||||
// # Upload and enable "matterpoll" plugin
|
||||
cy.apiUploadAndEnablePlugin(matterpollPlugin);
|
||||
|
||||
// # In center post the following: /poll "Do you like https://mattermost.com?"
|
||||
cy.postMessage('/poll "Do you like https://mattermost.com?"');
|
||||
|
||||
cy.getLastPostId().then((pollId) => {
|
||||
// # Post a reply on the POLL to create a thread and follow
|
||||
cy.postMessageAs({sender: user1, message: MESSAGES.SMALL, channelId: testChannel.id, rootId: pollId});
|
||||
|
||||
// # Click "Yes" or "No"
|
||||
cy.findByText('Yes').click();
|
||||
|
||||
// # Visit global threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * Text in ThreadItem should say 'username: Do you like https://mattermost.com?'
|
||||
cy.get('.attachment__truncated').first().should('have.text', user1.nickname + ': Do you like https://mattermost.com?');
|
||||
|
||||
// * Text in ThreadItem should say 'Total votes: 1'
|
||||
cy.get('.attachment__truncated').last().should('have.text', 'Total votes: 1');
|
||||
|
||||
// # Open the thread
|
||||
cy.get('.ThreadItem').last().click();
|
||||
|
||||
// # Go to channel
|
||||
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
|
||||
|
||||
// # End the poll
|
||||
cy.findByText('End Poll').click();
|
||||
cy.findByText('End').click();
|
||||
|
||||
// # Visit global threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * Text in ThreadItem should say 'username: Do you like https://mattermost.com?'
|
||||
cy.get('.attachment__truncated').first().should('have.text', user1.nickname + ': Do you like https://mattermost.com?');
|
||||
|
||||
// * Text in ThreadItem should say 'This poll has ended. The results are:'
|
||||
cy.get('.attachment__truncated').last().should('have.text', 'This poll has ended. The results are:');
|
||||
|
||||
// # Cleanup
|
||||
cy.apiDeletePost(pollId);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,142 @@
|
||||
// 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 @collapsed_reply_threads
|
||||
|
||||
import * as MESSAGES from '../../../fixtures/messages';
|
||||
import {waitUntilUploadComplete, interceptFileUpload} from '../files_and_attachments/helpers';
|
||||
|
||||
describe('Collapsed Reply Threads', () => {
|
||||
let testTeam;
|
||||
let testChannel;
|
||||
let user1;
|
||||
|
||||
const files = [
|
||||
{
|
||||
testCase: 'MM-T4777_2',
|
||||
filename: 'word-file.doc',
|
||||
extensions: 'DOC',
|
||||
icon: 'icon-file-word-outline',
|
||||
},
|
||||
{
|
||||
testCase: 'MM-T4777_3',
|
||||
filename: 'wordx-file.docx',
|
||||
extensions: 'DOCX',
|
||||
icon: 'icon-file-word-outline',
|
||||
},
|
||||
{
|
||||
testCase: 'MM-T4777_4',
|
||||
filename: 'powerpoint-file.ppt',
|
||||
extensions: 'PPT',
|
||||
icon: 'icon-file-powerpoint-outline',
|
||||
},
|
||||
{
|
||||
testCase: 'MM-T4777_5',
|
||||
filename: 'powerpointx-file.pptx',
|
||||
extensions: 'PPTX',
|
||||
icon: 'icon-file-powerpoint-outline',
|
||||
},
|
||||
{
|
||||
testCase: 'MM-T4777_6',
|
||||
filename: 'mp3-audio-file.mp3',
|
||||
extensions: 'MP3',
|
||||
icon: 'icon-file-audio-outline',
|
||||
},
|
||||
{
|
||||
testCase: 'MM-T4777_7',
|
||||
filename: 'mp4-video-file.mp4',
|
||||
extensions: 'MP4',
|
||||
icon: 'icon-file-video-outline',
|
||||
},
|
||||
{
|
||||
testCase: 'MM-T4777_8',
|
||||
filename: 'theme.json',
|
||||
extensions: 'JSON',
|
||||
icon: 'icon-file-code-outline',
|
||||
},
|
||||
];
|
||||
|
||||
before(() => {
|
||||
cy.apiUpdateConfig({
|
||||
ServiceSettings: {
|
||||
ThreadAutoFollow: true,
|
||||
CollapsedThreads: 'default_off',
|
||||
},
|
||||
});
|
||||
|
||||
// # Create new channel and other user, and add other user to channel
|
||||
cy.apiInitSetup({loginAfter: true, promoteNewUserAsAdmin: true}).then(({team, channel, user}) => {
|
||||
testTeam = team;
|
||||
user1 = user;
|
||||
testChannel = channel;
|
||||
|
||||
cy.apiSaveCRTPreference(user1.id, 'on');
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// # Visit the channel
|
||||
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
|
||||
interceptFileUpload();
|
||||
});
|
||||
|
||||
it('MM-T4777_1 should show image thumbnail in thread list item', () => {
|
||||
const image = 'jpg-image-file.jpg';
|
||||
|
||||
cy.get('#advancedTextEditorCell').find('#fileUploadInput').attachFile(image);
|
||||
waitUntilUploadComplete();
|
||||
cy.get('.post-image__thumbnail').should('be.visible');
|
||||
cy.uiGetPostTextBox().clear().type('{enter}');
|
||||
|
||||
cy.getLastPostId().then((rootId) => {
|
||||
// # Post a reply to create a thread and follow
|
||||
cy.postMessageAs({sender: user1, message: MESSAGES.SMALL, channelId: testChannel.id, rootId});
|
||||
|
||||
// # Visit Global Threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * Text should be the filename
|
||||
cy.get('.file_card__name').should('have.text', image);
|
||||
|
||||
// * Image should be shown
|
||||
cy.get('.file_card__image.post-image.small').should('be.visible');
|
||||
|
||||
// # Cleanup
|
||||
cy.apiDeletePost(rootId);
|
||||
});
|
||||
});
|
||||
|
||||
files.forEach((file) => {
|
||||
it(`${file.testCase} should display correct icon for ${file.extensions} on threads list`, () => {
|
||||
// # Post a file
|
||||
cy.get('#advancedTextEditorCell').find('#fileUploadInput').attachFile(file.filename);
|
||||
waitUntilUploadComplete();
|
||||
cy.get('.post-image__thumbnail').should('be.visible');
|
||||
cy.uiGetPostTextBox().clear().type('{enter}');
|
||||
|
||||
cy.getLastPostId().then((rootId) => {
|
||||
// # Post a reply to create a thread and follow
|
||||
cy.postMessageAs({sender: user1, message: MESSAGES.SMALL, channelId: testChannel.id, rootId});
|
||||
|
||||
// # Visit Global Threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * Thread item should display the correct icon
|
||||
cy.get('.file_card__attachment').should('have.class', file.icon);
|
||||
|
||||
// * Thread item should display text
|
||||
cy.get('.file_card__name').should('have.text', file.filename);
|
||||
|
||||
// # Cleanup
|
||||
cy.apiDeletePost(rootId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,274 @@
|
||||
// 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.
|
||||
// ***************************************************************
|
||||
|
||||
// Group: @channels @collapsed_reply_threads
|
||||
|
||||
import * as TIMEOUTS from '../../../fixtures/timeouts';
|
||||
import {isMac} from '../../../utils';
|
||||
|
||||
describe('Collapsed Reply Threads', () => {
|
||||
let testTeam;
|
||||
let testUser;
|
||||
let otherUser;
|
||||
let testChannel;
|
||||
|
||||
before(() => {
|
||||
cy.apiUpdateConfig({
|
||||
ServiceSettings: {
|
||||
ThreadAutoFollow: true,
|
||||
CollapsedThreads: 'default_off',
|
||||
},
|
||||
});
|
||||
|
||||
// # Create new channel and other user and add other user to channel
|
||||
cy.apiInitSetup({loginAfter: true, promoteNewUserAsAdmin: true}).then(({team, channel, user}) => {
|
||||
testTeam = team;
|
||||
testUser = user;
|
||||
testChannel = channel;
|
||||
|
||||
cy.apiSaveCRTPreference(testUser.id, 'on');
|
||||
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
|
||||
otherUser = user1;
|
||||
|
||||
cy.apiAddUserToTeam(testTeam.id, otherUser.id).then(() => {
|
||||
cy.apiAddUserToChannel(testChannel.id, otherUser.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// # Visit channel
|
||||
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
|
||||
});
|
||||
|
||||
it('MM-T4141_1 should follow a thread after replying', () => {
|
||||
// # Post message as other user
|
||||
cy.postMessageAs({
|
||||
sender: otherUser,
|
||||
message: 'Root post,',
|
||||
channelId: testChannel.id,
|
||||
}).then(({id: rootId}) => {
|
||||
// * Thread footer should not be visible
|
||||
cy.get(`#post_${rootId}`).find('.ThreadFooter').should('not.exist');
|
||||
|
||||
// # Click on post to open RHS
|
||||
cy.get(`#post_${rootId}`).click();
|
||||
|
||||
// * Button on header should say Follow as current user is not following
|
||||
cy.get('#rhsContainer').find('.FollowButton').should('have.text', 'Follow');
|
||||
|
||||
// # Post a reply as current user
|
||||
cy.postMessageReplyInRHS('Reply!');
|
||||
|
||||
// # Get last root post
|
||||
cy.get(`#post_${rootId}`).
|
||||
|
||||
// * thread footer should exist now
|
||||
get('.ThreadFooter').should('exist').
|
||||
within(() => {
|
||||
// * the button on the footer should say Following
|
||||
cy.get('.FollowButton').should('have.text', 'Following');
|
||||
});
|
||||
|
||||
// * the button on the RHS header should now say Following
|
||||
cy.get('#rhsContainer').find('.FollowButton').should('have.text', 'Following');
|
||||
|
||||
// # Visit global threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * There should be a thread there
|
||||
cy.get('article.ThreadItem').should('have.have.lengthOf', 1);
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T4141_2 should follow a thread after marking it as unread', () => {
|
||||
// # Post a root post as other user
|
||||
postMessageWithReply(testChannel.id, otherUser, 'Another interesting post,', otherUser, 'Self reply!').then(({rootId, replyId}) => {
|
||||
// # Get root post
|
||||
cy.get(`#post_${rootId}`).within(() => {
|
||||
// * Thread footer should be visible
|
||||
cy.get('.ThreadFooter').should('exist').
|
||||
|
||||
// * Thread footer button should say 'Follow'
|
||||
find('.FollowButton').should('have.text', 'Follow');
|
||||
});
|
||||
|
||||
// # Click on root post to open thread
|
||||
cy.get(`#post_${rootId}`).click();
|
||||
|
||||
// * RHS header button should say 'Follow'
|
||||
cy.get('#rhsContainer').find('.FollowButton').should('have.text', 'Follow');
|
||||
|
||||
// # Click on the reply's dot menu and mark as unread
|
||||
cy.uiClickPostDropdownMenu(replyId, 'Mark as Unread', 'RHS_COMMENT');
|
||||
|
||||
// * RHS header button should say 'Following'
|
||||
cy.get('#rhsContainer').find('.FollowButton').should('have.text', 'Following');
|
||||
|
||||
// # Get root post
|
||||
cy.get(`#post_${rootId}`).within(() => {
|
||||
// * Thread footer should be visible
|
||||
cy.get('.ThreadFooter').should('exist').
|
||||
|
||||
// * Thread footer button should say 'Following'
|
||||
find('.FollowButton').should('have.text', 'Following');
|
||||
});
|
||||
|
||||
// # Visit global threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * There should be 2 threads now
|
||||
cy.get('article.ThreadItem').should('have.have.lengthOf', 2);
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T4141_3 clicking "Following" button in the footer should unfollow the thread', () => {
|
||||
// # Post a root post as other user
|
||||
postMessageWithReply(testChannel.id, otherUser, 'Another interesting post,', testUser, 'Self reply!');
|
||||
|
||||
// # Get last root post in channel
|
||||
cy.getLastPostId().then((rootId) => {
|
||||
// # Open the thread
|
||||
cy.get(`#post_${rootId}`).click();
|
||||
|
||||
// * RHS header button should say 'Following'
|
||||
cy.get('#rhsContainer').find('.FollowButton').should('have.text', 'Following');
|
||||
|
||||
// # Get thread footer of last root post
|
||||
cy.get(`#post_${rootId}`).within(() => {
|
||||
// * thread footer should exist
|
||||
cy.get('.ThreadFooter').should('exist');
|
||||
|
||||
// * thread footer button should say 'Following'
|
||||
cy.get('.FollowButton').should('have.text', 'Following');
|
||||
|
||||
// # Click thread footer Following button
|
||||
cy.get('.FollowButton').click({force: true});
|
||||
|
||||
// * thread footer button should say 'Follow'
|
||||
cy.get('.FollowButton').should('have.text', 'Follow');
|
||||
});
|
||||
|
||||
// * RHS header button should say 'Follow'
|
||||
cy.get('#rhsContainer').find('.FollowButton').should('have.text', 'Follow');
|
||||
|
||||
// # Close RHS
|
||||
cy.uiCloseRHS();
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T4141_4 clicking "Follow" button in the footer should follow the thread', () => {
|
||||
// # Post a root post as other user
|
||||
postMessageWithReply(testChannel.id, otherUser, 'Another interesting post,', otherUser, 'Self reply!');
|
||||
|
||||
// # Get last root post in channel
|
||||
cy.getLastPostId().then((rootId) => {
|
||||
// # Open the thread
|
||||
cy.get(`#post_${rootId}`).click();
|
||||
|
||||
// * RHS header button should say 'Follow'
|
||||
cy.get('#rhsContainer').find('.FollowButton').should('have.text', 'Follow');
|
||||
|
||||
// # Get thread footer of last root post
|
||||
cy.get(`#post_${rootId}`).within(() => {
|
||||
// * thread footer should exist
|
||||
cy.get('.ThreadFooter').should('exist');
|
||||
|
||||
// * thread footer button should say 'Follow'
|
||||
cy.get('.FollowButton').should('have.text', 'Follow');
|
||||
|
||||
// # Click thread footer 'Follow' button
|
||||
cy.get('.FollowButton').click({force: true});
|
||||
|
||||
// * thread footer button should say 'Following'
|
||||
cy.get('.FollowButton').should('have.text', 'Following');
|
||||
});
|
||||
|
||||
// * RHS header button should say 'Following'
|
||||
cy.get('#rhsContainer').find('.FollowButton').should('have.text', 'Following');
|
||||
|
||||
// # Close RHS
|
||||
cy.uiCloseRHS();
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T4682 should show search guidance at the end of the list after scroll loading', () => {
|
||||
// # Create more than 25 threads so we can use scroll loading in the Threads list
|
||||
for (let i = 1; i <= 30; i++) {
|
||||
postMessageWithReply(testChannel.id, otherUser, `Another interesting post ${i}`, testUser, `Another reply ${i}!`).then(({rootId}) => {
|
||||
// # Mark last thread as Unread
|
||||
if (i === 30) {
|
||||
// # Click on root post to open thread
|
||||
cy.get(`#post_${rootId}`).click();
|
||||
|
||||
// # Click on the reply's dot menu and mark as unread
|
||||
cy.uiClickPostDropdownMenu(rootId, 'Mark as Unread', 'RHS_ROOT');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// # Scroll load the threads list to reach the end
|
||||
const maxScrolls = 3;
|
||||
scrollThreadsListToEnd(maxScrolls);
|
||||
|
||||
// * Search guidance item should be shown at the end of the threads list
|
||||
cy.get('.ThreadList .no-results__wrapper').should('be.visible').within(() => {
|
||||
// * Title, subtitle and shortcut keys should be shown
|
||||
cy.findByText('That’s the end of the list').should('be.visible');
|
||||
cy.contains('If you’re looking for older conversations, try searching with ').should('be.visible').within(() => {
|
||||
cy.findByText(isMac() ? '⌘' : 'Ctrl').should('be.visible');
|
||||
cy.findByText('Shift').should('be.visible');
|
||||
cy.findByText('F').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
// # Click Unreads button
|
||||
cy.findByText('Unreads').click();
|
||||
|
||||
// * Search guidance item should not be shown at the end of the Unreads threads list
|
||||
cy.get('.ThreadList .no-results__wrapper').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
function postMessageWithReply(channelId, postSender, postMessage, replySender, replyMessage) {
|
||||
return cy.postMessageAs({
|
||||
sender: postSender,
|
||||
message: postMessage || 'Another interesting post.',
|
||||
channelId,
|
||||
}).then(({id: rootId}) => {
|
||||
cy.postMessageAs({
|
||||
sender: replySender || postSender,
|
||||
message: replyMessage || 'Another reply!',
|
||||
channelId,
|
||||
rootId,
|
||||
}).then(({id: replyId}) => (Promise.resolve({rootId, replyId})));
|
||||
});
|
||||
}
|
||||
|
||||
function scrollThreadsListToEnd(maxScrolls = 1, scrolls = 0) {
|
||||
if (scrolls === maxScrolls) {
|
||||
return;
|
||||
}
|
||||
|
||||
cy.get('.ThreadList .virtualized-thread-list').scrollTo('bottom').then(($el) => {
|
||||
const element = $el.find('.no-results__wrapper');
|
||||
|
||||
if (element.length < 1) {
|
||||
cy.wait(TIMEOUTS.ONE_SEC).then(() => {
|
||||
scrollThreadsListToEnd(maxScrolls, scrolls + 1);
|
||||
});
|
||||
} else {
|
||||
cy.wrap(element).scrollIntoView();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,415 @@
|
||||
// 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.
|
||||
// ***************************************************************
|
||||
|
||||
// Group: @channels @collapsed_reply_threads
|
||||
|
||||
describe('Collapsed Reply Threads', () => {
|
||||
let testTeam;
|
||||
let testChannel;
|
||||
let user1;
|
||||
let user2;
|
||||
let user3;
|
||||
let rootPost;
|
||||
let replyPost1;
|
||||
let replyPost2;
|
||||
|
||||
const messages = {
|
||||
ROOT: 'ROOT POST',
|
||||
REPLY1: 'REPLY 1',
|
||||
REPLY2: 'REPLY 2',
|
||||
};
|
||||
|
||||
before(() => {
|
||||
cy.apiUpdateConfig({
|
||||
ServiceSettings: {
|
||||
ThreadAutoFollow: true,
|
||||
CollapsedThreads: 'default_off',
|
||||
},
|
||||
});
|
||||
|
||||
// # Create new channel and other user, and add other user to channel
|
||||
cy.apiInitSetup({loginAfter: true, promoteNewUserAsAdmin: true}).then(({team, channel, user}) => {
|
||||
testTeam = team;
|
||||
user1 = user;
|
||||
testChannel = channel;
|
||||
|
||||
cy.apiSaveCRTPreference(user1.id, 'on');
|
||||
cy.apiCreateUser({prefix: 'user2'}).then(({user: newUser}) => {
|
||||
user2 = newUser;
|
||||
|
||||
cy.apiAddUserToTeam(testTeam.id, user2.id).then(() => {
|
||||
cy.apiAddUserToChannel(testChannel.id, user2.id);
|
||||
});
|
||||
});
|
||||
|
||||
cy.apiCreateUser({prefix: 'user3'}).then(({user: newUser}) => {
|
||||
user3 = newUser;
|
||||
|
||||
cy.apiAddUserToTeam(testTeam.id, user3.id).then(() => {
|
||||
cy.apiAddUserToChannel(testChannel.id, user3.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// # Visit the channel
|
||||
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
|
||||
|
||||
// # Post a message as other user
|
||||
cy.postMessageAs({sender: user1, message: messages.ROOT, channelId: testChannel.id}).then((post) => {
|
||||
rootPost = post;
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T4379 Display: Click to open threads)', () => {
|
||||
cy.uiWaitUntilMessagePostedIncludes(rootPost.data.message);
|
||||
|
||||
// # Get the root post
|
||||
cy.getLastPost().click();
|
||||
|
||||
// * Verify the post is opened in RHS
|
||||
cy.get(`#rhsPost_${rootPost.id}`).should('be.visible');
|
||||
|
||||
// # Close RHS
|
||||
cy.uiCloseRHS();
|
||||
|
||||
// # Open settings modal and disable opening the thread in RHS when being clicked
|
||||
cy.uiOpenSettingsModal('Display');
|
||||
|
||||
// # Open section "click to open threads"
|
||||
cy.get('#click_to_replyTitle').click();
|
||||
|
||||
// # Click radio button with option B ('off')
|
||||
cy.get('#click_to_replyFormatB').click();
|
||||
|
||||
// # Save settings
|
||||
cy.get('#saveSetting').click();
|
||||
|
||||
// # close settings modal
|
||||
cy.uiClose();
|
||||
|
||||
// # (Re-) Visit the channel
|
||||
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
|
||||
|
||||
// # Get the root post
|
||||
cy.getLastPost().click();
|
||||
|
||||
// * Verify the post is opened in RHS
|
||||
cy.get(`rhsPost_${rootPost.id}`).should('not.exist');
|
||||
|
||||
// # Cleanup for next test
|
||||
cy.apiDeletePost(rootPost.id);
|
||||
});
|
||||
|
||||
it('MM-T4445 CRT - Delete root post', () => {
|
||||
/**
|
||||
* When you delete a post the current behavior in displaying it and the thread replies is incorrect.
|
||||
* Deleting a root post leads to a post showing `(message deleted)`, but still rendering the replies
|
||||
* in the ThreadFooter. Ideally we should remove the root post without removing the replies.
|
||||
*/
|
||||
cy.uiWaitUntilMessagePostedIncludes(rootPost.data.message);
|
||||
|
||||
// # Thread footer should not exist
|
||||
cy.uiGetPostThreadFooter(rootPost.id).should('not.exist');
|
||||
|
||||
// # Post a reply post as current user
|
||||
cy.postMessageAs({sender: user2, message: messages.REPLY1, channelId: testChannel.id, rootId: rootPost.id});
|
||||
|
||||
// # Visit global threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * There should be a single thread item
|
||||
cy.get('article.ThreadItem').should('have.lengthOf', 1);
|
||||
|
||||
// # Delete thread root post
|
||||
cy.apiDeletePost(rootPost.id);
|
||||
|
||||
/**
|
||||
* TODO: this should not be there once the root post is deleted, so remove it once the feature is adjusted
|
||||
*/
|
||||
// * There should be a single thread item showing '(message deleted)'
|
||||
cy.get('article.ThreadItem').should('have.lengthOf', 1).should('contain.text', '(message deleted)');
|
||||
|
||||
// # Refresh the page
|
||||
cy.reload(true);
|
||||
|
||||
// * There should be no thread item anymore
|
||||
cy.get('article.ThreadItem').should('have.lengthOf', 0);
|
||||
});
|
||||
|
||||
it('MM-T4446 CRT - Delete single reply post on a thread', () => {
|
||||
/**
|
||||
* When you delete a reply you are still following the thread, so the thread is not being removed
|
||||
* from the global threads view. The test description was written differently as it was assuming the
|
||||
* thread to not be followed after that. The test will be build keeping that in mind, but should
|
||||
* thread to not be followed after that. The test will be build keeping that in mind, but should
|
||||
* probably be adjusted later on.
|
||||
*/
|
||||
cy.uiWaitUntilMessagePostedIncludes(rootPost.data.message);
|
||||
|
||||
// # Thread footer should not exist
|
||||
cy.uiGetPostThreadFooter(rootPost.id).should('not.exist');
|
||||
|
||||
// # Post a reply post as current user
|
||||
cy.postMessageAs({sender: user1, message: messages.REPLY1, channelId: testChannel.id, rootId: rootPost.id}).then((post) => {
|
||||
replyPost1 = post;
|
||||
|
||||
// # Visit global threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * There should be a single thread item
|
||||
cy.get('article.ThreadItem').should('have.lengthOf', 1).first().click();
|
||||
|
||||
// * Reply should be in RHS
|
||||
cy.get(`#rhsPostMessageText_${replyPost1.id}`).should('be.visible').should('contain.text', messages.REPLY1);
|
||||
|
||||
// # Delete thread reply post
|
||||
cy.apiDeletePost(replyPost1.id);
|
||||
|
||||
// * There should be a single thread item
|
||||
cy.get('article.ThreadItem').should('have.lengthOf', 1);
|
||||
|
||||
// * The reply should be in RHS showing '(message deleted)'
|
||||
cy.get(`#rhsPost_${replyPost1.id}`).should('be.visible').should('contain.text', '(message deleted)');
|
||||
|
||||
// # Reload to re-render UI
|
||||
cy.reload(true);
|
||||
|
||||
// * There should be a single thread item with no reply
|
||||
cy.get('article.ThreadItem').should('have.lengthOf', 1);
|
||||
|
||||
// * The reply post should not exist anymore
|
||||
cy.get(`#rhsPost_${replyPost1.id}`).should('not.exist');
|
||||
|
||||
// # Cleanup for next test
|
||||
cy.apiDeletePost(rootPost.id);
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T4447 CRT - Delete single reply post on a multi-reply thread', () => {
|
||||
cy.uiWaitUntilMessagePostedIncludes(rootPost.data.message);
|
||||
|
||||
// # Thread footer should not exist
|
||||
cy.uiGetPostThreadFooter(rootPost.id).should('not.exist');
|
||||
|
||||
// # Post a reply post as current user
|
||||
cy.postMessageAs({sender: user2, message: messages.REPLY1, channelId: testChannel.id, rootId: rootPost.id}).then((post) => {
|
||||
replyPost1 = post;
|
||||
}).then(() => {
|
||||
return cy.postMessageAs({sender: user3, message: messages.REPLY2, channelId: testChannel.id, rootId: rootPost.id});
|
||||
}).then((post) => {
|
||||
replyPost2 = post;
|
||||
|
||||
// # Get thread footer of last post
|
||||
cy.uiGetPostThreadFooter(rootPost.id).within(() => {
|
||||
// * Reply button in Thread Footer should say '2 replies'
|
||||
cy.get('.ReplyButton').should('have.text', '2 replies');
|
||||
|
||||
// * 2 avatars/participants should show in Thread Footer
|
||||
cy.get('.Avatar').should('have.lengthOf', 2);
|
||||
});
|
||||
|
||||
// # Visit global threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * There should be a single thread item
|
||||
cy.get('article.ThreadItem').should('have.lengthOf', 1).first().click().within(() => {
|
||||
// * Activity section in ThreadItem should say '2 replies'
|
||||
cy.get('.activity').should('have.text', '2 replies');
|
||||
|
||||
// * 3 avatars/participants should show in ThreadItem
|
||||
cy.get('.Avatar').should('have.lengthOf', 3);
|
||||
});
|
||||
|
||||
// * The reply should be in RHS showing '(message deleted)'
|
||||
cy.get(`#rhsPost_${replyPost2.id}`).should('be.visible').should('contain.text', messages.REPLY2);
|
||||
|
||||
// # Delete thread reply post
|
||||
cy.apiDeletePost(replyPost2.id);
|
||||
|
||||
// # Reload to re-render UI
|
||||
cy.reload(true);
|
||||
|
||||
// * There should be a single thread item
|
||||
cy.get('article.ThreadItem').should('have.lengthOf', 1).first().click().within(() => {
|
||||
// * Activity section in ThreadItem should say '1 reply'
|
||||
cy.get('.activity').should('have.text', '1 reply');
|
||||
|
||||
// * 2 avatars/participants should show in ThreadItem
|
||||
cy.get('.Avatar').should('have.lengthOf', 2);
|
||||
});
|
||||
|
||||
// # Visit the channel
|
||||
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
|
||||
|
||||
// # Get thread footer of last post
|
||||
cy.uiGetPostThreadFooter(rootPost.id).within(() => {
|
||||
// * Reply button in Thread Footer should say '1 reply'
|
||||
cy.get('.ReplyButton').should('have.text', '1 reply');
|
||||
|
||||
// * 1 avatar/participant should show in Thread Footer
|
||||
cy.get('.Avatar').should('have.lengthOf', 1);
|
||||
});
|
||||
|
||||
// # Cleanup for next test
|
||||
cy.apiDeletePost(rootPost.id);
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T4448_1 CRT - L16 - Use “Mark all as read” button', () => {
|
||||
cy.uiWaitUntilMessagePostedIncludes(rootPost.data.message);
|
||||
|
||||
// # Thread footer should not exist
|
||||
cy.uiGetPostThreadFooter(rootPost.id).should('not.exist');
|
||||
|
||||
// # Post a reply post as current user
|
||||
cy.postMessageAs({sender: user2, message: messages.REPLY1, channelId: testChannel.id, rootId: rootPost.id}).then((post) => {
|
||||
replyPost1 = post;
|
||||
|
||||
// # Get thread footer of last post
|
||||
cy.uiGetPostThreadFooter(rootPost.id).within(() => {
|
||||
// * Reply button in Thread Footer should say '1 reply'
|
||||
cy.get('.ReplyButton').should('have.text', '1 reply');
|
||||
|
||||
// * 1 avatar/participant should show in Thread Footer
|
||||
cy.get('.Avatar').should('have.lengthOf', 1);
|
||||
});
|
||||
|
||||
// # Visit global threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * The unreads tab button has a blue dot (unread indicator)
|
||||
cy.get('#threads-list-unread-button .dot').should('have.lengthOf', 1);
|
||||
|
||||
// * There should be a single thread item
|
||||
cy.get('article.ThreadItem').should('have.lengthOf', 1).within(() => {
|
||||
// * The unread indicator (blue dot) should be present
|
||||
cy.get('.dot-unreads').should('have.lengthOf', 1);
|
||||
|
||||
// * Activity section in ThreadItem should say '1 new reply'
|
||||
cy.get('.activity').should('have.text', '1 new reply');
|
||||
});
|
||||
|
||||
// # Get the "mark all as read" button (the selector is pretty ambiguous here, but should work for now)
|
||||
cy.get('#threads-list__mark-all-as-read').click();
|
||||
|
||||
// * mark_all_threads_as_read_modal is open
|
||||
cy.get('#mark-all-threads-as-read-modal').should('exist');
|
||||
|
||||
// # Click confirm button
|
||||
cy.get('button.confirm').contains('Mark all as read').click();
|
||||
|
||||
// * mark_all_threads_as_read_modal is closed
|
||||
cy.get('#mark-all-threads-as-read-modal').should('not.exist');
|
||||
|
||||
// * The unreads tab button does NOT have a blue dot (unread indicator)
|
||||
cy.get('#threads-list-unread-button .dot').should('not.exist');
|
||||
|
||||
// * There should be a single thread item
|
||||
cy.get('article.ThreadItem').should('have.lengthOf', 1).within(() => {
|
||||
// * The unread indicator (blue dot) should NOT be present
|
||||
cy.get('.dot-unreads').should('have.lengthOf', 0);
|
||||
|
||||
// * Activity section in ThreadItem should say '1 reply'
|
||||
cy.get('.activity').should('have.text', '1 reply');
|
||||
});
|
||||
|
||||
// * Assert that the RHS shows the correct view
|
||||
cy.get('.no-results__holder').should('contain.text', 'Looks like you’re all caught up');
|
||||
|
||||
// # Cleanup for next test
|
||||
cy.apiDeletePost(rootPost.id);
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T4448_2 CRT - L16 - Cancel “Mark all as unread” modal', () => {
|
||||
cy.uiWaitUntilMessagePostedIncludes(rootPost.data.message);
|
||||
|
||||
// # Thread footer should not exist
|
||||
cy.uiGetPostThreadFooter(rootPost.id).should('not.exist');
|
||||
|
||||
// # Post a reply post as current user
|
||||
cy.postMessageAs({sender: user2, message: messages.REPLY1, channelId: testChannel.id, rootId: rootPost.id}).then((post) => {
|
||||
replyPost1 = post;
|
||||
|
||||
// # Visit global threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * The unreads tab button has a blue dot (unread indicator)
|
||||
cy.get('#threads-list-unread-button .dot').should('have.lengthOf', 1);
|
||||
|
||||
// * There should be a single thread item
|
||||
cy.get('article.ThreadItem').should('have.lengthOf', 1).within(() => {
|
||||
// * The unread indicator (blue dot) should be present
|
||||
cy.get('.dot-unreads').should('have.lengthOf', 1);
|
||||
|
||||
// * Activity section in ThreadItem should say '1 new reply'
|
||||
cy.get('.activity').should('have.text', '1 new reply');
|
||||
});
|
||||
|
||||
// # Get the "mark all as read" button
|
||||
cy.get('#threads-list__mark-all-as-read').click();
|
||||
|
||||
// * Verify mark_all_threads_as_read_modal is open
|
||||
cy.get('#mark-all-threads-as-read-modal').should('exist');
|
||||
|
||||
// # Click cancel button
|
||||
cy.get('button.cancel').contains('Cancel').click();
|
||||
|
||||
// * Verify mark_all_threads_as_read_modal is closed
|
||||
cy.get('#mark-all-threads-as-read-modal').should('not.exist');
|
||||
|
||||
// * Verify the unreads tab button still has a blue dot
|
||||
cy.get('#threads-list-unread-button .dot').should('exist');
|
||||
|
||||
// # Click on “Mark all as unread” button again
|
||||
cy.get('#threads-list__mark-all-as-read').click();
|
||||
|
||||
// * Verify mark_all_threads_as_read_modal is open
|
||||
cy.get('#mark-all-threads-as-read-modal').should('exist');
|
||||
|
||||
// # Click close button
|
||||
cy.get('button.close').click();
|
||||
|
||||
// * Verify mark_all_threads_as_read_modal is closed
|
||||
cy.get('#mark-all-threads-as-read-modal').should('not.exist');
|
||||
|
||||
// * Verify the unreads tab button still has a blue dot
|
||||
cy.get('#threads-list-unread-button .dot').should('exist');
|
||||
|
||||
// # Click on “Mark all as unread” button again
|
||||
cy.get('#threads-list__mark-all-as-read').click();
|
||||
|
||||
// # Press ESC key to close the modal
|
||||
cy.get('body').type('{esc}');
|
||||
|
||||
// * Verify mark_all_threads_as_read_modal is closed
|
||||
cy.get('#mark-all-threads-as-read-modal').should('not.exist');
|
||||
|
||||
// * Verify the unreads tab button still has a blue dot
|
||||
cy.get('#threads-list-unread-button .dot').should('exist');
|
||||
|
||||
// * There should be a single thread item
|
||||
cy.get('article.ThreadItem').should('have.lengthOf', 1).within(() => {
|
||||
// * The unread indicator (blue dot) should be present
|
||||
cy.get('.dot-unreads').should('have.lengthOf', 1);
|
||||
|
||||
// * Activity section in ThreadItem should say '1 new reply'
|
||||
cy.get('.activity').should('have.text', '1 new reply');
|
||||
});
|
||||
});
|
||||
|
||||
// * Assert that the RHS shows the correct view
|
||||
cy.get('.no-results__holder').should('not.contain.text', 'Looks like you’re all caught up');
|
||||
|
||||
// # Cleanup for next test
|
||||
cy.apiDeletePost(rootPost.id);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import * as TIMEOUTS from '../../../fixtures/timeouts';
|
||||
|
||||
// ***************************************************************
|
||||
// - [#] 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 @collapsed_reply_threads
|
||||
|
||||
describe('Collapsed Reply Threads', () => {
|
||||
let userA; // Member of team A and B
|
||||
let teamA;
|
||||
let teamB;
|
||||
let offTopicUrlA;
|
||||
let testChannel;
|
||||
|
||||
before(() => {
|
||||
cy.apiUpdateConfig({
|
||||
ServiceSettings: {
|
||||
ThreadAutoFollow: true,
|
||||
CollapsedThreads: 'default_on',
|
||||
},
|
||||
});
|
||||
|
||||
cy.apiInitSetup().then(({team, user, offTopicUrl: url}) => {
|
||||
userA = user;
|
||||
teamA = team;
|
||||
offTopicUrlA = url;
|
||||
|
||||
cy.apiCreateUser().then(() => {
|
||||
return cy.apiCreateTeam('team', 'Team');
|
||||
}).then(({team: otherTeam}) => {
|
||||
teamB = otherTeam;
|
||||
return cy.apiAddUserToTeam(teamB.id, userA.id);
|
||||
}).then(() => {
|
||||
return cy.apiCreateChannel(teamA.id, 'test', 'Test');
|
||||
}).then(({channel}) => {
|
||||
testChannel = channel;
|
||||
return cy.apiAddUserToChannel(testChannel.id, userA.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// # Log in to Team A with an account that has joined multiple teams.
|
||||
cy.apiLogin(userA);
|
||||
|
||||
// # On an account on two teams, view Team A
|
||||
cy.visit(offTopicUrlA);
|
||||
});
|
||||
|
||||
it('MM-T4887 should stay on threads view when switching teams', () => {
|
||||
// # Navigate to the new teams town square
|
||||
cy.visit(`/${teamA.name}/channels/town-square`);
|
||||
|
||||
// # Switch to Team B
|
||||
cy.get(`#${teamB.name}TeamButton`, {timeout: TIMEOUTS.ONE_MIN}).should('be.visible').click();
|
||||
|
||||
// * Verify team display name changes correctly.
|
||||
cy.uiGetLHSHeader().findByText(teamB.display_name);
|
||||
|
||||
// # Go to the ‘Threads’ view on Team B
|
||||
cy.uiGetSidebarThreadsButton().click();
|
||||
|
||||
cy.wait(TIMEOUTS.ONE_SEC);
|
||||
|
||||
// # Switch back to Team A
|
||||
cy.get(`#${teamA.name}TeamButton`, {timeout: TIMEOUTS.ONE_MIN}).should('be.visible').click();
|
||||
|
||||
// Verify url is set up for threads view
|
||||
cy.url().should('include', `${teamA.name}/threads`);
|
||||
});
|
||||
|
||||
it('MM-T4843_1 should go to threads view when switching a team if that was the last view on that team', () => {
|
||||
// # Go to the ‘Threads’ view on Team A
|
||||
cy.uiGetSidebarThreadsButton().click();
|
||||
|
||||
// # Switch to Team B
|
||||
cy.get(`#${teamB.name}TeamButton`, {timeout: TIMEOUTS.ONE_MIN}).should('be.visible').click();
|
||||
|
||||
// * Verify team display name changes correctly.
|
||||
cy.uiGetLHSHeader().findByText(teamB.display_name);
|
||||
|
||||
// # Switch back to Team A
|
||||
cy.get(`#${teamA.name}TeamButton`, {timeout: TIMEOUTS.ONE_MIN}).should('be.visible').click();
|
||||
|
||||
// Verify url is set up for threads view
|
||||
cy.url().should('include', `${teamA.name}/threads`);
|
||||
});
|
||||
|
||||
it('MM-T4843_2 should go to threads view when threads view is the penultimate view and leave the current channel', () => {
|
||||
// # Go to the ‘Threads’ view on Team A
|
||||
cy.uiGetSidebarThreadsButton().click();
|
||||
|
||||
// # Switch to Test Channel
|
||||
cy.uiClickSidebarItem(testChannel.name);
|
||||
|
||||
// # Leave the current channel
|
||||
cy.uiLeaveChannel();
|
||||
|
||||
// Verify url is set up for threads view when thread view is the penultimate view
|
||||
cy.url().should('include', `${teamA.name}/threads`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,177 @@
|
||||
// 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 @collapsed_reply_threads
|
||||
|
||||
describe('Collapsed Reply Threads', () => {
|
||||
let testTeam;
|
||||
let testUser;
|
||||
let otherUser;
|
||||
let testChannel;
|
||||
let rootPost;
|
||||
let postForAvatar;
|
||||
|
||||
before(() => {
|
||||
cy.apiUpdateConfig({
|
||||
ServiceSettings: {
|
||||
ThreadAutoFollow: true,
|
||||
CollapsedThreads: 'default_off',
|
||||
},
|
||||
});
|
||||
|
||||
// # Create new channel and other user, and add other user to channel
|
||||
cy.apiInitSetup({loginAfter: true, promoteNewUserAsAdmin: true}).then(({team, channel, user}) => {
|
||||
testTeam = team;
|
||||
testUser = user;
|
||||
testChannel = channel;
|
||||
|
||||
cy.apiSaveCRTPreference(testUser.id, 'on');
|
||||
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
|
||||
otherUser = user1;
|
||||
|
||||
cy.apiAddUserToTeam(testTeam.id, otherUser.id).then(() => {
|
||||
cy.apiAddUserToChannel(testChannel.id, otherUser.id);
|
||||
|
||||
// # Post a message as other user
|
||||
cy.postMessageAs({sender: otherUser, message: 'Root post', channelId: testChannel.id}).then((post) => {
|
||||
rootPost = post;
|
||||
});
|
||||
|
||||
// # Post a message as other user for clicking avatar test
|
||||
cy.postMessageAs({sender: otherUser, message: 'Root post for clicking avatar', channelId: testChannel.id}).then((post) => {
|
||||
postForAvatar = post;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// # Visit the channel
|
||||
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
|
||||
});
|
||||
|
||||
it('MM-T4142 should show number of replies in thread', () => {
|
||||
cy.uiWaitUntilMessagePostedIncludes(rootPost.data.message);
|
||||
|
||||
// # Thread footer should not exist
|
||||
cy.uiGetPostThreadFooter(rootPost.id).should('not.exist');
|
||||
|
||||
// # Post a reply post as current user
|
||||
cy.postMessageAs({sender: testUser, message: 'reply!', channelId: testChannel.id, rootId: rootPost.id});
|
||||
|
||||
// # Get thread footer of last post
|
||||
cy.uiGetPostThreadFooter(rootPost.id).within(() => {
|
||||
// * Reply button in Thread Footer should say '1 reply'
|
||||
cy.get('.ReplyButton').should('have.text', '1 reply');
|
||||
|
||||
// * 1 avatar/participant should show in Thread Footer
|
||||
cy.get('.Avatar').should('have.lengthOf', 1);
|
||||
});
|
||||
|
||||
// # Visit global threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * The sole thread item should have text in footer saying '1 reply'
|
||||
cy.get('article.ThreadItem').find('.activity').should('have.text', '1 reply');
|
||||
|
||||
// # Visit the channel
|
||||
cy.uiClickSidebarItem(testChannel.name);
|
||||
|
||||
// # Post another reply as current user
|
||||
cy.postMessageAs({sender: testUser, message: 'another reply!', channelId: testChannel.id, rootId: rootPost.id});
|
||||
|
||||
// # Get thread footer of last post
|
||||
cy.uiGetPostThreadFooter(rootPost.id).within(() => {
|
||||
// * Reply button in thread footer should say '2 replies'
|
||||
cy.get('.ReplyButton').should('have.text', '2 replies');
|
||||
|
||||
// * 1 avatar/participant should show in Thread Footer
|
||||
cy.get('.Avatar').should('have.lengthOf', 1);
|
||||
});
|
||||
|
||||
// # Visit global threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * The sole thread item should have text in footer saying '2 replies'
|
||||
cy.get('article.ThreadItem').find('.activity').should('have.text', '2 replies');
|
||||
|
||||
// # Visit the channel
|
||||
cy.uiClickSidebarItem(testChannel.name);
|
||||
|
||||
// # Post another reply as other user
|
||||
cy.postMessageAs({sender: otherUser, message: 'other reply!', channelId: testChannel.id, rootId: rootPost.id});
|
||||
|
||||
// # Get thread footer of last post
|
||||
cy.uiGetPostThreadFooter(rootPost.id).within(() => {
|
||||
// * Reply button in thread footer should say '3 replies'
|
||||
cy.get('.ReplyButton').should('have.text', '3 replies');
|
||||
|
||||
// * 2 avatars/participants should show in Thread Footer
|
||||
cy.get('.Avatar').should('have.lengthOf', 2);
|
||||
});
|
||||
|
||||
// # Visit global threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * The sole thread item should have text in footer saying '1 new reply'
|
||||
cy.get('article.ThreadItem').find('.activity').should('have.text', '1 new reply');
|
||||
});
|
||||
|
||||
it('MM-T4646 should open popover when avatar is clicked', () => {
|
||||
cy.uiWaitUntilMessagePostedIncludes(postForAvatar.data.message);
|
||||
|
||||
// # Post a reply post as current user
|
||||
cy.postMessageAs({sender: testUser, message: 'reply!', channelId: testChannel.id, rootId: postForAvatar.id});
|
||||
|
||||
// # Post another reply as other user
|
||||
cy.postMessageAs({sender: otherUser, message: 'another reply!', channelId: testChannel.id, rootId: postForAvatar.id});
|
||||
|
||||
// # Get thread footer of last post and find avatars
|
||||
cy.uiGetPostThreadFooter(postForAvatar.id).find('.Avatars').find('button').first().click();
|
||||
|
||||
// * Profile popover should be visible and close on ESC
|
||||
cy.get('div.user-profile-popover').first().should('be.visible').find('#messageButton').type('{esc}');
|
||||
|
||||
// # Visit global threads
|
||||
cy.uiClickSidebarItem('threads');
|
||||
|
||||
// * Find the first avatar and click it
|
||||
cy.get('article.ThreadItem').find('.activity').find('.Avatars').find('button').first().click();
|
||||
|
||||
// * Profile popover should be visible and close on ESC
|
||||
cy.get('div.user-profile-popover').first().should('be.visible').find('#messageButton').type('{esc}');
|
||||
});
|
||||
|
||||
it('MM-T4143 Emoji reaction - type +:+1:', () => {
|
||||
// # Create a root post
|
||||
cy.postMessage('Hello!');
|
||||
|
||||
cy.getLastPostId().then((postId) => {
|
||||
// # Click on post to open the thread in RHS
|
||||
cy.get(`#post_${postId}`).click();
|
||||
|
||||
// # Type "+:+1:" in comment box to react to the post with a thumbs-up and post
|
||||
cy.postMessageReplyInRHS('+:+1:');
|
||||
|
||||
// * Thumbs-up reaction displays as reaction on post
|
||||
cy.get(`#${postId}_message`).within(() => {
|
||||
cy.findByLabelText('reactions').should('be.visible');
|
||||
cy.findByLabelText('remove reaction +1').should('be.visible');
|
||||
});
|
||||
|
||||
// * Reacting to a root post should not create a thread (thread footer should not exist)
|
||||
cy.uiGetPostThreadFooter(postId).should('not.exist');
|
||||
|
||||
// # Close RHS
|
||||
cy.uiCloseRHS();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
// 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 @collapsed_reply_threads
|
||||
|
||||
import * as TIMEOUTS from '../../../fixtures/timeouts';
|
||||
|
||||
describe('Collapsed Reply Threads', () => {
|
||||
let testTeam;
|
||||
let testUser;
|
||||
let otherUser;
|
||||
let testChannel;
|
||||
|
||||
before(() => {
|
||||
cy.apiUpdateConfig({
|
||||
ServiceSettings: {
|
||||
ThreadAutoFollow: true,
|
||||
CollapsedThreads: 'default_off',
|
||||
},
|
||||
});
|
||||
|
||||
// # Create new channel and other user, and add other user to channel
|
||||
cy.apiInitSetup({loginAfter: true, promoteNewUserAsAdmin: true}).then(({team, channel, user}) => {
|
||||
testTeam = team;
|
||||
testUser = user;
|
||||
testChannel = channel;
|
||||
|
||||
cy.apiSaveCRTPreference(testUser.id, 'on');
|
||||
|
||||
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
|
||||
otherUser = user1;
|
||||
|
||||
cy.apiAddUserToTeam(testTeam.id, otherUser.id).then(() => {
|
||||
cy.apiAddUserToChannel(testChannel.id, otherUser.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// # Visit channel
|
||||
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
|
||||
});
|
||||
|
||||
it('MM-T4144_1 should show a new messages line for an unread thread', () => {
|
||||
// # Post a root post as current user
|
||||
cy.postMessageAs({
|
||||
sender: testUser,
|
||||
message: 'Another interesting post,',
|
||||
channelId: testChannel.id,
|
||||
}).then(({id: rootId}) => {
|
||||
// # Post multiple replies as other user so that the new messages line is pushed up
|
||||
Cypress._.times(20, (i) => {
|
||||
cy.postMessageAs({
|
||||
sender: otherUser,
|
||||
message: 'Reply ' + i,
|
||||
channelId: testChannel.id,
|
||||
rootId,
|
||||
});
|
||||
});
|
||||
|
||||
// # Click root post
|
||||
cy.get(`#post_${rootId}`).click();
|
||||
|
||||
// # Wait for RHS to open and scroll to position
|
||||
cy.wait(TIMEOUTS.ONE_SEC);
|
||||
|
||||
// * RHS should open and new messages line should be visible
|
||||
cy.get('#rhsContainer').findByTestId('NotificationSeparator').should('be.visible');
|
||||
|
||||
// # Close RHS
|
||||
cy.uiCloseRHS();
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T4144_2 should not show a new messages line after viewing the thread', () => {
|
||||
// # Get last message in channel
|
||||
cy.getLastPostId().then((rootId) => {
|
||||
// # Click on message
|
||||
cy.get(`#post_${rootId}`).click();
|
||||
|
||||
// * RHS should open and new messages line should NOT be visible
|
||||
cy.get('#rhsContainer').findByTestId('NotificationSeparator').should('not.exist');
|
||||
|
||||
// # Close RHS
|
||||
cy.uiCloseRHS();
|
||||
});
|
||||
});
|
||||
});
|
||||
Ссылка в новой задаче
Block a user