Remaining test for draft and Scheduled post (#8887)
* Added test for draft_scheduled_post and header component * Added test for drafts_button/index.ts * Added test for send_button/index.ts * Added test for servers/scheduled_post.ts queries * Added test for global_scheduled_post_list/index.ts * Added test for rescheduled draft index file and minor update * Added test for core option and index * Added test for scheduled post options * Added test for send_draft index file * updated test for draft_scheduled_post and draft_scheduled_post_header * Updated test for drafts_button index * Updated test for send_button index * Updated test for server/scheduled_post * Updated test for global_scheduled_post/index * removed the unnecessary config and team data to populate in db for test * Update app/components/draft_scheduled_post/draft_scheduled_post.test.tsx Co-authored-by: Daniel Espino García <larkox@gmail.com> * linter fixes --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Daniel Espino García <larkox@gmail.com>
This commit is contained in:
parent
d804f70d08
commit
6bdcf2be49
18 changed files with 1739 additions and 12 deletions
|
|
@ -0,0 +1,220 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {fireEvent, screen} from '@testing-library/react-native';
|
||||
import React from 'react';
|
||||
|
||||
import {switchToThread} from '@actions/local/thread';
|
||||
import {switchToChannelById} from '@actions/remote/channel';
|
||||
import {DRAFT_TYPE_DRAFT, DRAFT_TYPE_SCHEDULED} from '@constants/draft';
|
||||
import {openAsBottomSheet} from '@screens/navigation';
|
||||
import {renderWithIntlAndTheme} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import DraftAndScheduledPost from './draft_scheduled_post';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
jest.mock('@actions/local/thread', () => ({
|
||||
switchToThread: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@actions/remote/channel', () => ({
|
||||
switchToChannelById: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@screens/navigation', () => ({
|
||||
openAsBottomSheet: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@context/server', () => ({
|
||||
useServerUrl: jest.fn().mockReturnValue('https://server.com'),
|
||||
}));
|
||||
|
||||
jest.mock('@components/draft_scheduled_post_header', () => () => null);
|
||||
|
||||
jest.mock('./draft_scheduled_post_container', () => () => null);
|
||||
|
||||
describe('DraftAndScheduledPost', () => {
|
||||
const baseProps: Parameters<typeof DraftAndScheduledPost>[0] = {
|
||||
channel: TestHelper.fakeChannelModel({
|
||||
id: 'channel-id',
|
||||
teamId: 'team-id',
|
||||
}),
|
||||
location: 'location' as AvailableScreens,
|
||||
post: TestHelper.fakeDraftModel({
|
||||
rootId: '',
|
||||
updateAt: 1234567890,
|
||||
metadata: {},
|
||||
files: [] as FileInfo[],
|
||||
}),
|
||||
layoutWidth: 300,
|
||||
isPostPriorityEnabled: false,
|
||||
draftType: DRAFT_TYPE_DRAFT,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders correctly for draft', () => {
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPost {...baseProps}/>);
|
||||
expect(screen.getByTestId('draft_post')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders error line for scheduled post with error', () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
draftType: DRAFT_TYPE_SCHEDULED,
|
||||
post: TestHelper.fakeScheduledPostModel({
|
||||
rootId: '',
|
||||
updateAt: 1234567890,
|
||||
metadata: {},
|
||||
files: [] as FileInfo[],
|
||||
scheduledAt: 1234567890,
|
||||
errorCode: 'some_error',
|
||||
}),
|
||||
};
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPost {...props}/>);
|
||||
|
||||
expect(screen.getByTestId('draft_post.error_line')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not render error line for scheduled post without error', () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
draftType: DRAFT_TYPE_SCHEDULED,
|
||||
post: TestHelper.fakeScheduledPostModel({
|
||||
rootId: '',
|
||||
updateAt: 1234567890,
|
||||
metadata: {},
|
||||
files: [] as FileInfo[],
|
||||
scheduledAt: 1234567890,
|
||||
errorCode: '',
|
||||
}),
|
||||
};
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPost {...props}/>);
|
||||
|
||||
expect(screen.queryByTestId('draft_post.error_line')).toBeNull();
|
||||
});
|
||||
|
||||
it('does not render error line for draft posts', () => {
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPost {...baseProps}/>);
|
||||
|
||||
expect(screen.queryByTestId('draft_post.error_line')).toBeNull();
|
||||
});
|
||||
|
||||
it('renders post priority when enabled and present', () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
isPostPriorityEnabled: true,
|
||||
post: TestHelper.fakeDraftModel({
|
||||
rootId: '',
|
||||
updateAt: 1234567890,
|
||||
files: [] as FileInfo[],
|
||||
metadata: {
|
||||
priority: {
|
||||
priority: 'important',
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPost {...props}/>);
|
||||
|
||||
expect(screen.getByTestId('draft_post.priority')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not render post priority when disabled', () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
isPostPriorityEnabled: false,
|
||||
post: TestHelper.fakeDraftModel({
|
||||
rootId: '',
|
||||
updateAt: 1234567890,
|
||||
files: [] as FileInfo[],
|
||||
metadata: {
|
||||
priority: {
|
||||
priority: 'important',
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPost {...props}/>);
|
||||
|
||||
expect(screen.queryByTestId('draft_post.priority')).toBeNull();
|
||||
});
|
||||
|
||||
it('does not render post priority when not present in metadata', () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
isPostPriorityEnabled: true,
|
||||
post: TestHelper.fakeDraftModel({
|
||||
rootId: '',
|
||||
updateAt: 1234567890,
|
||||
files: [] as FileInfo[],
|
||||
metadata: {},
|
||||
}),
|
||||
};
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPost {...props}/>);
|
||||
|
||||
expect(screen.queryByTestId('draft_post.priority')).toBeNull();
|
||||
});
|
||||
|
||||
it('navigates to thread when post has rootId', () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
post: TestHelper.fakeDraftModel({
|
||||
rootId: 'root-id',
|
||||
updateAt: 1234567890,
|
||||
metadata: {},
|
||||
files: [] as FileInfo[],
|
||||
}),
|
||||
};
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPost {...props}/>);
|
||||
|
||||
fireEvent.press(screen.getByTestId('draft_post'));
|
||||
|
||||
expect(switchToThread).toHaveBeenCalledWith('https://server.com', 'root-id', false);
|
||||
expect(switchToChannelById).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('navigates to channel when post has no rootId', () => {
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPost {...baseProps}/>);
|
||||
|
||||
fireEvent.press(screen.getByTestId('draft_post'));
|
||||
|
||||
expect(switchToChannelById).toHaveBeenCalledWith('https://server.com', 'channel-id', 'team-id', false);
|
||||
expect(switchToThread).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('opens options on long press for draft', () => {
|
||||
const mockOpenAsBottonSheet = jest.mocked(openAsBottomSheet);
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPost {...baseProps}/>);
|
||||
|
||||
fireEvent(screen.getByTestId('draft_post'), 'longPress');
|
||||
|
||||
expect(mockOpenAsBottonSheet).toHaveBeenCalled();
|
||||
expect(mockOpenAsBottonSheet.mock.calls[0][0].props?.draftType).toBe(DRAFT_TYPE_DRAFT);
|
||||
});
|
||||
|
||||
it('opens options on long press for scheduled post', () => {
|
||||
const mockOpenAsBottonSheet = jest.mocked(openAsBottomSheet);
|
||||
const props = {
|
||||
...baseProps,
|
||||
draftType: DRAFT_TYPE_SCHEDULED,
|
||||
post: TestHelper.fakeScheduledPostModel({
|
||||
rootId: '',
|
||||
updateAt: 1234567890,
|
||||
metadata: {},
|
||||
files: [] as FileInfo[],
|
||||
scheduledAt: 1234567890,
|
||||
}),
|
||||
};
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPost {...props}/>);
|
||||
|
||||
fireEvent(screen.getByTestId('draft_post'), 'longPress');
|
||||
|
||||
expect(mockOpenAsBottonSheet).toHaveBeenCalled();
|
||||
expect(mockOpenAsBottonSheet.mock.calls[0][0].props?.draftType).toBe(DRAFT_TYPE_SCHEDULED);
|
||||
});
|
||||
});
|
||||
|
|
@ -118,7 +118,10 @@ const DraftAndScheduledPost: React.FC<Props> = ({
|
|||
>
|
||||
<View style={style.container}>
|
||||
{draftType === DRAFT_TYPE_SCHEDULED && (post as ScheduledPostModel).errorCode !== '' &&
|
||||
<View style={style.errorLine}/>
|
||||
<View
|
||||
style={style.errorLine}
|
||||
testID='draft_post.error_line'
|
||||
/>
|
||||
}
|
||||
<View
|
||||
style={[style.postContainer, firstItem ? null : style.postContainerBorderTop]}
|
||||
|
|
@ -134,7 +137,10 @@ const DraftAndScheduledPost: React.FC<Props> = ({
|
|||
scheduledPostErrorCode={'errorCode' in post ? post.errorCode : undefined}
|
||||
/>
|
||||
{showPostPriority && post.metadata?.priority &&
|
||||
<View style={style.postPriority}>
|
||||
<View
|
||||
style={style.postPriority}
|
||||
testID='draft_post.priority'
|
||||
>
|
||||
<Header
|
||||
noMentionsError={false}
|
||||
postPriority={post.metadata?.priority}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,179 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {General, Screens} from '@constants';
|
||||
import {SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import permissions from '@constants/permissions';
|
||||
import {PostPriorityType} from '@constants/post';
|
||||
import DatabaseManager from '@database/manager/__mocks__';
|
||||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import SendDraft from './send_draft';
|
||||
|
||||
import EnhancedSendDraft from './index';
|
||||
|
||||
import type ServerDataOperator from '@database/operator/server_data_operator';
|
||||
import type Database from '@nozbe/watermelondb/Database';
|
||||
|
||||
jest.mock('./send_draft', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mocked(SendDraft).mockImplementation((props) => React.createElement('SendDraft', {...props, testID: 'send-draft'}));
|
||||
|
||||
describe('EnhancedSendDraft', () => {
|
||||
const serverUrl = 'server-1';
|
||||
const teamId = 'team1';
|
||||
let database: Database;
|
||||
let operator: ServerDataOperator;
|
||||
|
||||
const defaultProps = {
|
||||
channelId: 'channel1',
|
||||
rootId: 'root1',
|
||||
channelType: General.DM_CHANNEL,
|
||||
currentUserId: 'user1',
|
||||
channelName: 'channel1',
|
||||
maxMessageLength: 1000,
|
||||
customEmojis: [],
|
||||
value: '',
|
||||
files: [],
|
||||
useChannelMentions: false,
|
||||
userIsOutOfOffice: false,
|
||||
persistentNotificationInterval: 0,
|
||||
persistentNotificationMaxRecipients: 0,
|
||||
postPriority: {
|
||||
priority: PostPriorityType.STANDARD,
|
||||
requested_ack: false,
|
||||
persistent_notifications: false,
|
||||
},
|
||||
bottomSheetId: Screens.SCHEDULED_POST_OPTIONS,
|
||||
channelDisplayName: 'channel1',
|
||||
draftReceiverUserName: '',
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await DatabaseManager.init([serverUrl]);
|
||||
const serverDatabaseAndOperator = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
database = serverDatabaseAndOperator.database;
|
||||
operator = serverDatabaseAndOperator.operator;
|
||||
await operator.handleUsers({users: [TestHelper.fakeUser({
|
||||
id: 'user1',
|
||||
})],
|
||||
prepareRecordsOnly: false});
|
||||
await operator.handleTeam({teams: [TestHelper.fakeTeam({id: teamId})], prepareRecordsOnly: false});
|
||||
await operator.handleSystem(
|
||||
{
|
||||
systems: [
|
||||
{
|
||||
id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID,
|
||||
value: teamId,
|
||||
},
|
||||
{
|
||||
id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID,
|
||||
value: 'user1',
|
||||
},
|
||||
{
|
||||
id: SYSTEM_IDENTIFIERS.CURRENT_CHANNEL_ID,
|
||||
value: 'channel1',
|
||||
},
|
||||
],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
await operator.handleConfigs({
|
||||
configs: [
|
||||
{id: 'Version', value: '7.6.0'},
|
||||
],
|
||||
configsToDelete: [],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
await operator.handleMyTeam({
|
||||
myTeams: [
|
||||
{
|
||||
id: teamId,
|
||||
roles: 'role-name',
|
||||
},
|
||||
],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
|
||||
await operator.handleRole({roles: [TestHelper.fakeRole({
|
||||
id: 'role-id',
|
||||
name: 'role-name',
|
||||
permissions: [permissions.CREATE_POST],
|
||||
})],
|
||||
prepareRecordsOnly: false});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await DatabaseManager.destroyServerDatabase(serverUrl);
|
||||
});
|
||||
|
||||
it('should return the correct values', async () => {
|
||||
await operator.handleChannel({channels: [TestHelper.fakeChannel({id: 'channel1', team_id: teamId})], prepareRecordsOnly: false});
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedSendDraft {...defaultProps}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(getByTestId('send-draft')).toBeTruthy();
|
||||
expect(getByTestId('send-draft').props.canPost).toBe(true);
|
||||
expect(getByTestId('send-draft').props.channelIsArchived).toBe(false);
|
||||
expect(getByTestId('send-draft').props.channelIsReadOnly).toBe(false);
|
||||
expect(getByTestId('send-draft').props.deactivatedChannel).toBe(false);
|
||||
});
|
||||
|
||||
it('should return the correct values when the channel is archived', async () => {
|
||||
await operator.handleChannel({channels: [TestHelper.fakeChannel({id: 'channel1', delete_at: 1})], prepareRecordsOnly: false});
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedSendDraft {...defaultProps}/>,
|
||||
{database},
|
||||
|
||||
);
|
||||
|
||||
expect(getByTestId('send-draft')).toBeTruthy();
|
||||
expect(getByTestId('send-draft').props.channelIsArchived).toBe(true);
|
||||
});
|
||||
|
||||
it('should return the correct values when the channel is read only', async () => {
|
||||
await operator.handleChannel({channels: [TestHelper.fakeChannel({id: 'channel1', name: General.DEFAULT_CHANNEL, delete_at: 0})], prepareRecordsOnly: false});
|
||||
await operator.handleConfigs({
|
||||
configs: [
|
||||
{id: 'ExperimentalTownSquareIsReadOnly', value: 'true'},
|
||||
],
|
||||
configsToDelete: [],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedSendDraft {...defaultProps}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(getByTestId('send-draft')).toBeTruthy();
|
||||
expect(getByTestId('send-draft').props.channelIsReadOnly).toBe(true);
|
||||
});
|
||||
|
||||
it('should return the correct values when the channel is deactivated', async () => {
|
||||
await operator.handleChannel({channels: [TestHelper.fakeChannel({id: 'channel1', name: 'user2__channel1', type: General.DM_CHANNEL, delete_at: 0})], prepareRecordsOnly: false});
|
||||
await operator.handleUsers({
|
||||
users: [
|
||||
TestHelper.fakeUser({
|
||||
id: 'user2',
|
||||
delete_at: 1,
|
||||
}),
|
||||
],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedSendDraft {...defaultProps}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(getByTestId('send-draft')).toBeTruthy();
|
||||
expect(getByTestId('send-draft').props.deactivatedChannel).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {screen} from '@testing-library/react-native';
|
||||
import React from 'react';
|
||||
|
||||
import {General} from '@constants';
|
||||
import {DRAFT_TYPE_DRAFT, DRAFT_TYPE_SCHEDULED} from '@constants/draft';
|
||||
import {renderWithIntlAndTheme} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import DraftAndScheduledPostHeader from './draft_scheduled_post_header';
|
||||
|
||||
// Mock the datetime util to provide both getReadableTimestamp and toMilliseconds
|
||||
jest.mock('@utils/datetime', () => ({
|
||||
getReadableTimestamp: jest.fn(() => 'May 5, 2025, 10:00 AM'),
|
||||
toMilliseconds: jest.fn(({minutes}) => minutes * 60 * 1000),
|
||||
}));
|
||||
|
||||
jest.mock('@utils/user', () => ({
|
||||
getUserTimezone: jest.fn(() => ({
|
||||
useAutomaticTimezone: true,
|
||||
automaticTimezone: 'America/New_York',
|
||||
manualTimezone: '',
|
||||
})),
|
||||
}));
|
||||
|
||||
jest.mock('@utils/scheduled_post', () => ({
|
||||
getErrorStringFromCode: jest.fn(() => 'Error message'),
|
||||
}));
|
||||
|
||||
jest.mock('@components/draft_scheduled_post_header/profile_avatar', () => {
|
||||
const MockProfileAvatar = () => null;
|
||||
return MockProfileAvatar;
|
||||
});
|
||||
|
||||
describe('DraftAndScheduledPostHeader', () => {
|
||||
const baseProps: Parameters<typeof DraftAndScheduledPostHeader>[0] = {
|
||||
channel: TestHelper.fakeChannelModel({
|
||||
id: 'channel-id',
|
||||
type: General.OPEN_CHANNEL,
|
||||
displayName: 'Test Channel',
|
||||
}),
|
||||
updateAt: 1620000000000,
|
||||
draftType: DRAFT_TYPE_DRAFT,
|
||||
testID: 'draft_header',
|
||||
isMilitaryTime: false,
|
||||
currentUser: TestHelper.fakeUserModel({
|
||||
id: 'user-id',
|
||||
username: 'testuser',
|
||||
locale: 'en',
|
||||
}),
|
||||
};
|
||||
|
||||
it('renders draft header correctly', () => {
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPostHeader {...baseProps}/>);
|
||||
|
||||
expect(screen.getByText('In:')).toBeTruthy();
|
||||
expect(screen.getByText('Test Channel')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders DM draft header correctly', () => {
|
||||
const dmProps: Parameters<typeof DraftAndScheduledPostHeader>[0] = {
|
||||
...baseProps,
|
||||
channel: TestHelper.fakeChannelModel({
|
||||
id: 'channel-id',
|
||||
type: General.DM_CHANNEL,
|
||||
displayName: 'Test Channel',
|
||||
}),
|
||||
postReceiverUser: TestHelper.fakeUserModel({
|
||||
id: 'receiver-id',
|
||||
username: 'receiver',
|
||||
}),
|
||||
};
|
||||
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPostHeader {...dmProps}/>);
|
||||
|
||||
expect(screen.getByText('To:')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders thread draft header correctly', () => {
|
||||
const threadProps = {
|
||||
...baseProps,
|
||||
rootId: 'root-post-id',
|
||||
};
|
||||
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPostHeader {...threadProps}/>);
|
||||
|
||||
expect(screen.getByText('Thread in:')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders scheduled post header correctly', () => {
|
||||
const scheduledProps = {
|
||||
...baseProps,
|
||||
draftType: DRAFT_TYPE_SCHEDULED,
|
||||
postScheduledAt: 1620100000000,
|
||||
};
|
||||
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPostHeader {...scheduledProps}/>);
|
||||
|
||||
expect(screen.getByTestId('scheduled_post_header.scheduled_at')).toBeTruthy();
|
||||
expect(screen.getByText('Send on May 5, 2025, 10:00 AM')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders scheduled post with error correctly', () => {
|
||||
const errorProps = {
|
||||
...baseProps,
|
||||
draftType: DRAFT_TYPE_SCHEDULED,
|
||||
postScheduledAt: 1620100000000,
|
||||
scheduledPostErrorCode: 'channel_not_found',
|
||||
};
|
||||
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPostHeader {...errorProps}/>);
|
||||
|
||||
expect(screen.getByText('Error message')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders sent scheduled post correctly', () => {
|
||||
const sentProps = {
|
||||
...baseProps,
|
||||
draftType: DRAFT_TYPE_SCHEDULED,
|
||||
postScheduledAt: 1620100000000,
|
||||
scheduledPostErrorCode: 'post_send_success_delete_failed',
|
||||
};
|
||||
|
||||
renderWithIntlAndTheme(<DraftAndScheduledPostHeader {...sentProps}/>);
|
||||
|
||||
expect(screen.getByText('Sent')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
99
app/components/drafts_buttton/index.test.tsx
Normal file
99
app/components/drafts_buttton/index.test.tsx
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {Screens} from '@constants';
|
||||
import {SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import DraftsButton from './drafts_button';
|
||||
|
||||
import EnhancedDraftButton from './index';
|
||||
|
||||
import type ServerDataOperator from '@database/operator/server_data_operator';
|
||||
import type {Database} from '@nozbe/watermelondb';
|
||||
|
||||
jest.mock('./drafts_button', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mocked(DraftsButton).mockImplementation((props) => React.createElement('DraftsButton', {...props, testID: 'drafts-button'}));
|
||||
|
||||
describe('DraftsButton', () => {
|
||||
|
||||
const serverUrl = 'server-1';
|
||||
const teamId = 'team1';
|
||||
let database: Database;
|
||||
let operator: ServerDataOperator;
|
||||
|
||||
beforeEach(async () => {
|
||||
await DatabaseManager.init([serverUrl]);
|
||||
const serverDatabaseAndOperator = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
database = serverDatabaseAndOperator.database;
|
||||
operator = serverDatabaseAndOperator.operator;
|
||||
|
||||
await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false});
|
||||
await operator.handleTeam({teams: [TestHelper.fakeTeam({id: teamId})], prepareRecordsOnly: false});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await DatabaseManager.destroyServerDatabase(serverUrl);
|
||||
});
|
||||
|
||||
it('should correctly show isActiveTab if the last channel id Global_DRAFTS', async () => {
|
||||
await operator.handleTeamChannelHistory({
|
||||
teamChannelHistories: [
|
||||
{channel_ids: [Screens.GLOBAL_DRAFTS], id: teamId},
|
||||
],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedDraftButton
|
||||
shouldHighlightActive={false}
|
||||
draftsCount={0}
|
||||
scheduledPostCount={0}
|
||||
scheduledPostHasError={false}
|
||||
/>, {database});
|
||||
|
||||
const draftsButton = getByTestId('drafts-button');
|
||||
expect(draftsButton.props.isActiveTab).toBe(true);
|
||||
});
|
||||
|
||||
it('should correctly show isActiveTab if the last channel id is not Global_DRAFTS', async () => {
|
||||
await operator.handleTeamChannelHistory({
|
||||
teamChannelHistories: [
|
||||
{channel_ids: ['channel1'], id: teamId},
|
||||
],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedDraftButton
|
||||
shouldHighlightActive={false}
|
||||
draftsCount={0}
|
||||
scheduledPostCount={0}
|
||||
scheduledPostHasError={false}
|
||||
/>, {database});
|
||||
|
||||
const draftsButton = getByTestId('drafts-button');
|
||||
expect(draftsButton.props.isActiveTab).toBe(false);
|
||||
});
|
||||
|
||||
it('if shouldHightlightActive is true and no last channel id is present, then isActiveTab should be true', async () => {
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedDraftButton
|
||||
shouldHighlightActive={true}
|
||||
draftsCount={0}
|
||||
scheduledPostCount={0}
|
||||
scheduledPostHasError={false}
|
||||
/>, {database});
|
||||
|
||||
const draftsButton = getByTestId('drafts-button');
|
||||
expect(draftsButton.props.isActiveTab).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -13,7 +13,11 @@ import DraftsButton from './drafts_button';
|
|||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
||||
const enhanced = withObservables(['shouldHighlightActive'], ({database, shouldHighlightActive}: {shouldHighlightActive: boolean} & WithDatabaseArgs) => {
|
||||
type Props = WithDatabaseArgs & {
|
||||
shouldHighlightActive: boolean;
|
||||
}
|
||||
|
||||
const enhanced = withObservables(['shouldHighlightActive'], ({database, shouldHighlightActive}: Props) => {
|
||||
const currentTeamId = observeCurrentTeamId(database);
|
||||
const isActiveTab = currentTeamId.pipe(
|
||||
switchMap(
|
||||
|
|
|
|||
68
app/components/post_draft/send_button/index.test.tsx
Normal file
68
app/components/post_draft/send_button/index.test.tsx
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {storeGlobal} from '@actions/app/global';
|
||||
import {Tutorial} from '@constants';
|
||||
import {SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {renderWithEverything, waitFor} from '@test/intl-test-helper';
|
||||
|
||||
import SendButton from './send_button';
|
||||
|
||||
import EnhancedSendButton from './index';
|
||||
|
||||
import type ServerDataOperator from '@database/operator/server_data_operator';
|
||||
import type {Database} from '@nozbe/watermelondb';
|
||||
|
||||
jest.mock('./send_button', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mocked(SendButton).mockImplementation((props) => React.createElement('SendButton', {...props, testID: 'send-button'}));
|
||||
|
||||
describe('SendButton', () => {
|
||||
const serverUrl = 'server-1';
|
||||
const teamId = 'team1';
|
||||
let database: Database;
|
||||
let operator: ServerDataOperator;
|
||||
|
||||
beforeEach(async () => {
|
||||
await DatabaseManager.init([serverUrl]);
|
||||
const serverDatabaseAndOperator = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
database = serverDatabaseAndOperator.database;
|
||||
operator = serverDatabaseAndOperator.operator;
|
||||
|
||||
await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await DatabaseManager.destroyServerDatabase(serverUrl);
|
||||
await storeGlobal(Tutorial.SCHEDULED_POST, null, false);
|
||||
});
|
||||
|
||||
const defaultProps: Parameters<typeof EnhancedSendButton>[0] = {
|
||||
testID: 'send-button',
|
||||
disabled: false,
|
||||
sendMessage: jest.fn(),
|
||||
showScheduledPostOptions: jest.fn(),
|
||||
scheduledPostEnabled: true,
|
||||
};
|
||||
|
||||
it('should return false if the scheduled post tutorial is not watched', async () => {
|
||||
const {getByTestId} = renderWithEverything(<EnhancedSendButton {...defaultProps}/>, {database});
|
||||
const sendButton = getByTestId('send-button');
|
||||
expect(sendButton.props.scheduledPostFeatureTooltipWatched).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true if the scheduled post tutorial is watched', async () => {
|
||||
await storeGlobal(Tutorial.SCHEDULED_POST, 'true', false);
|
||||
await waitFor(() => {
|
||||
const {getByTestId} = renderWithEverything(<EnhancedSendButton {...defaultProps}/>, {database});
|
||||
const sendButton = getByTestId('send-button');
|
||||
expect(sendButton.props.scheduledPostFeatureTooltipWatched).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import {withDatabase, withObservables} from '@nozbe/watermelondb/react';
|
||||
|
||||
import {SendButton} from '@components/post_draft/send_button/send_button';
|
||||
import SendButton from '@components/post_draft/send_button/send_button';
|
||||
import {Tutorial} from '@constants';
|
||||
import {observeTutorialWatched} from '@queries/app/global';
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {act} from '@testing-library/react-hooks';
|
|||
import React from 'react';
|
||||
import {InteractionManager} from 'react-native';
|
||||
|
||||
import {SendButton} from '@components/post_draft/send_button/send_button';
|
||||
import SendButton from '@components/post_draft/send_button/send_button';
|
||||
import {fireEvent, renderWithIntl} from '@test/intl-test-helper';
|
||||
|
||||
describe('components/post_draft/send_button', () => {
|
||||
|
|
|
|||
|
|
@ -51,14 +51,14 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
|||
};
|
||||
});
|
||||
|
||||
export function SendButton({
|
||||
const SendButton: React.FC<Props> = ({
|
||||
testID,
|
||||
disabled,
|
||||
sendMessage,
|
||||
showScheduledPostOptions,
|
||||
scheduledPostFeatureTooltipWatched,
|
||||
scheduledPostEnabled,
|
||||
}: Props) {
|
||||
}: Props) => {
|
||||
const theme = useTheme();
|
||||
const sendButtonTestID = `${testID}.send.button` + (disabled ? '.disabled' : '');
|
||||
const style = getStyleSheet(theme);
|
||||
|
|
@ -116,4 +116,6 @@ export function SendButton({
|
|||
</Tooltip>
|
||||
</TouchableWithFeedback>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default SendButton;
|
||||
|
|
|
|||
287
app/queries/servers/scheduled_post.test.ts
Normal file
287
app/queries/servers/scheduled_post.test.ts
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Database} from '@nozbe/watermelondb';
|
||||
|
||||
import {ActionType} from '@constants';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import ServerDataOperator from '@database/operator/server_data_operator';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import {
|
||||
observeFirstScheduledPost,
|
||||
observeScheduledPostCount,
|
||||
observeScheduledPostCountForChannel,
|
||||
observeScheduledPostCountForThread,
|
||||
observeScheduledPostsForTeam,
|
||||
queryScheduledPost,
|
||||
queryScheduledPostsForTeam,
|
||||
} from './scheduled_post';
|
||||
|
||||
describe('Scheduled Post Queries', () => {
|
||||
const serverUrl = 'scheduledpost.test.com';
|
||||
let database: Database;
|
||||
let operator: ServerDataOperator;
|
||||
|
||||
beforeEach(async () => {
|
||||
await DatabaseManager.init([serverUrl]);
|
||||
const serverDatabaseAndOperator = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
database = serverDatabaseAndOperator.database;
|
||||
operator = serverDatabaseAndOperator.operator;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await DatabaseManager.destroyServerDatabase(serverUrl);
|
||||
});
|
||||
|
||||
describe('queryScheduledPostsForTeam', () => {
|
||||
it('should return posts for specific team', async () => {
|
||||
const channel = TestHelper.fakeChannel({id: 'ch1', team_id: 'team1', type: 'O'});
|
||||
const post = TestHelper.fakeScheduledPost({id: 'post1', channel_id: 'ch1', message: 'Test'});
|
||||
|
||||
const models = (await Promise.all([
|
||||
operator.handleChannel({channels: [channel], prepareRecordsOnly: true}),
|
||||
operator.handleScheduledPosts({
|
||||
actionType: ActionType.SCHEDULED_POSTS.CREATE_OR_UPDATED_SCHEDULED_POST,
|
||||
scheduledPosts: [post],
|
||||
prepareRecordsOnly: true,
|
||||
}),
|
||||
])).flat();
|
||||
await operator.batchRecords(models, 'test');
|
||||
|
||||
const result = await queryScheduledPostsForTeam(database, 'team1').fetch();
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe('post1');
|
||||
});
|
||||
|
||||
it('should return empty for non-existent team', async () => {
|
||||
const result = await queryScheduledPostsForTeam(database, 'nonexistent').fetch();
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should include direct channels when specified', async () => {
|
||||
const dmChannel = TestHelper.fakeChannel({id: 'dm1', team_id: '', type: 'D'});
|
||||
const post = TestHelper.fakeScheduledPost({id: 'post1', channel_id: 'dm1', message: 'DM'});
|
||||
|
||||
const models = (await Promise.all([
|
||||
operator.handleChannel({channels: [dmChannel], prepareRecordsOnly: true}),
|
||||
operator.handleScheduledPosts({
|
||||
actionType: ActionType.SCHEDULED_POSTS.CREATE_OR_UPDATED_SCHEDULED_POST,
|
||||
scheduledPosts: [post],
|
||||
prepareRecordsOnly: true,
|
||||
}),
|
||||
])).flat();
|
||||
await operator.batchRecords(models, 'test');
|
||||
|
||||
const result = await queryScheduledPostsForTeam(database, 'team1', true).fetch();
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe('post1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryScheduledPost', () => {
|
||||
it('should return posts for specific channel and root', async () => {
|
||||
const channel = TestHelper.fakeChannel({id: 'ch1', team_id: 'team1'});
|
||||
const posts = [
|
||||
TestHelper.fakeScheduledPost({id: 'post1', channel_id: 'ch1', root_id: 'root1', message: 'Reply1'}),
|
||||
TestHelper.fakeScheduledPost({id: 'post2', channel_id: 'ch1', root_id: 'root1', message: 'Reply2'}),
|
||||
TestHelper.fakeScheduledPost({id: 'post3', channel_id: 'ch1', root_id: 'root2', message: 'Other'}),
|
||||
];
|
||||
|
||||
const models = (await Promise.all([
|
||||
operator.handleChannel({channels: [channel], prepareRecordsOnly: true}),
|
||||
operator.handleScheduledPosts({
|
||||
actionType: ActionType.SCHEDULED_POSTS.CREATE_OR_UPDATED_SCHEDULED_POST,
|
||||
scheduledPosts: posts,
|
||||
prepareRecordsOnly: true,
|
||||
}),
|
||||
])).flat();
|
||||
await operator.batchRecords(models, 'test');
|
||||
|
||||
const result = await queryScheduledPost(database, 'ch1', 'root1').fetch();
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result.map((p) => p.id).sort()).toEqual(['post1', 'post2']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('observeScheduledPostsForTeam', () => {
|
||||
it('should observe team posts', (done) => {
|
||||
const channel = TestHelper.fakeChannel({id: 'ch1', team_id: 'team1', type: 'O'});
|
||||
const post = TestHelper.fakeScheduledPost({id: 'post1', channel_id: 'ch1', message: 'Test'});
|
||||
|
||||
Promise.all([
|
||||
operator.handleChannel({channels: [channel], prepareRecordsOnly: false}),
|
||||
operator.handleScheduledPosts({
|
||||
actionType: ActionType.SCHEDULED_POSTS.CREATE_OR_UPDATED_SCHEDULED_POST,
|
||||
scheduledPosts: [post],
|
||||
prepareRecordsOnly: false,
|
||||
}),
|
||||
]).then(() => {
|
||||
observeScheduledPostsForTeam(database, 'team1').subscribe((posts) => {
|
||||
if (posts.length === 1 && posts[0].id === 'post1') {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
}, 1500);
|
||||
});
|
||||
|
||||
describe('observeScheduledPostCount', () => {
|
||||
it('should count team posts correctly', (done) => {
|
||||
const channels = [
|
||||
TestHelper.fakeChannel({id: 'ch1', team_id: 'team1', type: 'O'}),
|
||||
TestHelper.fakeChannel({id: 'ch2', team_id: 'team1', type: 'P'}),
|
||||
];
|
||||
const posts = [
|
||||
TestHelper.fakeScheduledPost({id: 'post1', channel_id: 'ch1', message: 'Test1'}),
|
||||
TestHelper.fakeScheduledPost({id: 'post2', channel_id: 'ch2', message: 'Test2'}),
|
||||
];
|
||||
|
||||
Promise.all([
|
||||
operator.handleChannel({channels, prepareRecordsOnly: false}),
|
||||
operator.handleScheduledPosts({
|
||||
actionType: ActionType.SCHEDULED_POSTS.CREATE_OR_UPDATED_SCHEDULED_POST,
|
||||
scheduledPosts: posts,
|
||||
prepareRecordsOnly: false,
|
||||
}),
|
||||
]).then(() => {
|
||||
observeScheduledPostCount(database, 'team1', false).subscribe((count) => {
|
||||
if (count === 2) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
}, 1500);
|
||||
|
||||
it('should include direct channels when specified', (done) => {
|
||||
const channels = [
|
||||
TestHelper.fakeChannel({id: 'ch1', team_id: 'team1', type: 'O'}),
|
||||
TestHelper.fakeChannel({id: 'dm1', team_id: '', type: 'D'}),
|
||||
];
|
||||
const posts = [
|
||||
TestHelper.fakeScheduledPost({id: 'post1', channel_id: 'ch1', message: 'Team'}),
|
||||
TestHelper.fakeScheduledPost({id: 'post2', channel_id: 'dm1', message: 'DM'}),
|
||||
];
|
||||
|
||||
Promise.all([
|
||||
operator.handleChannel({channels, prepareRecordsOnly: false}),
|
||||
operator.handleScheduledPosts({
|
||||
actionType: ActionType.SCHEDULED_POSTS.CREATE_OR_UPDATED_SCHEDULED_POST,
|
||||
scheduledPosts: posts,
|
||||
prepareRecordsOnly: false,
|
||||
}),
|
||||
]).then(() => {
|
||||
observeScheduledPostCount(database, 'team1', true).subscribe((count) => {
|
||||
if (count === 2) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
}, 1500);
|
||||
});
|
||||
|
||||
describe('observeScheduledPostCountForChannel', () => {
|
||||
it('should count channel posts without errors', (done) => {
|
||||
const channel = TestHelper.fakeChannel({id: 'ch1', team_id: 'team1'});
|
||||
const posts = [
|
||||
TestHelper.fakeScheduledPost({id: 'post1', channel_id: 'ch1', error_code: '', message: 'Success1'}),
|
||||
TestHelper.fakeScheduledPost({id: 'post2', channel_id: 'ch1', error_code: '', message: 'Success2'}),
|
||||
TestHelper.fakeScheduledPost({id: 'post3', channel_id: 'ch1', error_code: 'failed', message: 'Error'}),
|
||||
];
|
||||
|
||||
Promise.all([
|
||||
operator.handleChannel({channels: [channel], prepareRecordsOnly: false}),
|
||||
operator.handleScheduledPosts({
|
||||
actionType: ActionType.SCHEDULED_POSTS.CREATE_OR_UPDATED_SCHEDULED_POST,
|
||||
scheduledPosts: posts,
|
||||
prepareRecordsOnly: false,
|
||||
}),
|
||||
]).then(() => {
|
||||
observeScheduledPostCountForChannel(database, 'ch1', false).subscribe((count) => {
|
||||
if (count === 2) { // Only posts without errors
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
}, 1500);
|
||||
|
||||
it('should handle CRT filtering', (done) => {
|
||||
const channel = TestHelper.fakeChannel({id: 'ch1', team_id: 'team1'});
|
||||
const posts = [
|
||||
TestHelper.fakeScheduledPost({id: 'post1', channel_id: 'ch1', root_id: '', error_code: '', message: 'Root1'}),
|
||||
TestHelper.fakeScheduledPost({id: 'post2', channel_id: 'ch1', root_id: '', error_code: '', message: 'Root2'}),
|
||||
TestHelper.fakeScheduledPost({id: 'post3', channel_id: 'ch1', root_id: 'thread1', error_code: '', message: 'Reply'}),
|
||||
];
|
||||
|
||||
Promise.all([
|
||||
operator.handleChannel({channels: [channel], prepareRecordsOnly: false}),
|
||||
operator.handleScheduledPosts({
|
||||
actionType: ActionType.SCHEDULED_POSTS.CREATE_OR_UPDATED_SCHEDULED_POST,
|
||||
scheduledPosts: posts,
|
||||
prepareRecordsOnly: false,
|
||||
}),
|
||||
]).then(() => {
|
||||
observeScheduledPostCountForChannel(database, 'ch1', true).subscribe((count) => {
|
||||
if (count === 2) { // Only root posts when CRT enabled
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
}, 1500);
|
||||
});
|
||||
|
||||
describe('observeScheduledPostCountForThread', () => {
|
||||
it('should count thread replies only', (done) => {
|
||||
const channel = TestHelper.fakeChannel({id: 'ch1', team_id: 'team1'});
|
||||
const posts = [
|
||||
TestHelper.fakeScheduledPost({id: 'post1', channel_id: 'ch1', root_id: 'thread1', error_code: '', message: 'Reply1'}),
|
||||
TestHelper.fakeScheduledPost({id: 'post2', channel_id: 'ch1', root_id: 'thread1', error_code: '', message: 'Reply2'}),
|
||||
TestHelper.fakeScheduledPost({id: 'post3', channel_id: 'ch1', root_id: 'thread2', error_code: '', message: 'Other'}),
|
||||
TestHelper.fakeScheduledPost({id: 'post4', channel_id: 'ch1', root_id: '', error_code: '', message: 'Root'}),
|
||||
];
|
||||
|
||||
Promise.all([
|
||||
operator.handleChannel({channels: [channel], prepareRecordsOnly: false}),
|
||||
operator.handleScheduledPosts({
|
||||
actionType: ActionType.SCHEDULED_POSTS.CREATE_OR_UPDATED_SCHEDULED_POST,
|
||||
scheduledPosts: posts,
|
||||
prepareRecordsOnly: false,
|
||||
}),
|
||||
]).then(() => {
|
||||
observeScheduledPostCountForThread(database, 'thread1').subscribe((count) => {
|
||||
if (count === 2) { // Only replies to thread1
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
}, 1500);
|
||||
});
|
||||
|
||||
describe('observeFirstScheduledPost', () => {
|
||||
it('should return undefined for empty array', (done) => {
|
||||
observeFirstScheduledPost([]).subscribe((result) => {
|
||||
if (result === undefined) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should return first post when available', (done) => {
|
||||
const mockPost = TestHelper.fakeScheduledPostModel({
|
||||
id: 'first_post',
|
||||
observe: jest.fn().mockReturnValue({
|
||||
subscribe: (callback: (result: any) => void) => {
|
||||
callback({id: 'first_post', message: 'First post'});
|
||||
return {unsubscribe: jest.fn()};
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
observeFirstScheduledPost([mockPost]).subscribe((result) => {
|
||||
if (result?.id === 'first_post') {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -13,7 +13,7 @@ import type ScheduledPostModel from '@typings/database/models/servers/scheduled_
|
|||
const {SERVER: {CHANNEL, SCHEDULED_POST}} = MM_TABLES;
|
||||
|
||||
export const queryScheduledPostsForTeam = (database: Database, teamId: string, includeDirectChannelPosts?: boolean) => {
|
||||
return database.collections.get<ScheduledPostModel>(SCHEDULED_POST).query(
|
||||
return database.get<ScheduledPostModel>(SCHEDULED_POST).query(
|
||||
Q.on(CHANNEL,
|
||||
Q.or(
|
||||
Q.where('team_id', teamId),
|
||||
|
|
@ -27,7 +27,7 @@ export const queryScheduledPostsForTeam = (database: Database, teamId: string, i
|
|||
};
|
||||
|
||||
export const queryScheduledPost = (database: Database, channelId: string, rootId = '') => {
|
||||
return database.collections.get<ScheduledPostModel>(SCHEDULED_POST).query(
|
||||
return database.get<ScheduledPostModel>(SCHEDULED_POST).query(
|
||||
Q.and(
|
||||
Q.where('channel_id', channelId),
|
||||
Q.where('root_id', rootId),
|
||||
|
|
@ -67,7 +67,7 @@ export const observeScheduledPostCountForChannel = (
|
|||
};
|
||||
|
||||
export const observeScheduledPostCountForThread = (database: Database, rootId: string) => {
|
||||
return database.collections.get<ScheduledPostModel>(SCHEDULED_POST).query(
|
||||
return database.get<ScheduledPostModel>(SCHEDULED_POST).query(
|
||||
Q.where('root_id', rootId),
|
||||
).observeCount();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,234 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {storeGlobal} from '@actions/app/global';
|
||||
import {ActionType, Tutorial, Screens} from '@constants';
|
||||
import {SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {act, renderWithEverything, waitFor} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import GlobalScheduledPostList from './global_scheduled_post_list';
|
||||
|
||||
import EnhancedGlobalScheduledPostList from './index';
|
||||
|
||||
import type ServerDataOperator from '@database/operator/server_data_operator';
|
||||
import type {Database} from '@nozbe/watermelondb';
|
||||
import type ScheduledPostModel from '@typings/database/models/servers/scheduled_post';
|
||||
|
||||
jest.mock('./global_scheduled_post_list', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mocked(GlobalScheduledPostList).mockImplementation((props) => React.createElement('GlobalScheduledPostList', {...props, testID: 'global-scheduled-post-list'}));
|
||||
|
||||
describe('GlobalScheduledPostList', () => {
|
||||
const serverUrl = 'server-1';
|
||||
const teamId = 'team1';
|
||||
let database: Database;
|
||||
let operator: ServerDataOperator;
|
||||
|
||||
beforeEach(async () => {
|
||||
await DatabaseManager.init([serverUrl]);
|
||||
const serverDatabaseAndOperator = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
database = serverDatabaseAndOperator.database;
|
||||
operator = serverDatabaseAndOperator.operator;
|
||||
|
||||
await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}], prepareRecordsOnly: false});
|
||||
await operator.handleTeam({teams: [TestHelper.fakeTeam({id: teamId})], prepareRecordsOnly: false});
|
||||
await operator.handleConfigs({
|
||||
configs: [
|
||||
{id: 'Version', value: '7.6.0'},
|
||||
],
|
||||
configsToDelete: [],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await DatabaseManager.destroyServerDatabase(serverUrl);
|
||||
});
|
||||
|
||||
it('should return empty array is there is no scheduled post', async () => {
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedGlobalScheduledPostList
|
||||
location={Screens.GLOBAL_DRAFTS}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
const globalScheduledPostList = getByTestId('global-scheduled-post-list');
|
||||
expect(globalScheduledPostList.props.allScheduledPosts).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it('should show scheduled post when one exists', async () => {
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedGlobalScheduledPostList
|
||||
location={Screens.GLOBAL_DRAFTS}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
const globalScheduledPostList = getByTestId('global-scheduled-post-list');
|
||||
expect(globalScheduledPostList.props.allScheduledPosts).toStrictEqual([]);
|
||||
|
||||
const channelId = 'channel1';
|
||||
await operator.handleChannel({
|
||||
channels: [TestHelper.fakeChannel({id: channelId, team_id: teamId})],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
|
||||
const scheduledPosts = [TestHelper.fakeScheduledPost({
|
||||
message: 'test message',
|
||||
files: [],
|
||||
channel_id: channelId,
|
||||
})];
|
||||
|
||||
await act(async () => {
|
||||
await operator.handleScheduledPosts({
|
||||
actionType: ActionType.SCHEDULED_POSTS.CREATE_OR_UPDATED_SCHEDULED_POST,
|
||||
scheduledPosts,
|
||||
includeDirectChannelPosts: false,
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const updatedGlobalScheduledPostList = getByTestId('global-scheduled-post-list');
|
||||
expect(updatedGlobalScheduledPostList.props.allScheduledPosts).toHaveLength(1);
|
||||
expect(updatedGlobalScheduledPostList.props.allScheduledPosts[0].id).toStrictEqual(scheduledPosts[0].id);
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly handle tutorial watched state', async () => {
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedGlobalScheduledPostList
|
||||
location={Screens.GLOBAL_DRAFTS}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
const globalScheduledPostList = getByTestId('global-scheduled-post-list');
|
||||
expect(globalScheduledPostList.props.tutorialWatched).toBe(false);
|
||||
|
||||
await storeGlobal(Tutorial.SCHEDULED_POSTS_LIST, 'true', false);
|
||||
|
||||
await waitFor(() => {
|
||||
const updatedGlobalScheduledPostList = getByTestId('global-scheduled-post-list');
|
||||
expect(updatedGlobalScheduledPostList.props.tutorialWatched).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should only show scheduled posts for current team', async () => {
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedGlobalScheduledPostList
|
||||
location={Screens.GLOBAL_DRAFTS}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
const team1ChannelId = 'team1_channel';
|
||||
const team2ChannelId = 'team2_channel';
|
||||
|
||||
await operator.handleChannel({
|
||||
channels: [
|
||||
TestHelper.fakeChannel({id: team1ChannelId, team_id: teamId, type: 'O'}),
|
||||
TestHelper.fakeChannel({id: team2ChannelId, team_id: 'team2', type: 'O'}),
|
||||
],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
|
||||
const scheduledPosts = [
|
||||
TestHelper.fakeScheduledPost({
|
||||
id: 'post1',
|
||||
message: 'Team1 message',
|
||||
channel_id: team1ChannelId,
|
||||
}),
|
||||
TestHelper.fakeScheduledPost({
|
||||
id: 'post2',
|
||||
message: 'Team2 message',
|
||||
channel_id: team2ChannelId,
|
||||
}),
|
||||
];
|
||||
|
||||
await act(async () => {
|
||||
await operator.handleScheduledPosts({
|
||||
actionType: ActionType.SCHEDULED_POSTS.CREATE_OR_UPDATED_SCHEDULED_POST,
|
||||
scheduledPosts,
|
||||
includeDirectChannelPosts: false,
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const globalScheduledPostList = getByTestId('global-scheduled-post-list');
|
||||
|
||||
// Should only show the post from current team (team1)
|
||||
expect(globalScheduledPostList.props.allScheduledPosts).toHaveLength(1);
|
||||
expect(globalScheduledPostList.props.allScheduledPosts[0].id).toBe('post1');
|
||||
});
|
||||
});
|
||||
|
||||
it('should include DMs and GMs in scheduled posts', async () => {
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedGlobalScheduledPostList
|
||||
location={Screens.GLOBAL_DRAFTS}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
const teamChannelId = 'team_channel';
|
||||
const dmChannelId = 'dm_channel';
|
||||
const gmChannelId = 'gm_channel';
|
||||
|
||||
await operator.handleChannel({
|
||||
channels: [
|
||||
TestHelper.fakeChannel({id: teamChannelId, team_id: teamId, type: 'O'}), // Team channel
|
||||
TestHelper.fakeChannel({id: dmChannelId, team_id: '', type: 'D'}), // Direct message
|
||||
TestHelper.fakeChannel({id: gmChannelId, team_id: '', type: 'G'}), // Group message
|
||||
],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
|
||||
const scheduledPosts = [
|
||||
TestHelper.fakeScheduledPost({
|
||||
id: 'team_post',
|
||||
message: 'Team message',
|
||||
channel_id: teamChannelId,
|
||||
}),
|
||||
TestHelper.fakeScheduledPost({
|
||||
id: 'dm_post',
|
||||
message: 'DM message',
|
||||
channel_id: dmChannelId,
|
||||
}),
|
||||
TestHelper.fakeScheduledPost({
|
||||
id: 'gm_post',
|
||||
message: 'GM message',
|
||||
channel_id: gmChannelId,
|
||||
}),
|
||||
];
|
||||
|
||||
await act(async () => {
|
||||
await operator.handleScheduledPosts({
|
||||
actionType: ActionType.SCHEDULED_POSTS.CREATE_OR_UPDATED_SCHEDULED_POST,
|
||||
scheduledPosts,
|
||||
includeDirectChannelPosts: true,
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const globalScheduledPostList = getByTestId('global-scheduled-post-list');
|
||||
|
||||
expect(globalScheduledPostList.props.allScheduledPosts).toHaveLength(3);
|
||||
|
||||
const postIds = globalScheduledPostList.props.allScheduledPosts.map((post: ScheduledPostModel) => post.id);
|
||||
expect(postIds).toContain('team_post');
|
||||
expect(postIds).toContain('dm_post');
|
||||
expect(postIds).toContain('gm_post');
|
||||
});
|
||||
});
|
||||
});
|
||||
81
app/screens/reschedule_draft/indext.test.tsx
Normal file
81
app/screens/reschedule_draft/indext.test.tsx
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {Screens} from '@constants';
|
||||
import {SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import DatabaseManager from '@database/manager/__mocks__';
|
||||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import RescheduledDraft from './reschedule_draft';
|
||||
|
||||
import EnhancedRescheduledDraft from './index';
|
||||
|
||||
import type ServerDataOperator from '@database/operator/server_data_operator';
|
||||
import type {Database} from '@nozbe/watermelondb';
|
||||
|
||||
jest.mock('./reschedule_draft', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mocked(RescheduledDraft).mockImplementation((props) => React.createElement('RescheduledDraft', {...props, testID: 'reschedule-draft'}));
|
||||
|
||||
describe('EnhancedRescheduledDraft', () => {
|
||||
|
||||
const serverUrl = 'server-1';
|
||||
const teamId = 'team1';
|
||||
let database: Database;
|
||||
let operator: ServerDataOperator;
|
||||
|
||||
beforeEach(async () => {
|
||||
await DatabaseManager.init([serverUrl]);
|
||||
const serverDatabaseAndOperator = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
database = serverDatabaseAndOperator.database;
|
||||
operator = serverDatabaseAndOperator.operator;
|
||||
|
||||
await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}, {id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: 'user1'}], prepareRecordsOnly: false});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await DatabaseManager.destroyServerDatabase(serverUrl);
|
||||
});
|
||||
|
||||
it('should correctly return the current user timezone', async () => {
|
||||
await operator.handleUsers({users: [TestHelper.fakeUser({id: 'user1', timezone: {useAutomaticTimezone: false, manualTimezone: 'America/New_York', automaticTimezone: 'America/New_York'}})], prepareRecordsOnly: false});
|
||||
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedRescheduledDraft
|
||||
componentId={Screens.RESCHEDULE_DRAFT}
|
||||
closeButtonId={'close-button'}
|
||||
draft={TestHelper.fakeScheduledPostModel({id: 'draft1'})}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(getByTestId('reschedule-draft')).toBeTruthy();
|
||||
expect(getByTestId('reschedule-draft').props.currentUserTimezone).toStrictEqual({
|
||||
useAutomaticTimezone: false,
|
||||
manualTimezone: 'America/New_York',
|
||||
automaticTimezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return undefined if the current user timezone is not set', async () => {
|
||||
await operator.handleUsers({users: [TestHelper.fakeUser({id: 'user1', timezone: undefined})], prepareRecordsOnly: false});
|
||||
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedRescheduledDraft
|
||||
componentId={Screens.RESCHEDULE_DRAFT}
|
||||
closeButtonId={'close-button'}
|
||||
draft={TestHelper.fakeScheduledPostModel({id: 'draft1'})}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(getByTestId('reschedule-draft')).toBeTruthy();
|
||||
expect(getByTestId('reschedule-draft').props.currentUserTimezone).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {fireEvent} from '@testing-library/react-native';
|
||||
import moment from 'moment-timezone';
|
||||
import React from 'react';
|
||||
|
||||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import {ScheduledPostCoreOptions} from './core_options';
|
||||
|
||||
import type Database from '@nozbe/watermelondb/Database';
|
||||
|
||||
jest.mock('@utils/time', () => ({
|
||||
getFormattedTime: jest.fn().mockReturnValue('9:00 AM'),
|
||||
}));
|
||||
|
||||
describe('ScheduledPostCoreOptions', () => {
|
||||
let database: Database;
|
||||
const baseProps = {
|
||||
userTimezone: 'America/New_York',
|
||||
isMilitaryTime: false,
|
||||
onSelectOption: jest.fn(),
|
||||
onCustomTimeSelected: jest.fn(),
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const server = await TestHelper.setupServerDatabase();
|
||||
database = server.database;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders correctly for Sunday', () => {
|
||||
// Sunday is weekday 7 in moment
|
||||
jest.spyOn(moment.prototype, 'weekday').mockReturnValue(7);
|
||||
|
||||
const {getByText, queryByText} = renderWithEverything(
|
||||
<ScheduledPostCoreOptions {...baseProps}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
// Should show Tomorrow option
|
||||
expect(getByText('Tomorrow at 9:00 AM')).toBeTruthy();
|
||||
|
||||
// Should not show Monday or Next Monday options
|
||||
expect(queryByText('Monday at 9:00 AM')).toBeNull();
|
||||
expect(queryByText('Next Monday at 9:00 AM')).toBeNull();
|
||||
|
||||
// Should always show Custom Time option
|
||||
expect(getByText('Custom Time')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders correctly for Monday', () => {
|
||||
// Monday is weekday 1 in moment
|
||||
jest.spyOn(moment.prototype, 'weekday').mockReturnValue(1);
|
||||
|
||||
const {getByText, queryByText} = renderWithEverything(
|
||||
<ScheduledPostCoreOptions {...baseProps}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
// Should show Tomorrow and Next Monday options
|
||||
expect(getByText('Tomorrow at 9:00 AM')).toBeTruthy();
|
||||
expect(getByText('Next Monday at 9:00 AM')).toBeTruthy();
|
||||
|
||||
// Should not show Monday option
|
||||
expect(queryByText('Monday at 9:00 AM')).toBeNull();
|
||||
|
||||
// Should always show Custom Time option
|
||||
expect(getByText('Custom Time')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders correctly for Friday', () => {
|
||||
// Friday is weekday 5 in moment
|
||||
jest.spyOn(moment.prototype, 'weekday').mockReturnValue(5);
|
||||
|
||||
const {getByText, queryByText} = renderWithEverything(
|
||||
<ScheduledPostCoreOptions {...baseProps}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
// Should show Monday option
|
||||
expect(getByText('Monday at 9:00 AM')).toBeTruthy();
|
||||
|
||||
// Should not show Tomorrow or Next Monday options
|
||||
expect(queryByText('Tomorrow at 9:00 AM')).toBeNull();
|
||||
expect(queryByText('Next Monday at 9:00 AM')).toBeNull();
|
||||
|
||||
// Should always show Custom Time option
|
||||
expect(getByText('Custom Time')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders correctly for Saturday', () => {
|
||||
// Saturday is weekday 6 in moment
|
||||
jest.spyOn(moment.prototype, 'weekday').mockReturnValue(6);
|
||||
|
||||
const {getByText, queryByText} = renderWithEverything(
|
||||
<ScheduledPostCoreOptions {...baseProps}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
// Should show Monday option
|
||||
expect(getByText('Monday at 9:00 AM')).toBeTruthy();
|
||||
|
||||
// Should not show Tomorrow or Next Monday options
|
||||
expect(queryByText('Tomorrow at 9:00 AM')).toBeNull();
|
||||
expect(queryByText('Next Monday at 9:00 AM')).toBeNull();
|
||||
|
||||
// Should always show Custom Time option
|
||||
expect(getByText('Custom Time')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('calls onSelectOption with correct timestamp when Tomorrow is selected', () => {
|
||||
const mockDate = moment.tz('2023-05-15 12:00', 'America/New_York');
|
||||
jest.spyOn(moment, 'tz').mockReturnValue(mockDate);
|
||||
jest.spyOn(moment.prototype, 'weekday').mockReturnValue(1);
|
||||
jest.spyOn(moment.prototype, 'clone').mockReturnValue(mockDate);
|
||||
jest.spyOn(moment.prototype, 'valueOf').mockReturnValue(1684242000000);
|
||||
|
||||
const {getByText} = renderWithEverything(
|
||||
<ScheduledPostCoreOptions {...baseProps}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
fireEvent.press(getByText('Tomorrow at 9:00 AM'));
|
||||
|
||||
expect(baseProps.onSelectOption).toHaveBeenCalledWith('1684242000000');
|
||||
expect(baseProps.onCustomTimeSelected).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('calls onSelectOption with correct timestamp when Monday is selected', () => {
|
||||
const mockDate = moment.tz('2023-05-11 12:00', 'America/New_York');
|
||||
jest.spyOn(moment, 'tz').mockReturnValue(mockDate);
|
||||
jest.spyOn(moment.prototype, 'weekday').mockReturnValue(4);
|
||||
|
||||
const {getByText} = renderWithEverything(
|
||||
<ScheduledPostCoreOptions {...baseProps}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
fireEvent.press(getByText('Monday at 9:00 AM'));
|
||||
|
||||
const expectedTime = mockDate.clone().isoWeekday(1).add(1, 'week').startOf('day').hour(9).minute(0);
|
||||
expect(baseProps.onSelectOption).toHaveBeenCalledWith(expectedTime.valueOf().toString());
|
||||
expect(baseProps.onCustomTimeSelected).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('calls onSelectOption with correct timestamp when Next Monday is selected', () => {
|
||||
const mockDate = moment.tz('2023-05-15 12:00', 'America/New_York');
|
||||
jest.spyOn(moment, 'tz').mockReturnValue(mockDate);
|
||||
jest.spyOn(moment.prototype, 'weekday').mockReturnValue(1);
|
||||
|
||||
const {getByText} = renderWithEverything(
|
||||
<ScheduledPostCoreOptions {...baseProps}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
fireEvent.press(getByText('Next Monday at 9:00 AM'));
|
||||
|
||||
const expectedTime = mockDate.clone().isoWeekday(1).add(1, 'week').startOf('day').hour(9).minute(0);
|
||||
expect(baseProps.onSelectOption).toHaveBeenCalledWith(expectedTime.valueOf().toString());
|
||||
expect(baseProps.onCustomTimeSelected).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('shows DateTimeSelector when Custom Time is selected', () => {
|
||||
const {getByText, queryByTestId} = renderWithEverything(
|
||||
<ScheduledPostCoreOptions {...baseProps}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
// Initially, DateTimeSelector should not be visible
|
||||
expect(queryByTestId('custom_date_time_picker')).toBeNull();
|
||||
|
||||
// Select Custom Time option
|
||||
fireEvent.press(getByText('Custom Time'));
|
||||
|
||||
// DateTimeSelector should now be visible
|
||||
expect(queryByTestId('custom_date_time_picker')).not.toBeNull();
|
||||
|
||||
// Should call onCustomTimeSelected with true
|
||||
expect(baseProps.onCustomTimeSelected).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it('calls handleCustomTimeChange when a custom time is selected', () => {
|
||||
// This test would need to mock the DateTimeSelector component's behavior
|
||||
// Since we can't directly test the internal callback without exposing it,
|
||||
// we'll verify that the component is rendered and the callback is set up
|
||||
|
||||
const {getByText} = renderWithEverything(
|
||||
<ScheduledPostCoreOptions {...baseProps}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
// Select Custom Time option
|
||||
fireEvent.press(getByText('Custom Time'));
|
||||
|
||||
// Verify onCustomTimeSelected was called
|
||||
expect(baseProps.onCustomTimeSelected).toHaveBeenCalledWith(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {Preferences} from '@constants';
|
||||
import {SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import DatabaseManager from '@database/manager/__mocks__';
|
||||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
import {getTimezone} from '@utils/user';
|
||||
|
||||
import {ScheduledPostCoreOptions} from './core_options';
|
||||
|
||||
import EnhancedScheduledPostCoreOptions from './index';
|
||||
|
||||
import type ServerDataOperator from '@database/operator/server_data_operator';
|
||||
import type {Database} from '@nozbe/watermelondb';
|
||||
|
||||
jest.mock('./core_options', () => ({
|
||||
__esModule: true,
|
||||
ScheduledPostCoreOptions: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mocked(ScheduledPostCoreOptions).mockImplementation((props) => React.createElement('ScheduledPostCoreOptions', {...props, testID: 'scheduled-post-core-options'}));
|
||||
|
||||
describe('EnhancedScheduledPostCoreOptions', () => {
|
||||
const serverUrl = 'server-1';
|
||||
const teamId = 'team1';
|
||||
let database: Database;
|
||||
let operator: ServerDataOperator;
|
||||
|
||||
beforeEach(async () => {
|
||||
await DatabaseManager.init([serverUrl]);
|
||||
const serverDatabaseAndOperator = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
database = serverDatabaseAndOperator.database;
|
||||
operator = serverDatabaseAndOperator.operator;
|
||||
|
||||
await operator.handleUsers({users: [TestHelper.fakeUser({id: 'user1', timezone: {useAutomaticTimezone: false, manualTimezone: 'America/New_York', automaticTimezone: 'America/New_York'}})], prepareRecordsOnly: false});
|
||||
await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}, {id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: 'user1'}], prepareRecordsOnly: false});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await DatabaseManager.destroyServerDatabase(serverUrl);
|
||||
});
|
||||
|
||||
it('should correctly return the isMilitaryTime preference', async () => {
|
||||
await operator.handlePreferences({
|
||||
preferences: [{
|
||||
category: Preferences.CATEGORIES.DISPLAY_SETTINGS,
|
||||
name: 'use_military_time',
|
||||
user_id: 'user1',
|
||||
value: 'true',
|
||||
}],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedScheduledPostCoreOptions
|
||||
userTimezone={getTimezone({
|
||||
useAutomaticTimezone: false,
|
||||
manualTimezone: 'America/New_York',
|
||||
automaticTimezone: 'America/New_York',
|
||||
})}
|
||||
onSelectOption={jest.fn()}
|
||||
onCustomTimeSelected={jest.fn()}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(getByTestId('scheduled-post-core-options')).toBeTruthy();
|
||||
expect(getByTestId('scheduled-post-core-options').props.isMilitaryTime).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the isMilitaryTime preference is not set', async () => {
|
||||
await operator.handlePreferences({
|
||||
preferences: [],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedScheduledPostCoreOptions
|
||||
userTimezone={getTimezone({
|
||||
useAutomaticTimezone: false,
|
||||
manualTimezone: 'America/New_York',
|
||||
automaticTimezone: 'America/New_York',
|
||||
})}
|
||||
onSelectOption={jest.fn()}
|
||||
onCustomTimeSelected={jest.fn()}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(getByTestId('scheduled-post-core-options')).toBeTruthy();
|
||||
expect(getByTestId('scheduled-post-core-options').props.isMilitaryTime).toBe(false);
|
||||
});
|
||||
});
|
||||
78
app/screens/scheduled_post_options/index.test.tsx
Normal file
78
app/screens/scheduled_post_options/index.test.tsx
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import DatabaseManager from '@database/manager/__mocks__';
|
||||
import {ScheduledPostOptions} from '@screens/scheduled_post_options/scheduled_post_picker';
|
||||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
import EnhancedScheduledPostPicker from './index';
|
||||
|
||||
import type ServerDataOperator from '@database/operator/server_data_operator';
|
||||
import type {Database} from '@nozbe/watermelondb';
|
||||
|
||||
jest.mock('./scheduled_post_picker', () => ({
|
||||
__esModule: true,
|
||||
ScheduledPostOptions: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mocked(ScheduledPostOptions).mockImplementation((props) => React.createElement('ScheduledPostOptions', {...props, testID: 'scheduled-post-options'}));
|
||||
|
||||
describe('EnhancedRescheduledDraft', () => {
|
||||
|
||||
const serverUrl = 'server-1';
|
||||
const teamId = 'team1';
|
||||
let database: Database;
|
||||
let operator: ServerDataOperator;
|
||||
|
||||
beforeEach(async () => {
|
||||
await DatabaseManager.init([serverUrl]);
|
||||
const serverDatabaseAndOperator = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
database = serverDatabaseAndOperator.database;
|
||||
operator = serverDatabaseAndOperator.operator;
|
||||
|
||||
await operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID, value: teamId}, {id: SYSTEM_IDENTIFIERS.CURRENT_USER_ID, value: 'user1'}], prepareRecordsOnly: false});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await DatabaseManager.destroyServerDatabase(serverUrl);
|
||||
});
|
||||
|
||||
it('should correctly return the current user timezone', async () => {
|
||||
await operator.handleUsers({users: [TestHelper.fakeUser({id: 'user1', timezone: {useAutomaticTimezone: false, manualTimezone: 'America/New_York', automaticTimezone: 'America/New_York'}})], prepareRecordsOnly: false});
|
||||
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedScheduledPostPicker
|
||||
onSchedule={jest.fn()}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(getByTestId('scheduled-post-options')).toBeTruthy();
|
||||
expect(getByTestId('scheduled-post-options').props.currentUserTimezone).toStrictEqual({
|
||||
useAutomaticTimezone: false,
|
||||
manualTimezone: 'America/New_York',
|
||||
automaticTimezone: 'America/New_York',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return undefined if the current user timezone is not set', async () => {
|
||||
await operator.handleUsers({users: [TestHelper.fakeUser({id: 'user1', timezone: undefined})], prepareRecordsOnly: false});
|
||||
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedScheduledPostPicker
|
||||
onSchedule={jest.fn()}
|
||||
/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
expect(getByTestId('scheduled-post-options')).toBeTruthy();
|
||||
expect(getByTestId('scheduled-post-options').props.currentUserTimezone).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -37,6 +37,7 @@ import type PostModel from '@typings/database/models/servers/post';
|
|||
import type PostsInChannelModel from '@typings/database/models/servers/posts_in_channel';
|
||||
import type PreferenceModel from '@typings/database/models/servers/preference';
|
||||
import type RoleModel from '@typings/database/models/servers/role';
|
||||
import type ScheduledPostModel from '@typings/database/models/servers/scheduled_post';
|
||||
import type TeamModel from '@typings/database/models/servers/team';
|
||||
import type ThreadModel from '@typings/database/models/servers/thread';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
|
@ -165,7 +166,7 @@ class TestHelperSingleton {
|
|||
if (c === 'x') {
|
||||
v = r;
|
||||
} else {
|
||||
// eslint-disable-next-line no-mixed-operators
|
||||
|
||||
v = (r & 0x3) | 0x8;
|
||||
}
|
||||
|
||||
|
|
@ -855,6 +856,43 @@ class TestHelperSingleton {
|
|||
};
|
||||
};
|
||||
|
||||
fakeScheduledPostModel = (overwrite?: Partial<ScheduledPostModel>): ScheduledPostModel => {
|
||||
return {
|
||||
...this.fakeModel(),
|
||||
id: this.generateId(),
|
||||
createAt: 0,
|
||||
updateAt: 0,
|
||||
channelId: this.generateId(),
|
||||
message: '',
|
||||
rootId: '',
|
||||
files: [],
|
||||
metadata: {},
|
||||
scheduledAt: 0,
|
||||
processedAt: 0,
|
||||
errorCode: '',
|
||||
toApi: jest.fn(),
|
||||
...overwrite,
|
||||
};
|
||||
};
|
||||
|
||||
fakeScheduledPost = (overwrite?: Partial<ScheduledPost>): ScheduledPost => {
|
||||
return {
|
||||
id: this.generateId(),
|
||||
channel_id: this.generateId(),
|
||||
message: '',
|
||||
root_id: '',
|
||||
files: [],
|
||||
metadata: {},
|
||||
create_at: Date.now(),
|
||||
update_at: Date.now(),
|
||||
scheduled_at: Date.now(),
|
||||
processed_at: 0,
|
||||
error_code: '',
|
||||
user_id: this.generateId(),
|
||||
...overwrite,
|
||||
};
|
||||
};
|
||||
|
||||
fakePreferenceModel = (overwrite?: Partial<PreferenceModel>): PreferenceModel => {
|
||||
return {
|
||||
...this.fakeModel(),
|
||||
|
|
|
|||
Loading…
Reference in a new issue