mirror of
https://github.com/hanzoai/team.git
synced 2026-09-30 01:57:35 +00:00
* Dependencies: Remove unused dependency on @mattermost/dynamic-virtualized-list * Update Components package to React 18 and new version of RTL * Upgrade React, React Redux, RTL, and associated libraries I had to upgrade React Redux for the new version of React, and that brought with it new versions of associated packges (Redux, Reselect, Redux Thunk). A few other libraries needed to be updated or have their explicit dependencies overridden for the new version of Redux as well. To note for future dependency upgrades, redux-mock-store is deprecated, and redux-batched-actions and redux-persist are inactive. For RTL: 1. `@testing-library/react-hooks` has been rolled into `@testing-library/react`, and its interface has changed. 2. I had to make some changes to get TS to use the types for the new methods added to `expect`. * Dependencies: Fix dom-accessibility-api patch from #33553 * Tests: Fix tests that use jest.spyOn with react-redux * Functional: Remove usage of defaultProps on function components * Tests: Remove usage of react-dom/test-utils * Functional: Rename conflicting context prop on Apps components * Tests: Always await on userEvent calls * Functional: Patch react-overlays to fix pressing escape in unit tests I did something similar in React Bootstrap a few weeks ago. See https://github.com/mattermost/react-bootstrap/pull/5 * Tests: Prevent tests from fetching from real URLs * Tests: Update snapshots changed by upgrading react-redux and styled-components * Functional: Stop passing deprecated pure parameter to connect * Tests: Change how we intercept console errors in tests to preserve stack traces * Tests: Fix incorrect usage of act in Enzyme tests These tests are a mix of: 1. Not calling act when performing something that will update the DOM (like clicking on a button or invoking a method) which either caused warnings or failed snapshots as changes weren't visible. 2. Calling act in weird ways (such as wrapping mount in an async act) which caused Enzyme to not function Some of these changes just silence warnings, but most of them are required to make the test even run * Tests: Fix incorrect usage of act in RTL tests * Tests: Fix a few minor issues in tests * Functional: Add note for why we're not using ReactDOM.createRoot * Functional: Fix focus trap infinite recursion in test * Types: Replace removed React.SFC * Types: Fix type of functions in FormattedMessage values prop * Functional: Fix DropdownInputHybrid placeholder * Types: Patch type definitions of react-batched-actions * Types: Fix mattermost-redux build failing due to type check in Redux Dev Tools * Dependencies: Add type definitions for react-is * Types: Update types around ReactNode and ReactElement React's gotten more strict with these, so we need to be more careful with what we return from a render method. In some of these places, we also misused some types, so hopefully I've sorted those out. * Types: Explicitly added types to all instances of useCallback * Types/Tests: Update typing of Redux actions and hooks useDispatch is sort of stricter now, but it doesn't seem to rely on the global type of `Dispatch` any more, so I ended up having to add an extra overload to make that work. * Tests: Update new tests in useChannelSystemPolicies for new version of RTL These were added on master after I updated RTL on the branch * Tests: Update action used to test initial store state * Functional: Remove remnants of code for hot reloading Redux store * Types/Tests: Update typing around React Router * Types/Functional: Update typing involving the FormattedMessage values prop There's a couple functional changes to ensure that the value passed is either a valid string or Date * Types: Misc fixes * Functional: Don't pass unused props to ChannelHeader * Functional: Ensure plugin setting text is rendered before passing to Setting component The previous version might've allowed MessageDescriptors to be passed unformatted into the Setting component (which would then be rendered in the DOM). As best as I can tell, we never actually did that, so this shouldn't change anything in practice. * Tests: Make tests for identifyElementRegion more reliable * Tests: Update recent tests for new version of RTL * E2E: Make editLastPostWithNewMessage more reliable * Downgrade React to 18.2.0 and manually dedupe React versions 18.2.0 and 18.3.0 are nearly identical to one another, except 18.3.x includes warnings when using any API that will be removed in React 19. I don't want to flood the console and test logs with warnings for things we're not addressing for the time being. Also, they didn't export act from React itself until 18.3.1 for some reason (despite the old import path printing a warning), so I needed to revert the changes to its import path. To get this all to work, for some reason, I had to manually delete all the entries for `react` and `react-dom` from the lockfile to get NPM to use a single version of React and ReactDOM everywhere. I did discover `npm dedupe` in the process, but that didn't solve this problem where I was trying to consistently downgrade everything. * Revert changes to notice file build tool to speed up CI * Add explicit version of types/scheduler The version of `@types/react` that we use says it works with any version of `@types/scheduler` which causes NPM to install a newer version of it which is missing a file of types that it needs. * Update new test to await on userEvent * Fix Playwright test that relied on autogenerated class name * Tests: Disable test for identifyElementRegion * Functional: Change DynamicVirtualizedList ListItem to use useLayoutEffect In a previous commit, I changed the RHS to use the monorepo DynamicVirtualizedList instead of the old version that lived in its own repo. That caused the RHS to no longer scroll to the bottom on initial mount or be able to render additional pages (even though the posts are loaded). This seems like it has to do with the improved size calculation that Zubair made because that's the main difference in the monorepo version of that component. For some reason I don't entirely understand, changing to useLayoutEffect seems to fix that. I think that's because the old measurement code is written as a class component, and useLayoutEffect fires at the same time as componentDidMount/componentDidUpdate. * Types: Revert some type changes to reduce test log output * Functional: Fix places where useSelector returned new results when called with the same arguments I wasn't planning on fixing this now, but I think the increased length of the warning logs in the tests are causing the GitHub action for the unit tests to abort as it reaches around 10000 lines long. * Tests: Fix place where mocked selector returned new results when called with the same arguments Same reason as before, but this one only occurred because of a test's mocked selector. I replaced it with a real one to get around that. * Tests: Fail tests when selector returns new results when called with the same arguments * Attempt to fix web app unit tests failing in CI * Change CI tests to set workerIdleMemoryLimit instead of reducing maxWorkers * Increase workerIdleMemoryLimit in CI * Revert changes to test-ci command and revert changes to how unit test logs are collected * Unrevert changes to test logging, re-add workerIdleMemoryLimit, and try disabling test coverage * Actually disable coverage * Fix flaky test * Update a couple new tests to fit PR and remove an unnecessary act * Replace bad mock in new unit test * Fix types of new code * Remove mock from new unit test
281 lines
9.7 KiB
TypeScript
281 lines
9.7 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {ChainableT} from '../../types';
|
|
|
|
function uiGetPostTextBox(option = {exist: true}): ChainableT<JQuery> {
|
|
if (option.exist) {
|
|
return cy.get('#post_textbox').should('be.visible');
|
|
}
|
|
|
|
return cy.get('#post_textbox').should('not.exist');
|
|
}
|
|
Cypress.Commands.add('uiGetPostTextBox', uiGetPostTextBox);
|
|
|
|
function uiGetReplyTextBox(option = {exist: true}): ChainableT<JQuery> {
|
|
if (option.exist) {
|
|
return cy.get('#reply_textbox').should('be.visible');
|
|
}
|
|
|
|
return cy.get('#reply_textbox').should('not.exist');
|
|
}
|
|
Cypress.Commands.add('uiGetReplyTextBox', uiGetReplyTextBox);
|
|
|
|
function uiGetPostProfileImage(postId: string): ChainableT<JQuery> {
|
|
return getPost(postId).within(() => {
|
|
return cy.get('.post__img').should('be.visible');
|
|
});
|
|
}
|
|
Cypress.Commands.add('uiGetPostProfileImage', uiGetPostProfileImage);
|
|
|
|
function uiGetPostHeader(postId: string): ChainableT<JQuery> {
|
|
return getPost(postId).within(() => {
|
|
return cy.get('.post__header').should('be.visible');
|
|
});
|
|
}
|
|
Cypress.Commands.add('uiGetPostHeader', uiGetPostHeader);
|
|
|
|
function uiGetPostBody(postId: string): ChainableT<JQuery> {
|
|
return getPost(postId).within(() => {
|
|
return cy.get('.post__body').should('be.visible');
|
|
});
|
|
}
|
|
Cypress.Commands.add('uiGetPostBody', uiGetPostBody);
|
|
|
|
function uiGetPostThreadFooter(postId: string): ChainableT<JQuery> {
|
|
return getPost(postId).find('.ThreadFooter');
|
|
}
|
|
Cypress.Commands.add('uiGetPostThreadFooter', uiGetPostThreadFooter);
|
|
|
|
function uiGetPostEmbedContainer(postId: string): ChainableT<JQuery> {
|
|
return cy.uiGetPostBody(postId).
|
|
find('.file-preview__button').
|
|
should('be.visible');
|
|
}
|
|
Cypress.Commands.add('uiGetPostEmbedContainer', uiGetPostEmbedContainer);
|
|
|
|
function getPost(postId: string): ChainableT<JQuery> {
|
|
if (postId) {
|
|
return cy.get(`#post_${postId}`).should('be.visible');
|
|
}
|
|
|
|
return cy.getLastPost();
|
|
}
|
|
Cypress.Commands.add('getPost', getPost);
|
|
|
|
function editLastPostWithNewMessage(message: string) {
|
|
cy.uiGetPostTextBox().type('{uparrow}');
|
|
|
|
// * Edit Post Input should appear
|
|
cy.get('#edit_textbox').should('be.visible');
|
|
|
|
// # Update the post message and click Save
|
|
cy.get('#edit_textbox').clear().type(message)
|
|
cy.get('#create_post').findByText('Save').scrollIntoView().click();
|
|
}
|
|
Cypress.Commands.add('editLastPostWithNewMessage', editLastPostWithNewMessage);
|
|
|
|
export function verifySavedPost(postId, message) {
|
|
// * Check that the center save icon has been updated correctly
|
|
cy.get(`#post_${postId}`).trigger('mouseover', {force: true});
|
|
cy.get(`#CENTER_flagIcon_${postId}`).
|
|
should('have.class', 'post-menu__item').
|
|
and('have.attr', 'aria-label', 'remove from saved');
|
|
|
|
// # Open the post-dotmenu
|
|
cy.clickPostDotMenu(postId, 'CENTER');
|
|
|
|
// * Check that the dotmenu item is changed accordingly
|
|
cy.findAllByTestId(`post-menu-${postId}`).eq(0).should('be.visible');
|
|
cy.findByText('Remove from Saved').scrollIntoView().should('be.visible');
|
|
cy.get('body').type('{esc}');
|
|
|
|
cy.get('#postListContent').within(() => {
|
|
// * Check that the post is highlighted
|
|
cy.get(`#post_${postId}`).should('have.class', 'post--pinned-or-flagged');
|
|
|
|
// * Check that the post pre-header is visible
|
|
cy.get('div.post-pre-header').should('be.visible');
|
|
|
|
// * Check that the post pre-header has the saved icon
|
|
cy.get('span.icon--post-pre-header').
|
|
should('be.visible').
|
|
within(() => {
|
|
cy.get('svg').should('have.attr', 'aria-label', 'Saved Icon');
|
|
});
|
|
|
|
// * Check that the post pre-header has the saved post link
|
|
cy.get('div.post-pre-header__text-container').
|
|
should('be.visible').
|
|
and('have.text', 'Saved').
|
|
within(() => {
|
|
cy.get('a').as('savedLink').should('be.visible');
|
|
});
|
|
});
|
|
|
|
// * Check that the saved posts list is not open in RHS before clicking the link in the post pre-header
|
|
cy.get('#searchContainer').should('not.exist');
|
|
|
|
// # Click the link
|
|
cy.get('@savedLink').click();
|
|
|
|
// * Check that the saved posts list is open in RHS
|
|
cy.get('#searchContainer').should('be.visible').within(() => {
|
|
cy.get('.sidebar--right__title').
|
|
should('be.visible').
|
|
and('have.text', 'Saved messages');
|
|
|
|
// * Check that the post pre-header is not shown for the saved message in RHS
|
|
cy.get(`#searchResult_${postId}`).within(() => {
|
|
cy.get(`#rhsPostMessageText_${postId}`).contains(message);
|
|
cy.get('div.post-pre-header').should('not.exist');
|
|
});
|
|
});
|
|
|
|
// # Close the RHS
|
|
cy.get('#searchResultsCloseButton').should('be.visible').click();
|
|
}
|
|
|
|
export function verifyUnsavedPost(postId) {
|
|
// * Check that the center save icon has been updated correctly
|
|
cy.get(`#post_${postId}`).trigger('mouseover', {force: true});
|
|
cy.get(`#CENTER_flagIcon_${postId}`).
|
|
should('have.class', 'post-menu__item').
|
|
and('have.attr', 'aria-label', 'save message');
|
|
|
|
// # Open the post-dotmenu
|
|
cy.clickPostDotMenu(postId, 'CENTER');
|
|
|
|
// * Check that the dotmenu item is changed accordingly
|
|
cy.findAllByTestId(`post-menu-${postId}`).eq(0).should('be.visible');
|
|
cy.findByText('Save Message').scrollIntoView().should('be.visible');
|
|
cy.get('body').type('{esc}');
|
|
|
|
cy.get('#postListContent').within(() => {
|
|
// * Check that the post is not highlighted
|
|
cy.get(`#post_${postId}`).should('not.have.class', 'post--pinned-or-flagged');
|
|
|
|
// * Check that the post pre-header is not visible
|
|
cy.get('div.post-pre-header').should('not.exist');
|
|
|
|
// * Check that the post pre-header does not have the saved icon
|
|
cy.get('span.icon--post-pre-header').
|
|
should('not.exist');
|
|
|
|
// * Check that the post pre-header does not have the saved post link
|
|
cy.get('div.post-pre-header__text-container').
|
|
should('not.exist');
|
|
});
|
|
|
|
// * Check that the saved posts list is not open in RHS before clicking the link in the post pre-header
|
|
cy.get('#searchContainer').should('not.exist');
|
|
|
|
// # Click the link
|
|
cy.uiGetSavedPostButton().click();
|
|
|
|
// * Check that the saved posts list is open in RHS
|
|
cy.get('#searchContainer').should('be.visible').within(() => {
|
|
cy.get('.sidebar--right__title').
|
|
should('be.visible').
|
|
and('have.text', 'Saved messages');
|
|
|
|
// * Check that the post pre-header is not shown for the saved message in RHS
|
|
cy.get('#search-items-container').within(() => {
|
|
cy.get(`#rhsPostMessageText_${postId}`).should('not.exist');
|
|
});
|
|
});
|
|
|
|
// # Close the RHS
|
|
cy.get('#searchResultsCloseButton').should('be.visible').click();
|
|
}
|
|
|
|
declare global {
|
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
namespace Cypress {
|
|
interface Chainable {
|
|
|
|
/**
|
|
* Edit last post with a new message
|
|
*
|
|
* @param {string} - message
|
|
*
|
|
* @example
|
|
* cy.editLastPostWithNewMessage('new message');
|
|
*/
|
|
editLastPostWithNewMessage: typeof editLastPostWithNewMessage;
|
|
|
|
/**
|
|
* Get post profile image of a given post ID or the last post if post ID is not given
|
|
*
|
|
* @param {string} - postId (optional)
|
|
*
|
|
* @example
|
|
* cy.uiGetPostProfileImage();
|
|
*/
|
|
uiGetPostProfileImage: typeof uiGetPostProfileImage;
|
|
|
|
/**
|
|
* Get post header of a given post ID or the last post if post ID is not given
|
|
*
|
|
* @param {string} - postId (optional)
|
|
*
|
|
* @example
|
|
* cy.uiGetPostHeader();
|
|
*/
|
|
uiGetPostHeader: typeof uiGetPostHeader;
|
|
|
|
/**
|
|
* Get post body of a given post ID or the last post if post ID is not given
|
|
*
|
|
* @param {string} - postId (optional)
|
|
*
|
|
* @example
|
|
* cy.uiGetPostBody();
|
|
*/
|
|
uiGetPostBody: typeof uiGetPostBody;
|
|
|
|
/**
|
|
* Get post thread footer of a given post ID or the last post if post ID is not given
|
|
*
|
|
* @param {string} - postId (optional)
|
|
*
|
|
* @example
|
|
* cy.uiGetPostThreadFooter();
|
|
*/
|
|
uiGetPostThreadFooter: typeof uiGetPostThreadFooter;
|
|
|
|
/**
|
|
* Get post embed container of a given post ID or the last post if post ID is not given
|
|
*
|
|
* @param {string} - postId (optional)
|
|
*
|
|
* @example
|
|
* cy.uiGetPostEmbedContainer();
|
|
*/
|
|
uiGetPostEmbedContainer: typeof uiGetPostEmbedContainer;
|
|
|
|
/**
|
|
* Get post textbox
|
|
*
|
|
* @param {bool} option.exist - Set to false to check whether element should not exist. Otherwise, true (default) to check visibility.
|
|
*
|
|
* @example
|
|
* cy.uiGetPostTextBox();
|
|
*/
|
|
uiGetPostTextBox: typeof uiGetPostTextBox;
|
|
|
|
/**
|
|
* Get reply textbox
|
|
*
|
|
* @param {bool} option.exist - Set to false to check whether element should not exist. Otherwise, true (default) to check visibility.
|
|
*
|
|
* @example
|
|
* cy.uiGetReplyTextBox();
|
|
*/
|
|
uiGetReplyTextBox: typeof uiGetReplyTextBox;
|
|
|
|
getPost: typeof getPost;
|
|
}
|
|
}
|
|
}
|