Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mattermost/mattermost-mobile
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: a036a4945c3eedf727ccedd5aa5c21ddb1edbdc4
Choose a base ref
..
head repository: mattermost/mattermost-mobile
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: b83266faf9b50a868fac5cb8f29c11b954ed06ee
Choose a head ref
26 changes: 17 additions & 9 deletions app/components/post_draft/send_handler/send_handler.test.tsx
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
import {act, fireEvent, waitFor} from '@testing-library/react-native';
import React from 'react';

import {removeDraft} from '@actions/local/draft';
import {General} from '@constants';
import {DRAFT_TYPE_DRAFT, DRAFT_TYPE_SCHEDULED, type DraftType} from '@screens/global_drafts/constants';
import {renderWithEverything} from '@test/intl-test-helper';
@@ -20,12 +21,17 @@ jest.mock('@actions/remote/channel', () => ({

jest.mock('@utils/post', () => ({
sendMessageWithAlert: jest.fn(),
persistentNotificationsConfirmation: jest.fn(),
}));

jest.mock('@screens/navigation', () => ({
dismissBottomSheet: jest.fn(),
}));

jest.mock('@actions/local/draft', () => ({
removeDraft: jest.fn(),
}));

describe('components/post_draft/send_handler/SendHandler', () => {
let database: Database;
const baseProps = {
@@ -102,7 +108,7 @@ describe('components/post_draft/send_handler/SendHandler', () => {
expect(wrapper.getByText('Send')).toBeTruthy();
});

it('should handle post priority updates', async () => {
it('should show correct post priority', async () => {
const wrapper = renderWithEverything(
<SendHandler
{...baseProps}
@@ -112,7 +118,8 @@ describe('components/post_draft/send_handler/SendHandler', () => {

const draftInput = wrapper.getByTestId('test_send_handler');
expect(draftInput).toBeTruthy();
expect(baseProps.postPriority.priority).toBe('urgent');
const priority = wrapper.getByText('URGENT');
expect(priority).toBeTruthy();
});

it('should pass correct props to SendDraft component when in draft view', async () => {
@@ -149,12 +156,12 @@ describe('components/post_draft/send_handler/SendHandler', () => {
files: [],
};

const emptyWrapper = renderWithEverything(
<SendHandler {...emptyProps}/>, {database},
wrapper.rerender(
<SendHandler {...emptyProps}/>,
);

// Button should still exist but pressing it should not trigger send when empty
const emptyButton = emptyWrapper.getByTestId('send_draft_button');
const emptyButton = wrapper.getByTestId('send_draft_button');
expect(emptyButton).toBeTruthy();

fireEvent.press(emptyButton);
@@ -187,7 +194,7 @@ describe('components/post_draft/send_handler/SendHandler', () => {
await waitFor(() => {
expect(sendMessageWithAlert).toHaveBeenCalledWith(expect.objectContaining({
channelName: 'test-channel',
title: expect.any(String),
title: 'Send message now',
intl: expect.any(Object),
sendMessageHandler: expect.any(Function),
}));
@@ -197,18 +204,16 @@ describe('components/post_draft/send_handler/SendHandler', () => {
it('should execute sendMessageHandler when send_draft_button is clicked', async () => {
// Mock implementation to capture the sendMessageHandler
let capturedHandler: Function;
(sendMessageWithAlert as jest.Mock).mockImplementation((params) => {
jest.mocked(sendMessageWithAlert).mockImplementation((params) => {
capturedHandler = params.sendMessageHandler;
return Promise.resolve();
});

const mockClearDraft = jest.fn();
const props = {
...baseProps,
isFromDraftView: true,
draftType: DRAFT_TYPE_DRAFT as DraftType,
value: 'test message',
clearDraft: mockClearDraft,
};

const wrapper = renderWithEverything(
@@ -230,5 +235,8 @@ describe('components/post_draft/send_handler/SendHandler', () => {
await act(async () => {
await capturedHandler();
});

// Varify removeDraft function is been called.
expect(removeDraft).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -77,7 +77,10 @@ const DateTimeSelector = ({timezone, handleChange, isMilitaryTime, theme, showIn
};

return (
<View style={styles.container}>
<View
style={styles.container}
testID='custom_date_time_picker'
>
<View style={styles.buttonContainer}>
<Button
testID={'custom_status_clear_after.menu_item.date_and_time.button.date'}
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import React from 'react';
import {Screens} from '@constants';
import {dismissBottomSheet, showModal} from '@screens/navigation';
import {renderWithIntlAndTheme} from '@test/intl-test-helper';
import test_helper from '@test/test_helper';

import RescheduledDraft from './rescheduled_draft';

@@ -62,8 +63,7 @@ describe('RescheduledDraft', () => {

fireEvent.press(getByTestId('rescheduled_draft'));

// Wait for the next tick to allow async operations to complete
await new Promise((resolve) => setTimeout(resolve, 0));
await test_helper.wait(0);

expect(dismissBottomSheet).toHaveBeenCalledWith(baseProps.bottomSheetId);
expect(showModal).toHaveBeenCalledWith(
55 changes: 0 additions & 55 deletions app/screens/draft_scheduled_post_options/send_draft.test.tsx
Original file line number Diff line number Diff line change
@@ -3,10 +3,7 @@

import React from 'react';

import {removeDraft} from '@actions/local/draft';
import {deleteScheduledPost} from '@actions/remote/scheduled_post';
import {General, Screens} from '@constants';
import {useServerUrl} from '@context/server';
import {DRAFT_TYPE_DRAFT, DRAFT_TYPE_SCHEDULED, type DraftType} from '@screens/global_drafts/constants';
import {dismissBottomSheet} from '@screens/navigation';
import {fireEvent, renderWithIntlAndTheme} from '@test/intl-test-helper';
@@ -116,56 +113,4 @@ describe('Send Draft', () => {
const {getByText} = wrapper;
expect(getByText('Send')).toBeTruthy();
});

it('should call removeDraft when clearing a draft', () => {
const mockHandleSendMessage = jest.fn();
jest.mock('@hooks/handle_send_message', () => ({
useHandleSendMessage: () => ({
handleSendMessage: mockHandleSendMessage,
}),
}));

const TestClearDraft = () => {
const serverUrl = useServerUrl();
const channelId = 'channel_id';
const rootId = 'root_id';
const draftType = DRAFT_TYPE_DRAFT;

React.useEffect(() => {
// This simulates the clearDraft function logic
if (draftType === DRAFT_TYPE_DRAFT) {
removeDraft(serverUrl, channelId, rootId);
}
}, []);

return null;
};

renderWithIntlAndTheme(<TestClearDraft/>);

expect(removeDraft).toHaveBeenCalledWith('https://server.com', 'channel_id', 'root_id');
expect(deleteScheduledPost).not.toHaveBeenCalled();
});

it('should call deleteScheduledPost when clearing a scheduled post', () => {
const TestClearScheduledDraft = () => {
const serverUrl = useServerUrl();
const draftType = DRAFT_TYPE_SCHEDULED;
const postId = 'post_id';

React.useEffect(() => {
// This simulates the clearDraft function logic
if (draftType !== (DRAFT_TYPE_DRAFT as DraftType) && postId) {
deleteScheduledPost(serverUrl, postId);
}
}, []);

return null;
};

renderWithIntlAndTheme(<TestClearScheduledDraft/>);

expect(deleteScheduledPost).toHaveBeenCalledWith('https://server.com', 'post_id');
expect(removeDraft).not.toHaveBeenCalled();
});
});
93 changes: 49 additions & 44 deletions app/screens/reschedule_draft/reschedule_draft.test.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {act} from '@testing-library/react-native';
import {act, fireEvent} from '@testing-library/react-native';
import moment from 'moment-timezone';
import React from 'react';
import {Navigation} from 'react-native-navigation';

import {updateScheduledPost} from '@actions/remote/scheduled_post';
import {Screens} from '@constants';
import {useServerUrl} from '@context/server';
import DateTimeSelector from '@screens/custom_status_clear_after/components/date_time_selector';
import {dismissModal, setButtons} from '@screens/navigation';
import {renderWithEverything} from '@test/intl-test-helper';
import TestHelper from '@test/test_helper';
@@ -17,10 +17,9 @@ import RescheduledDraft from './reschedule_draft';

import type {Database} from '@nozbe/watermelondb';
import type ScheduledPostModel from '@typings/database/models/servers/scheduled_post';
import type {AvailableScreens} from '@typings/screens/navigation';

jest.mock('@actions/remote/scheduled_post', () => ({
updateScheduledPost: jest.fn(),
updateScheduledPost: jest.fn().mockResolvedValue({scheduledPost: {} as ScheduledPost, error: undefined}),
}));

jest.mock('@screens/navigation', () => ({
@@ -61,7 +60,7 @@ describe('RescheduledDraft', () => {
} as unknown as ScheduledPostModel;

const baseProps = {
componentId: 'test-component-id' as AvailableScreens,
componentId: Screens.RESCHEDULE_DRAFT,
closeButtonId: 'close-button-id',
currentUserTimezone: {
useAutomaticTimezone: true,
@@ -88,58 +87,66 @@ describe('RescheduledDraft', () => {
expect(getByTestId('edit_post.screen')).toBeTruthy();
});

it('should initialize with save button disabled', () => {
it('should have navigation component registered on initialization', () => {
renderWithEverything(<RescheduledDraft {...baseProps}/>, {database});

expect(setButtons).toHaveBeenCalledWith(
'test-component-id',
expect.objectContaining({
rightButtons: expect.arrayContaining([
expect.objectContaining({
enabled: true,
}),
]),
}),
// Verify navigation listener was registered
expect(Navigation.events().registerComponentListener).toHaveBeenCalledWith(
expect.any(Object),
baseProps.componentId,
);

// Check that the component registered with proper navigation handler
const registerCall = jest.mocked(Navigation.events().registerComponentListener).mock.calls[0][0];
expect(registerCall).toBeDefined();
expect(registerCall.navigationButtonPressed).toBeDefined();
});

it('Should enable save button when data change', async () => {
it('Should enable save button when data changes', async () => {
jest.mocked(updateScheduledPost).mockResolvedValue({scheduledPost: {} as ScheduledPost, error: undefined});
const {UNSAFE_getByType: getByType} = renderWithEverything(
<RescheduledDraft {...baseProps}/>, {database},

const {getByTestId} = renderWithEverything(
<RescheduledDraft {...baseProps}/>,
{database},
);

const dateTimeSelector = getByType(DateTimeSelector);
const dateTimeSelector = getByTestId('custom_date_time_picker'); // Ensure testID is set in the component
expect(dateTimeSelector).toBeTruthy();

const newDate = moment().add(2, 'days');
await act(async () => {
dateTimeSelector.props.handleChange(newDate);
fireEvent(dateTimeSelector, 'handleChange', newDate);
});

expect(setButtons).toHaveBeenCalled();

const mockCalls = (setButtons as jest.Mock).mock.calls;
// Use jest.mocked for proper type inference
const setButtonsMock = jest.mocked(setButtons);
const mockCalls = setButtonsMock.mock.calls;

const lastCallIndex = mockCalls.length - 1;
const setButtonsCall = mockCalls[lastCallIndex][1];
const setButtonsCall = mockCalls[lastCallIndex]?.[1]; // Ensure lastCallIndex exists

expect(setButtonsCall.rightButtons).toBeDefined();
expect(setButtonsCall.rightButtons.length).toBeGreaterThan(0);
expect(setButtonsCall).toBeDefined();
expect(setButtonsCall?.rightButtons).toBeDefined();
expect(setButtonsCall?.rightButtons?.length).toBeGreaterThan(0);

const saveButton = setButtonsCall.rightButtons[0];
expect(saveButton.enabled).toBeTruthy();
const saveButton = setButtonsCall?.rightButtons?.[0];
expect(saveButton?.enabled).toBeTruthy();
});

it('should call Navigation event when save button is pressed', async () => {
const {UNSAFE_getByType: getByType} = renderWithEverything(
<RescheduledDraft {...baseProps}/>, {database},
const {getByTestId} = renderWithEverything(
<RescheduledDraft {...baseProps}/>,
{database},
);

// Change the date to enable the save button
const dateTimeSelector = getByType(DateTimeSelector);
const dateTimeSelector = getByTestId('custom_date_time_picker'); // Ensure testID is set in the component
expect(dateTimeSelector).toBeTruthy();

const newDate = moment().add(2, 'days');
await act(async () => {
dateTimeSelector.props.handleChange(newDate);
fireEvent(dateTimeSelector, 'handleChange', newDate);
});

// Verify navigation listener was registered
@@ -149,17 +156,17 @@ describe('RescheduledDraft', () => {
);

// Get the navigationButtonPressed handler
const functionToCall = jest.mocked(Navigation.events().registerComponentListener).mock.calls[0][0].navigationButtonPressed;
const functionToCall = jest.mocked(Navigation.events().registerComponentListener).mock.calls[1][0].navigationButtonPressed;

// Simulate pressing the save button
await act(async () => {
if (functionToCall) {
functionToCall({
buttonId: 'reschedule-draft',
componentId: '',
});
}
functionToCall?.({
buttonId: 'reschedule-draft',
componentId: '',
});
});

expect(dismissModal).toHaveBeenCalledWith({componentId: baseProps.componentId});
});

it('should dismiss modal when close button is pressed', async () => {
@@ -178,12 +185,10 @@ describe('RescheduledDraft', () => {

// Simulate pressing the close button
await act(async () => {
if (functionToCall) {
functionToCall({
buttonId: baseProps.closeButtonId,
componentId: '',
});
}
functionToCall?.({
buttonId: baseProps.closeButtonId,
componentId: '',
});
});

// Verify dismissModal was called
Loading