Draft link activation (#30547)
* activated draft LHS item on scheduled post tab * Added tests * removed unused snapshot --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
6c8235d031
Коммит
619655f567
@@ -1,37 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/drafts/drafts_link should match snapshot 1`] = `
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
"store": Object {
|
||||
"clearActions": [Function],
|
||||
"dispatch": [Function],
|
||||
"getActions": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
},
|
||||
"subscription": Subscription {
|
||||
"handleChangeWrapper": [Function],
|
||||
"listeners": Object {
|
||||
"notify": [Function],
|
||||
},
|
||||
"onStateChange": [Function],
|
||||
"parentSub": undefined,
|
||||
"store": Object {
|
||||
"clearActions": [Function],
|
||||
"dispatch": [Function],
|
||||
"getActions": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
},
|
||||
"unsubscribe": null,
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
<Memo(DraftsLink) />
|
||||
</ContextProvider>
|
||||
`;
|
||||
@@ -1,23 +1,241 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import {Provider} from 'react-redux';
|
||||
import {MemoryRouter, Route} from 'react-router-dom';
|
||||
|
||||
import mockStore from 'tests/test_store';
|
||||
import type {DeepPartial} from '@mattermost/types/utilities';
|
||||
|
||||
import {renderWithContext, screen, waitFor} from 'tests/react_testing_utils';
|
||||
import {SCHEDULED_POST_URL_SUFFIX} from 'utils/constants';
|
||||
import {TestHelper} from 'utils/test_helper';
|
||||
|
||||
import type {GlobalState} from 'types/store';
|
||||
|
||||
import DraftsLink from './drafts_link';
|
||||
|
||||
describe('components/drafts/drafts_link', () => {
|
||||
it('should match snapshot', () => {
|
||||
const store = mockStore();
|
||||
// Mock the actions that are dispatched
|
||||
jest.mock('actions/views/drafts', () => ({
|
||||
getDrafts: jest.fn(() => ({type: 'MOCK_GET_DRAFTS'})),
|
||||
}));
|
||||
|
||||
const wrapper = shallow(
|
||||
<Provider store={store}>
|
||||
jest.mock('mattermost-redux/actions/scheduled_posts', () => ({
|
||||
fetchTeamScheduledPosts: jest.fn(() => ({type: 'MOCK_FETCH_SCHEDULED_POSTS'})),
|
||||
}));
|
||||
|
||||
// Base state with all required properties
|
||||
const baseState: DeepPartial<GlobalState> = {
|
||||
entities: {
|
||||
general: {
|
||||
config: {
|
||||
ScheduledPosts: 'true',
|
||||
},
|
||||
license: {
|
||||
IsLicensed: 'true',
|
||||
},
|
||||
},
|
||||
preferences: {
|
||||
myPreferences: {},
|
||||
},
|
||||
teams: {
|
||||
currentTeamId: 'team1',
|
||||
teams: {
|
||||
team1: TestHelper.getTeamMock({id: 'team1'}),
|
||||
},
|
||||
},
|
||||
users: {
|
||||
currentUserId: 'user1',
|
||||
profiles: {
|
||||
user1: TestHelper.getUserMock({id: 'user1'}),
|
||||
},
|
||||
},
|
||||
scheduledPosts: {},
|
||||
channels: {
|
||||
channels: {
|
||||
channel_id_1: TestHelper.getChannelMock({id: 'channel_id_1', type: 'O'}),
|
||||
},
|
||||
channelsInTeam: {
|
||||
team1: new Set(['channel_id_1']),
|
||||
},
|
||||
myMembers: {
|
||||
channel_id_1: {channel_id: 'channel_id_1', user_id: 'user1'},
|
||||
},
|
||||
},
|
||||
},
|
||||
views: {
|
||||
drafts: {
|
||||
remotes: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Helper function to render the component with router
|
||||
const renderWithRouter = (state: any, initialEntries = ['/team1/channels/town-square']) => {
|
||||
return renderWithContext(
|
||||
<MemoryRouter initialEntries={initialEntries}>
|
||||
<Route path='/:team'>
|
||||
<DraftsLink/>
|
||||
</Provider>,
|
||||
</Route>
|
||||
</MemoryRouter>,
|
||||
state,
|
||||
);
|
||||
};
|
||||
|
||||
describe('components/drafts/drafts_link', () => {
|
||||
it('should not render when no drafts or scheduled posts exist', () => {
|
||||
renderWithRouter(baseState);
|
||||
|
||||
expect(screen.queryByText('Drafts')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render when drafts exist', () => {
|
||||
const state: DeepPartial<GlobalState> = {
|
||||
...baseState,
|
||||
storage: {
|
||||
storage: {
|
||||
draft_draft1: {timestamp: new Date(), value: {message: 'Draft message', show: true, channelId: 'channel_id_1'}},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderWithRouter(state);
|
||||
|
||||
expect(screen.getByText('Drafts')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('draftIcon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render when scheduled posts exist', () => {
|
||||
const state: DeepPartial<GlobalState> = {
|
||||
...baseState,
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
scheduledPosts: {
|
||||
byTeamId: {
|
||||
team1: ['scheduled_post1', 'scheduled_post2'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderWithRouter(state);
|
||||
|
||||
expect(screen.getByText('Drafts')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('scheduledPostIcon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show scheduled posts badge when scheduled posts are disabled', () => {
|
||||
const state = {
|
||||
...baseState,
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
scheduledPosts: {
|
||||
byTeamId: {
|
||||
team1: ['scheduled_post1', 'scheduled_post2'],
|
||||
},
|
||||
},
|
||||
general: {
|
||||
config: {
|
||||
ScheduledPosts: 'false',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderWithRouter(state);
|
||||
|
||||
expect(screen.queryByTestId('scheduledPostIcon')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show scheduled posts badge when not licensed', () => {
|
||||
const state = {
|
||||
...baseState,
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
scheduledPosts: {
|
||||
byTeamId: {
|
||||
team1: ['scheduled_post1', 'scheduled_post2'],
|
||||
},
|
||||
},
|
||||
general: {
|
||||
license: {
|
||||
IsLicensed: 'false',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderWithRouter(state);
|
||||
|
||||
expect(screen.queryByTestId('scheduledPostIcon')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show error indicator when scheduled posts have errors', () => {
|
||||
const state: DeepPartial<GlobalState> = {
|
||||
...baseState,
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
scheduledPosts: {
|
||||
byTeamId: {
|
||||
team1: ['scheduled_post1', 'scheduled_post2'],
|
||||
},
|
||||
errorsByTeamId: {
|
||||
team1: ['scheduled_post1'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderWithRouter(state);
|
||||
|
||||
const badge = screen.getByTestId('scheduledPostIcon').closest('.scheduledPostBadge');
|
||||
expect(badge).toHaveClass('persistent');
|
||||
});
|
||||
|
||||
it('should fetch scheduled posts when component mounts', async () => {
|
||||
const fetchTeamScheduledPosts = require('mattermost-redux/actions/scheduled_posts').fetchTeamScheduledPosts;
|
||||
|
||||
renderWithRouter(baseState);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetchTeamScheduledPosts).toHaveBeenCalledWith('team1', true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be active when on drafts route', () => {
|
||||
const state: DeepPartial<GlobalState> = {
|
||||
...baseState,
|
||||
storage: {
|
||||
storage: {
|
||||
draft_draft1: {timestamp: new Date(), value: {message: 'Draft message', show: true, channelId: 'channel_id_1'}},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderWithRouter(
|
||||
state,
|
||||
['/team1/drafts'],
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
const navLink = screen.getByText('Drafts').closest('a');
|
||||
expect(navLink).toHaveClass('active');
|
||||
});
|
||||
|
||||
it('should be active when on scheduled posts route', () => {
|
||||
const state: DeepPartial<GlobalState> = {
|
||||
...baseState,
|
||||
storage: {
|
||||
storage: {
|
||||
draft_draft1: {timestamp: new Date(), value: {message: 'Draft message', show: true, channelId: 'channel_id_1'}},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
renderWithRouter(
|
||||
state,
|
||||
[`/team1/${SCHEDULED_POST_URL_SUFFIX}`],
|
||||
);
|
||||
|
||||
const navLink = screen.getByText('Drafts').closest('a');
|
||||
expect(navLink).toHaveClass('active');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import classNames from 'classnames';
|
||||
import React, {memo, useEffect, useMemo, useRef} from 'react';
|
||||
import React, {memo, useCallback, useEffect, useMemo, useRef} from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import {useSelector, useDispatch} from 'react-redux';
|
||||
import {NavLink, useRouteMatch} from 'react-router-dom';
|
||||
@@ -63,7 +63,9 @@ function DraftsLink() {
|
||||
const isDraftUrlMatch = useRouteMatch('/:team/drafts');
|
||||
const isScheduledPostUrlMatch = useRouteMatch('/:team/' + SCHEDULED_POST_URL_SUFFIX);
|
||||
|
||||
const urlMatches = isDraftUrlMatch || isScheduledPostUrlMatch;
|
||||
const urlMatches = Boolean(isDraftUrlMatch || isScheduledPostUrlMatch);
|
||||
|
||||
const isNavLinkActive = useCallback(() => urlMatches, [urlMatches]);
|
||||
|
||||
useEffect(() => {
|
||||
if (syncedDraftsAllowedAndEnabled) {
|
||||
@@ -123,6 +125,7 @@ function DraftsLink() {
|
||||
draggable='false'
|
||||
className='SidebarLink sidebar-item'
|
||||
tabIndex={0}
|
||||
isActive={isNavLinkActive}
|
||||
>
|
||||
<i
|
||||
data-testid='sendPostIcon'
|
||||
|
||||
Ссылка в новой задаче
Block a user