* feat: ai rewrite * feat: allow crating content apart from editing it * feat: feature parity with webapp * feat: feature parity with webapp * chore: fixed padding * map ux to webapp * refactored ai rewrite logic to separate package * chore: tests and lint * Update app/products/ai/rewrite/screens/options/options.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * rewrite post editor animation * i18n * ui feedback, centered icon and less top padding * chore: lint * Consolidate @ai product into @agents Move all rewrite functionality from app/products/ai/ into app/products/agents/ to unify under a single namespace. - Add rewrite types, store, hooks, components, and screens to agents - Merge AI client methods into agents client - Update screen constants (AI_* -> AGENTS_*) - Update all consumer imports - Remove AI state from EphemeralStore - Remove @ai path alias from config - Delete app/products/ai/ directory * refactor: load screens from product package * refactor: move detection logic to the agents pacakge * refactor: remove "backwards compatibility" * refactor: move hooks to proper package * refactor: styles * refactor: remove unneedd position attribute * refactor: remove unneeded cancel animation calls * refactor: "backwards compat" * refactor: optimize renderContent with useCallback for performance * refactor: rename variable to avoid confusion * refactor: update handleRewrite to use async/await for better error handling * refactor: simplify handleRewrite by always dismissing keyboard * refactor: use hook, always all keyboard.dismiss * chore: enhance AgentSelector component with FlatList support * feat: add rewriteMessage function for AI message rewriting and integrate it into useRewrite hook * refactor: simplify message length calculation in useHandleSendMessage hook * refactor: consolidate agent screen constants and integrate with existing screens * fix: update dependency array in useMemo for isUnrevealedPost to include post expiration metadata * refactor: remove unused variable to clean up Typing component * refactor: simplify logic for atDisabled and slashDisabled flags in QuickActions component * feat: add AIRewriteAction component for AI message rewriting functionality * revert: thread.ts changes manually * refactor: integrate useSafeAreaInsets * refactor: cleanup unused methods * chore: add comment to clarify casting * refactor: use_agents * chore: lint and test * fix: trim --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
144 lines
4.4 KiB
TypeScript
144 lines
4.4 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import React from 'react';
|
|
import timezones from 'timezones.json';
|
|
|
|
import {renderWithIntl} from '@test/intl-test-helper';
|
|
import {logDebug} from '@utils/log';
|
|
|
|
import locales from '../../i18n/languages';
|
|
|
|
import FormattedDate, {type FormattedDateFormat} from './index';
|
|
|
|
jest.mock('@utils/log', () => ({
|
|
logDebug: jest.fn(),
|
|
logError: jest.fn(),
|
|
logInfo: jest.fn(),
|
|
logWarning: jest.fn(),
|
|
}));
|
|
|
|
const DATE = new Date('2024-10-26T10:01:04.653Z');
|
|
const FORMATS = [
|
|
undefined,
|
|
{weekday: 'long'},
|
|
{dateStyle: 'medium'},
|
|
{month: 'short', day: 'numeric'},
|
|
{
|
|
month: 'short',
|
|
day: 'numeric',
|
|
hour: 'numeric',
|
|
minute: 'numeric',
|
|
},
|
|
{
|
|
year: 'numeric',
|
|
month: 'short',
|
|
day: 'numeric',
|
|
hour: 'numeric',
|
|
minute: 'numeric',
|
|
},
|
|
] satisfies Array<FormattedDateFormat | undefined>;
|
|
|
|
const TEST_MATRIX = Object.keys(locales).
|
|
map((locale) => FORMATS.map<[string, FormattedDateFormat | undefined]>((format) => [locale, format])).
|
|
flat(1);
|
|
|
|
function getTimezoneTestsCases() {
|
|
// Mimics the logic for the timezones offered by the web app
|
|
// in webapp/channels/src/components/user_settings/display/manage_timezones/manage_timezones.tsx
|
|
let index = 0;
|
|
const testCases = [];
|
|
let previousTimezone = '';
|
|
for (const timezone of timezones) {
|
|
if (timezone.utc[index] === previousTimezone) {
|
|
index++;
|
|
} else {
|
|
index = 0;
|
|
}
|
|
testCases.push([timezone.utc[index]]);
|
|
previousTimezone = timezone.utc[index];
|
|
}
|
|
return testCases;
|
|
}
|
|
|
|
describe('<FormattedDate/>', () => {
|
|
it.each(TEST_MATRIX)("should match snapshot for '%s' locale and '%p' format", (locale, format) => {
|
|
const wrapper = renderWithIntl(
|
|
<FormattedDate
|
|
format={format}
|
|
value={DATE}
|
|
timezone='UTC'
|
|
/>,
|
|
{locale},
|
|
);
|
|
expect(wrapper.toJSON()).toMatchSnapshot();
|
|
});
|
|
|
|
it('should render with a manual user time', () => {
|
|
const wrapper = renderWithIntl(
|
|
<FormattedDate
|
|
value={DATE}
|
|
timezone={{
|
|
automaticTimezone: '',
|
|
manualTimezone: 'Indian/Mauritius',
|
|
useAutomaticTimezone: '',
|
|
}}
|
|
/>,
|
|
);
|
|
expect(wrapper.toJSON()).toMatchSnapshot();
|
|
});
|
|
|
|
it('should render with an automatic user time', () => {
|
|
const wrapper = renderWithIntl(
|
|
<FormattedDate
|
|
value={DATE}
|
|
timezone={{
|
|
automaticTimezone: 'Indian/Mauritius',
|
|
manualTimezone: '',
|
|
useAutomaticTimezone: 'true',
|
|
}}
|
|
/>,
|
|
);
|
|
|
|
// Just check that the component render as automatic timezone is environment dependant
|
|
expect(wrapper.toJSON()).toBeTruthy();
|
|
});
|
|
|
|
it.each(getTimezoneTestsCases())('should render with timezone %s', (timezone) => {
|
|
const wrapper = renderWithIntl(
|
|
<FormattedDate
|
|
value={DATE}
|
|
timezone={timezone}
|
|
format={{hour: 'numeric', minute: 'numeric'}}
|
|
/>,
|
|
);
|
|
expect(wrapper.queryByText('Unknown')).not.toBeTruthy();
|
|
expect(logDebug).not.toHaveBeenCalled();
|
|
expect(wrapper.toJSON()).toMatchSnapshot();
|
|
});
|
|
|
|
it('should default when timezone is not found', () => {
|
|
const wrapper = renderWithIntl(
|
|
<FormattedDate
|
|
value={DATE}
|
|
timezone={'not valid timezone'}
|
|
format={{hour: 'numeric', minute: 'numeric'}}
|
|
/>,
|
|
);
|
|
expect(wrapper.queryByText('Unknown')).not.toBeTruthy();
|
|
expect(logDebug).toHaveBeenCalledTimes(1);
|
|
expect(wrapper.toJSON()).toMatchSnapshot();
|
|
});
|
|
|
|
it('should show unknown on other errors', () => {
|
|
const wrapper = renderWithIntl(
|
|
<FormattedDate
|
|
value={DATE}
|
|
timezone={undefined}
|
|
format={{hour: 'numeric', minute: 'invalid' as any}}
|
|
/>,
|
|
);
|
|
expect(wrapper.queryByText('Unknown')).toBeTruthy();
|
|
expect(logDebug).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|