Merge branch 'develop' into fix/show-error-on-clipboard-errors

This commit is contained in:
Shivam Mishra
2024-07-22 21:43:10 +05:30
committed by GitHub
240 changed files with 3180 additions and 3902 deletions
@@ -4,12 +4,12 @@
class="emoji-dialog bg-white shadow-lg dark:bg-slate-900 rounded-md border border-solid border-slate-75 dark:border-slate-800/50 box-content h-[300px] absolute right-0 -top-[95px] w-80 z-20"
>
<div class="flex flex-col">
<div class="emoji-search--wrap flex gap-2">
<div class="flex gap-2 emoji-search--wrap">
<input
ref="searchbar"
v-model="search"
type="text"
class="emoji-search--input focus:box-shadow-blue dark:focus:box-shadow-dark"
class="emoji-search--input focus:box-shadow-blue dark:focus:box-shadow-dark !mb-0"
:placeholder="$t('EMOJI.PLACEHOLDER')"
/>
<woot-button
@@ -174,7 +174,9 @@ export default {
return categoryItem ? categoryItem.emojis[0].emoji : '';
},
focusSearchInput() {
this.$refs.searchbar.focus();
this.$nextTick(() => {
this.$refs.searchbar.focus();
});
},
},
};
@@ -1,4 +1,4 @@
import DateSeparator from '../DateSeparator';
import DateSeparator from '../DateSeparator.vue';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import VueI18n from 'vue-i18n';
@@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils';
import Spinner from '../Spinner';
import Spinner from '../Spinner.vue';
describe('Spinner', () => {
test('matches snapshot', () => {
@@ -1,11 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`dateSeparator date separator snapshot 1`] = `
exports[`dateSeparator > date separator snapshot 1`] = `
<div
class="date--separator text-slate-700"
data-v-b24b73fa=""
>
Nov 18, 2019
Nov 18, 2019
</div>
`;
@@ -1,7 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Spinner matches snapshot 1`] = `
exports[`Spinner > matches snapshot 1`] = `
<span
class="spinner small "
data-v-3e416633=""
/>
`;
@@ -1,4 +1,4 @@
const { DuplicateContactException } = require('../CustomErrors');
import { DuplicateContactException } from '../CustomErrors';
describe('DuplicateContactException', () => {
it('returns correct exception', () => {
@@ -2,7 +2,7 @@ import { dispatchWindowEvent } from '../CustomEventHelper';
describe('dispatchWindowEvent', () => {
it('dispatches correct event', () => {
window.dispatchEvent = jest.fn();
window.dispatchEvent = vi.fn();
dispatchWindowEvent({ eventName: 'chatwoot:ready' });
expect(dispatchEvent).toHaveBeenCalled();
});
@@ -2,7 +2,7 @@ import { emitter } from '../mitt';
describe('emitter', () => {
it('should emit and listen to events correctly', () => {
const mockCallback = jest.fn();
const mockCallback = vi.fn();
// Subscribe to an event
emitter.on('event', mockCallback);
@@ -0,0 +1,92 @@
import {
messageStamp,
messageTimestamp,
dynamicTime,
dateFormat,
shortTimestamp,
} from 'shared/helpers/timeHelper';
beforeEach(() => {
process.env.TZ = 'UTC';
vi.useFakeTimers('modern');
const mockDate = new Date(Date.UTC(2023, 4, 5));
vi.setSystemTime(mockDate);
});
afterEach(() => {
vi.useRealTimers();
});
describe('#messageStamp', () => {
it('returns correct value', () => {
expect(messageStamp(1612971343)).toEqual('3:35 PM');
expect(messageStamp(1612971343, 'LLL d, h:mm a')).toEqual(
'Feb 10, 3:35 PM'
);
});
});
describe('#messageTimestamp', () => {
it('should return the message date in the specified format if the message was sent in the current year', () => {
expect(messageTimestamp(1680777464)).toEqual('Apr 6, 2023');
});
it('should return the message date and time in a different format if the message was sent in a different year', () => {
expect(messageTimestamp(1612971343)).toEqual('Feb 10 2021, 3:35 PM');
});
});
describe('#dynamicTime', () => {
it('returns correct value', () => {
Date.now = vi.fn(() => new Date(Date.UTC(2023, 1, 14)).valueOf());
expect(dynamicTime(1612971343)).toEqual('about 2 years ago');
});
});
describe('#dateFormat', () => {
it('returns correct value', () => {
expect(dateFormat(1612971343)).toEqual('Feb 10, 2021');
expect(dateFormat(1612971343, 'LLL d, yyyy')).toEqual('Feb 10, 2021');
});
});
describe('#shortTimestamp', () => {
// Test cases when withAgo is false or not provided
it('returns correct value without ago', () => {
expect(shortTimestamp('less than a minute ago')).toEqual('now');
expect(shortTimestamp('1 minute ago')).toEqual('1m');
expect(shortTimestamp('12 minutes ago')).toEqual('12m');
expect(shortTimestamp('a minute ago')).toEqual('1m');
expect(shortTimestamp('an hour ago')).toEqual('1h');
expect(shortTimestamp('1 hour ago')).toEqual('1h');
expect(shortTimestamp('2 hours ago')).toEqual('2h');
expect(shortTimestamp('1 day ago')).toEqual('1d');
expect(shortTimestamp('a day ago')).toEqual('1d');
expect(shortTimestamp('3 days ago')).toEqual('3d');
expect(shortTimestamp('a month ago')).toEqual('1mo');
expect(shortTimestamp('1 month ago')).toEqual('1mo');
expect(shortTimestamp('2 months ago')).toEqual('2mo');
expect(shortTimestamp('a year ago')).toEqual('1y');
expect(shortTimestamp('1 year ago')).toEqual('1y');
expect(shortTimestamp('4 years ago')).toEqual('4y');
});
// Test cases when withAgo is true
it('returns correct value with ago', () => {
expect(shortTimestamp('less than a minute ago', true)).toEqual('now');
expect(shortTimestamp('1 minute ago', true)).toEqual('1m ago');
expect(shortTimestamp('12 minutes ago', true)).toEqual('12m ago');
expect(shortTimestamp('a minute ago', true)).toEqual('1m ago');
expect(shortTimestamp('an hour ago', true)).toEqual('1h ago');
expect(shortTimestamp('1 hour ago', true)).toEqual('1h ago');
expect(shortTimestamp('2 hours ago', true)).toEqual('2h ago');
expect(shortTimestamp('1 day ago', true)).toEqual('1d ago');
expect(shortTimestamp('a day ago', true)).toEqual('1d ago');
expect(shortTimestamp('3 days ago', true)).toEqual('3d ago');
expect(shortTimestamp('a month ago', true)).toEqual('1mo ago');
expect(shortTimestamp('1 month ago', true)).toEqual('1mo ago');
expect(shortTimestamp('2 months ago', true)).toEqual('2mo ago');
expect(shortTimestamp('a year ago', true)).toEqual('1y ago');
expect(shortTimestamp('1 year ago', true)).toEqual('1y ago');
expect(shortTimestamp('4 years ago', true)).toEqual('4y ago');
});
});
@@ -0,0 +1,93 @@
import {
format,
isSameYear,
fromUnixTime,
formatDistanceToNow,
} from 'date-fns';
/**
* Formats a Unix timestamp into a human-readable time format.
* @param {number} time - Unix timestamp.
* @param {string} [dateFormat='h:mm a'] - Desired format of the time.
* @returns {string} Formatted time string.
*/
export const messageStamp = (time, dateFormat = 'h:mm a') => {
const unixTime = fromUnixTime(time);
return format(unixTime, dateFormat);
};
/**
* Provides a formatted timestamp, adjusting the format based on the current year.
* @param {number} time - Unix timestamp.
* @param {string} [dateFormat='MMM d, yyyy'] - Desired date format.
* @returns {string} Formatted date string.
*/
export const messageTimestamp = (time, dateFormat = 'MMM d, yyyy') => {
const messageTime = fromUnixTime(time);
const now = new Date();
const messageDate = format(messageTime, dateFormat);
if (!isSameYear(messageTime, now)) {
return format(messageTime, 'LLL d y, h:mm a');
}
return messageDate;
};
/**
* Converts a Unix timestamp to a relative time string (e.g., 3 hours ago).
* @param {number} time - Unix timestamp.
* @returns {string} Relative time string.
*/
export const dynamicTime = time => {
const unixTime = fromUnixTime(time);
return formatDistanceToNow(unixTime, { addSuffix: true });
};
/**
* Formats a Unix timestamp into a specified date format.
* @param {number} time - Unix timestamp.
* @param {string} [dateFormat='MMM d, yyyy'] - Desired date format.
* @returns {string} Formatted date string.
*/
export const dateFormat = (time, df = 'MMM d, yyyy') => {
const unixTime = fromUnixTime(time);
return format(unixTime, df);
};
/**
* Converts a detailed time description into a shorter format, optionally appending 'ago'.
* @param {string} time - Detailed time description (e.g., 'a minute ago').
* @param {boolean} [withAgo=false] - Whether to append 'ago' to the result.
* @returns {string} Shortened time description.
*/
export const shortTimestamp = (time, withAgo = false) => {
// This function takes a time string and converts it to a short time string
// with the following format: 1m, 1h, 1d, 1mo, 1y
// The function also takes an optional boolean parameter withAgo
// which will add the word "ago" to the end of the time string
const suffix = withAgo ? ' ago' : '';
const timeMappings = {
'less than a minute ago': 'now',
'a minute ago': `1m${suffix}`,
'an hour ago': `1h${suffix}`,
'a day ago': `1d${suffix}`,
'a month ago': `1mo${suffix}`,
'a year ago': `1y${suffix}`,
};
// Check if the time string is one of the specific cases
if (timeMappings[time]) {
return timeMappings[time];
}
const convertToShortTime = time
.replace(/about|over|almost|/g, '')
.replace(' minute ago', `m${suffix}`)
.replace(' minutes ago', `m${suffix}`)
.replace(' hour ago', `h${suffix}`)
.replace(' hours ago', `h${suffix}`)
.replace(' day ago', `d${suffix}`)
.replace(' days ago', `d${suffix}`)
.replace(' month ago', `mo${suffix}`)
.replace(' months ago', `mo${suffix}`)
.replace(' year ago', `y${suffix}`)
.replace(' years ago', `y${suffix}`);
return convertToShortTime;
};
@@ -1,9 +0,0 @@
import { CONTENT_TYPES } from '../constants/contentType';
export default {
computed: {
isEmailContentType() {
return this.contentType === CONTENT_TYPES.INCOMING_EMAIL;
},
},
};
@@ -1,32 +0,0 @@
import { shallowMount } from '@vue/test-utils';
import contentTypeMixin from '../contentTypeMixin';
describe('contentTypeMixin', () => {
it('returns true if contentType is incoming_email', () => {
const Component = {
render() {},
mixins: [contentTypeMixin],
computed: {
contentType() {
return 'incoming_email';
},
},
};
const wrapper = shallowMount(Component);
expect(wrapper.vm.isEmailContentType).toBe(true);
});
it('returns false if contentType is not incoming_email', () => {
const Component = {
render() {},
mixins: [contentTypeMixin],
computed: {
contentType() {
return 'input_select';
},
},
};
const wrapper = shallowMount(Component);
expect(wrapper.vm.isEmailContentType).toBe(false);
});
});
@@ -1,29 +1,26 @@
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { shallowMount } from '@vue/test-utils';
import rtlMixin from 'shared/mixins/rtlMixin';
describe('rtlMixin', () => {
it('returns is direction right-to-left view', () => {
const Component = {
const createComponent = rtl_view => {
return shallowMount({
render() {},
mixins: [rtlMixin, uiSettingsMixin],
data() {
return { uiSettings: { rtl_view: true } };
mixins: [rtlMixin],
computed: {
uiSettings() {
return { rtl_view };
},
},
};
const wrapper = shallowMount(Component);
});
};
it('returns is direction right-to-left view', () => {
const wrapper = createComponent(true);
expect(wrapper.vm.isRTLView).toBe(true);
});
it('returns is direction left-to-right view', () => {
const Component = {
render() {},
mixins: [rtlMixin, uiSettingsMixin],
data() {
return { uiSettings: { rtl_view: false } };
},
};
const wrapper = shallowMount(Component);
const wrapper = createComponent(false);
expect(wrapper.vm.isRTLView).toBe(false);
});
});