309 lines
11 KiB
TypeScript
309 lines
11 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
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 {dismissModal, setButtons} from '@screens/navigation';
|
|
import {renderWithEverything} from '@test/intl-test-helper';
|
|
import TestHelper from '@test/test_helper';
|
|
import {showSnackBar} from '@utils/snack_bar';
|
|
|
|
import RescheduledDraft from './reschedule_draft';
|
|
|
|
import type {Database} from '@nozbe/watermelondb';
|
|
import type ScheduledPostModel from '@typings/database/models/servers/scheduled_post';
|
|
|
|
jest.mock('@actions/remote/scheduled_post', () => ({
|
|
updateScheduledPost: jest.fn().mockResolvedValue({scheduledPost: {} as ScheduledPost, error: undefined}),
|
|
}));
|
|
|
|
jest.mock('@utils/snack_bar', () => ({
|
|
showSnackBar: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('@screens/navigation', () => ({
|
|
buildNavigationButton: jest.fn().mockReturnValue({
|
|
id: 'reschedule-draft',
|
|
testID: 'reschedule-draft.save.button',
|
|
showAsAction: 'always',
|
|
}),
|
|
dismissModal: jest.fn(),
|
|
setButtons: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('react-native-navigation', () => {
|
|
const registerComponentListenerMock = jest.fn();
|
|
return {
|
|
Navigation: {
|
|
events: () => ({
|
|
registerComponentListener: registerComponentListenerMock,
|
|
}),
|
|
},
|
|
};
|
|
});
|
|
|
|
jest.mock('@context/server', () => ({
|
|
useServerUrl: jest.fn(),
|
|
}));
|
|
|
|
const SERVER_URL = 'https://appv1.mattermost.com';
|
|
|
|
describe('RescheduledDraft', () => {
|
|
let database: Database;
|
|
|
|
const mockDraft = {
|
|
scheduledAt: moment().add(1, 'day').valueOf(),
|
|
toApi: jest.fn().mockResolvedValue({
|
|
scheduled_at: moment().add(1, 'day').valueOf(),
|
|
}),
|
|
} as unknown as ScheduledPostModel;
|
|
|
|
const baseProps = {
|
|
componentId: Screens.RESCHEDULE_DRAFT,
|
|
closeButtonId: 'close-button-id',
|
|
currentUserTimezone: {
|
|
useAutomaticTimezone: true,
|
|
automaticTimezone: 'America/New_York',
|
|
manualTimezone: '',
|
|
},
|
|
draft: mockDraft,
|
|
};
|
|
|
|
beforeAll(async () => {
|
|
const server = await TestHelper.setupServerDatabase();
|
|
database = server.database;
|
|
});
|
|
|
|
beforeEach(() => {
|
|
jest.mocked(useServerUrl).mockReturnValue(SERVER_URL);
|
|
});
|
|
|
|
it('should render correctly', () => {
|
|
const {getByTestId} = renderWithEverything(
|
|
<RescheduledDraft {...baseProps}/>, {database},
|
|
);
|
|
|
|
expect(getByTestId('edit_post.screen')).toBeTruthy();
|
|
});
|
|
|
|
it('should have navigation component registered on initialization', async () => {
|
|
const {getByTestId} = renderWithEverything(<RescheduledDraft {...baseProps}/>, {database});
|
|
|
|
// 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();
|
|
|
|
// Check if save button is in the header and on clicking should call the navigation handler
|
|
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 () => {
|
|
fireEvent(dateTimeSelector, 'handleChange', newDate);
|
|
});
|
|
|
|
// Verify navigation listener was registered
|
|
expect(Navigation.events().registerComponentListener).toHaveBeenCalledWith(
|
|
expect.any(Object),
|
|
baseProps.componentId,
|
|
);
|
|
});
|
|
|
|
it('Should enable save button when data changes', async () => {
|
|
jest.mocked(updateScheduledPost).mockResolvedValue({scheduledPost: {} as ScheduledPost, error: undefined});
|
|
|
|
const setButtonsMock = jest.mocked(setButtons);
|
|
setButtonsMock.mockClear();
|
|
|
|
const {getByTestId} = renderWithEverything(
|
|
<RescheduledDraft {...baseProps}/>,
|
|
{database},
|
|
);
|
|
|
|
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 () => {
|
|
fireEvent(dateTimeSelector, 'handleChange', newDate);
|
|
});
|
|
|
|
expect(setButtons).toHaveBeenCalledTimes(1);
|
|
|
|
// Use jest.mocked for proper type inference
|
|
const mockCalls = setButtonsMock.mock.calls;
|
|
|
|
const lastCallIndex = mockCalls.length - 1;
|
|
const setButtonsCall = mockCalls[lastCallIndex]?.[1]; // Ensure lastCallIndex exists
|
|
|
|
expect(setButtonsCall).toBeDefined();
|
|
expect(setButtonsCall?.rightButtons).toBeDefined();
|
|
expect(setButtonsCall?.rightButtons?.length).toBeGreaterThan(0);
|
|
|
|
const saveButton = setButtonsCall?.rightButtons?.[0];
|
|
expect(saveButton?.enabled).toBeTruthy();
|
|
});
|
|
|
|
it('should call Navigation event when save button is pressed', async () => {
|
|
const {getByTestId} = renderWithEverything(
|
|
<RescheduledDraft {...baseProps}/>,
|
|
{database},
|
|
);
|
|
|
|
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 () => {
|
|
fireEvent(dateTimeSelector, 'handleChange', newDate);
|
|
});
|
|
|
|
// Verify navigation listener was registered
|
|
expect(Navigation.events().registerComponentListener).toHaveBeenCalledWith(
|
|
expect.any(Object),
|
|
baseProps.componentId,
|
|
);
|
|
|
|
// Get the navigationButtonPressed handler
|
|
const functionToCall = jest.mocked(Navigation.events().registerComponentListener).mock.calls[1][0].navigationButtonPressed;
|
|
|
|
// Simulate pressing the save button
|
|
await act(async () => {
|
|
functionToCall?.({
|
|
buttonId: 'reschedule-draft',
|
|
componentId: '',
|
|
});
|
|
});
|
|
|
|
expect(dismissModal).toHaveBeenCalledWith({componentId: baseProps.componentId});
|
|
});
|
|
|
|
it('should dismiss modal when close button is pressed', async () => {
|
|
renderWithEverything(
|
|
<RescheduledDraft {...baseProps}/>, {database},
|
|
);
|
|
|
|
// Verify navigation listener was registered
|
|
expect(Navigation.events().registerComponentListener).toHaveBeenCalledWith(
|
|
expect.any(Object),
|
|
baseProps.componentId,
|
|
);
|
|
|
|
// Get the navigationButtonPressed handler
|
|
const functionToCall = jest.mocked(Navigation.events().registerComponentListener).mock.calls[0][0].navigationButtonPressed;
|
|
|
|
// Simulate pressing the close button
|
|
await act(async () => {
|
|
functionToCall?.({
|
|
buttonId: baseProps.closeButtonId,
|
|
componentId: '',
|
|
});
|
|
});
|
|
|
|
// Verify dismissModal was called
|
|
expect(dismissModal).toHaveBeenCalledWith({componentId: baseProps.componentId});
|
|
});
|
|
|
|
it('should show snackbar when no time is selected', async () => {
|
|
jest.mocked(updateScheduledPost).mockResolvedValue({scheduledPost: {} as ScheduledPost, error: undefined});
|
|
const {getByTestId} = renderWithEverything(
|
|
<RescheduledDraft {...baseProps}/>, {database},
|
|
);
|
|
|
|
// Force selectedTime.current to be null
|
|
const dateTimeSelector = getByTestId('custom_date_time_picker');
|
|
expect(dateTimeSelector).toBeTruthy();
|
|
|
|
// Reset the mock to clear previous calls
|
|
jest.mocked(showSnackBar).mockClear();
|
|
|
|
await act(async () => {
|
|
fireEvent(dateTimeSelector, 'handleChange', '');
|
|
});
|
|
|
|
// Get the navigationButtonPressed handler
|
|
const functionToCall = jest.mocked(Navigation.events().registerComponentListener).mock.calls[1][0].navigationButtonPressed;
|
|
|
|
// Simulate pressing the save button without selecting a time
|
|
await act(async () => {
|
|
// This will force the component to use the initial null value for selectedTime.current
|
|
if (functionToCall) {
|
|
functionToCall({
|
|
buttonId: 'reschedule-draft',
|
|
componentId: '',
|
|
});
|
|
}
|
|
});
|
|
|
|
// Verify showSnackBar was called with error message
|
|
expect(showSnackBar).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
barType: 'RESCHEDULED_POST',
|
|
customMessage: expect.stringContaining('No time selected'),
|
|
type: 'error',
|
|
}),
|
|
);
|
|
|
|
expect(updateScheduledPost).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should pass the draft scheduledAt time as initialDate to DateTimeSelector', () => {
|
|
const scheduledTime = moment().add(3, 'days').valueOf();
|
|
const draftWithScheduledTime = {
|
|
...mockDraft,
|
|
scheduledAt: scheduledTime,
|
|
} as unknown as ScheduledPostModel;
|
|
|
|
const propsWithScheduledDraft = {
|
|
...baseProps,
|
|
draft: draftWithScheduledTime,
|
|
};
|
|
|
|
const {getByTestId} = renderWithEverything(
|
|
<RescheduledDraft {...propsWithScheduledDraft}/>,
|
|
{database},
|
|
);
|
|
|
|
const dateTimeSelector = getByTestId('custom_date_time_picker');
|
|
expect(dateTimeSelector).toBeTruthy();
|
|
});
|
|
|
|
it('should initialize with draft scheduledAt time for different timezone', () => {
|
|
const scheduledTime = moment.tz('2024-12-25 14:30', 'Asia/Tokyo').valueOf();
|
|
const draftWithScheduledTime = {
|
|
...mockDraft,
|
|
scheduledAt: scheduledTime,
|
|
} as unknown as ScheduledPostModel;
|
|
|
|
const propsWithTimezone = {
|
|
...baseProps,
|
|
draft: draftWithScheduledTime,
|
|
currentUserTimezone: {
|
|
useAutomaticTimezone: true,
|
|
automaticTimezone: 'Asia/Tokyo',
|
|
manualTimezone: '',
|
|
},
|
|
};
|
|
|
|
const {getByTestId} = renderWithEverything(
|
|
<RescheduledDraft {...propsWithTimezone}/>,
|
|
{database},
|
|
);
|
|
|
|
const dateTimeSelector = getByTestId('custom_date_time_picker');
|
|
expect(dateTimeSelector).toBeTruthy();
|
|
});
|
|
});
|