feat: Migrate availability mixins to composable and helper (#11596)

# Pull Request Template

## Description

**This PR includes:**

* Refactored two legacy mixins (`availability.js`,
`nextAvailability.js`) into a Vue 3 composable (`useAvailability`),
helper module and component based rendering logic.
* Fixed an issue where the widget wouldn't load if business hours were
enabled but all days were unchecked.
* Fixed translation issue
[[#11280](https://github.com/chatwoot/chatwoot/issues/11280)](https://github.com/chatwoot/chatwoot/issues/11280).
* Reduced code complexity and size.
* Added test coverage for both the composable and helper functions.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

### Loom video

https://www.loom.com/share/2bc3ed694b4349419505e275d14d0b98?sid=22d585e4-0dc7-4242-bcb6-e3edc16e3aee

### Story
<img width="995" height="442" alt="image"
src="https://github.com/user-attachments/assets/d6340738-07db-41d5-86fa-a8ecf734cc70"
/>



## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules


Fixes https://github.com/chatwoot/chatwoot/issues/12012

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
This commit is contained in:
Sivin Varghese
2025-08-22 00:43:34 +05:30
committed by GitHub
parent 1a1dfd09cb
commit 6ca38e10e9
21 changed files with 1662 additions and 1006 deletions

View File

@@ -1,100 +0,0 @@
import { utcToZonedTime } from 'date-fns-tz';
import { isTimeAfter } from 'shared/helpers/DateHelper';
export default {
computed: {
channelConfig() {
return window.chatwootWebChannel;
},
replyTime() {
return window.chatwootWebChannel.replyTime;
},
replyTimeStatus() {
switch (this.replyTime) {
case 'in_a_few_minutes':
return this.$t('REPLY_TIME.IN_A_FEW_MINUTES');
case 'in_a_few_hours':
return this.$t('REPLY_TIME.IN_A_FEW_HOURS');
case 'in_a_day':
return this.$t('REPLY_TIME.IN_A_DAY');
default:
return this.$t('REPLY_TIME.IN_A_FEW_HOURS');
}
},
replyWaitMessage() {
const { workingHoursEnabled } = this.channelConfig;
if (workingHoursEnabled) {
return this.isOnline
? this.replyTimeStatus
: `${this.$t('REPLY_TIME.BACK_IN')} ${this.timeLeftToBackInOnline}`;
}
return this.isOnline
? this.replyTimeStatus
: this.$t('TEAM_AVAILABILITY.OFFLINE');
},
outOfOfficeMessage() {
return this.channelConfig.outOfOfficeMessage;
},
isInBetweenTheWorkingHours() {
const {
openHour,
openMinute,
closeHour,
closeMinute,
closedAllDay,
openAllDay,
} = this.currentDayAvailability;
if (openAllDay) {
return true;
}
if (closedAllDay) {
return false;
}
const { utcOffset } = this.channelConfig;
const today = this.getDateWithOffset(utcOffset);
const currentHours = today.getHours();
const currentMinutes = today.getMinutes();
const isAfterStartTime = isTimeAfter(
currentHours,
currentMinutes,
openHour,
openMinute
);
const isBeforeEndTime = isTimeAfter(
closeHour,
closeMinute,
currentHours,
currentMinutes
);
return isAfterStartTime && isBeforeEndTime;
},
currentDayAvailability() {
const { utcOffset } = this.channelConfig;
const dayOfTheWeek = this.getDateWithOffset(utcOffset).getDay();
const [workingHourConfig = {}] = this.channelConfig.workingHours.filter(
workingHour => workingHour.day_of_week === dayOfTheWeek
);
return {
closedAllDay: workingHourConfig.closed_all_day,
openHour: workingHourConfig.open_hour,
openMinute: workingHourConfig.open_minutes,
closeHour: workingHourConfig.close_hour,
closeMinute: workingHourConfig.close_minutes,
openAllDay: workingHourConfig.open_all_day,
};
},
isInBusinessHours() {
const { workingHoursEnabled } = this.channelConfig;
return workingHoursEnabled ? this.isInBetweenTheWorkingHours : true;
},
},
methods: {
getDateWithOffset(utcOffset) {
return utcToZonedTime(new Date().toISOString(), utcOffset);
},
},
};

View File

@@ -1,250 +0,0 @@
import {
timeSlotParse,
defaultTimeSlot,
} from 'dashboard/routes/dashboard/settings/inbox/helpers/businessHour.js';
import { utcToZonedTime } from 'date-fns-tz';
import { generateRelativeTime } from 'shared/helpers/DateHelper';
const MINUTE_ROUNDING_FACTOR = 5;
export default {
data() {
return {
dayNames: [
this.$t('DAY_NAMES.SUNDAY'),
this.$t('DAY_NAMES.MONDAY'),
this.$t('DAY_NAMES.TUESDAY'),
this.$t('DAY_NAMES.WEDNESDAY'),
this.$t('DAY_NAMES.THURSDAY'),
this.$t('DAY_NAMES.FRIDAY'),
this.$t('DAY_NAMES.SATURDAY'),
],
timeSlots: [...defaultTimeSlot],
timeSlot: {},
};
},
computed: {
channelConfig() {
return window.chatwootWebChannel;
},
workingHours() {
return this.channelConfig.workingHours;
},
newDateWithTimeZone() {
return utcToZonedTime(new Date(), this.timeZoneValue);
},
presentHour() {
return this.newDateWithTimeZone.getHours();
},
presentMinute() {
return this.newDateWithTimeZone.getMinutes();
},
currentDay() {
const date = this.newDateWithTimeZone;
const day = date.getDay();
const currentDay = Object.keys(this.dayNames).find(
key => this.dayNames[key] === this.dayNames[day]
);
return Number(currentDay);
},
timeZoneValue() {
return this.channelConfig.timezone;
},
languageCode() {
return window.chatwootWebChannel.locale;
},
currentDayWorkingHours() {
return this.workingHours.find(
slot => slot.day_of_week === this.currentDay
);
},
nextDayWorkingHours() {
let nextDay = this.getNextDay(this.currentDay);
let nextWorkingHour = this.getNextWorkingHour(nextDay);
// It gets the next working hour for the next day. If there is no working hour for the next day,
// it keeps iterating through the days of the week until it finds the next working hour.
while (!nextWorkingHour) {
nextDay = this.getNextDay(nextDay);
nextWorkingHour = this.getNextWorkingHour(nextDay);
}
return nextWorkingHour;
},
currentDayTimings() {
const {
open_hour: openHour,
open_minutes: openMinute,
close_hour: closeHour,
} = this.currentDayWorkingHours ?? {};
return {
openHour,
openMinute,
closeHour,
};
},
nextDayTimings() {
const { open_hour: openHour, open_minutes: openMinute } =
this.nextDayWorkingHours ?? {};
return {
openHour,
openMinute,
};
},
dayDiff() {
// Here this is used to get the difference between current day and next working day
const nextDay = this.nextDayWorkingHours.day_of_week;
const totalDays = 6;
return nextDay > this.currentDay
? nextDay - this.currentDay - 1
: totalDays - this.currentDay + nextDay;
},
dayNameOfNextWorkingDay() {
return this.dayNames[this.nextDayWorkingHours.day_of_week];
},
hoursAndMinutesBackInOnline() {
if (this.presentHour >= this.currentDayTimings.closeHour) {
return this.getHoursAndMinutesUntilNextDayOpen(
this.nextDayWorkingHours.open_all_day
? 0
: this.nextDayTimings.openHour,
this.nextDayTimings.openMinute,
this.currentDayTimings.closeHour
);
}
return this.getHoursAndMinutesUntilNextDayOpen(
this.currentDayTimings.openHour,
this.currentDayTimings.openMinute,
this.currentDayTimings.closeHour
);
},
exactTimeInAmPm() {
return `${
this.timeSlot.day === this.currentDay ? `at ${this.timeSlot.from}` : ''
}`;
},
hoursAndMinutesLeft() {
const { hoursLeft, minutesLeft } = this.hoursAndMinutesBackInOnline;
const timeLeftChars = [];
if (hoursLeft > 0) {
const roundedUpHoursLeft = minutesLeft > 0 ? hoursLeft + 1 : hoursLeft;
const hourRelative = generateRelativeTime(
roundedUpHoursLeft,
'hour',
this.languageCode
);
timeLeftChars.push(`${hourRelative}`);
}
if (minutesLeft > 0 && hoursLeft === 0) {
const roundedUpMinLeft =
Math.ceil(minutesLeft / MINUTE_ROUNDING_FACTOR) *
MINUTE_ROUNDING_FACTOR;
const minRelative = generateRelativeTime(
roundedUpMinLeft,
'minutes',
this.languageCode
);
timeLeftChars.push(`${minRelative}`);
}
return timeLeftChars.join(' ');
},
hoursAndMinutesToBack() {
const { hoursLeft, minutesLeft } = this.hoursAndMinutesBackInOnline;
if (hoursLeft >= 3) {
return this.exactTimeInAmPm;
}
if (hoursLeft > 0 || minutesLeft > 0) {
return this.hoursAndMinutesLeft;
}
return 'in some time';
},
timeLeftToBackInOnline() {
if (
this.hoursAndMinutesBackInOnline.hoursLeft >= 24 ||
(this.timeSlot.day !== this.currentDay && this.dayDiff === 0)
) {
const hourRelative = generateRelativeTime(
this.dayDiff + 1,
'days',
this.languageCode
);
return `${hourRelative}`;
}
if (
this.dayDiff >= 1 &&
this.presentHour >= this.currentDayTimings.closeHour
) {
return `on ${this.dayNameOfNextWorkingDay}`;
}
return this.hoursAndMinutesToBack;
},
},
mounted() {
this.setTimeSlot();
},
methods: {
getNextDay(day) {
// This code calculates the next day of the week based on the current day. If the current day is Saturday (6), then the next day will be Sunday (0).
return (day + 1) % 7;
},
getNextWorkingHour(day) {
const workingHour = this.workingHours.find(
slot => slot.day_of_week === day
);
if (workingHour && !workingHour.closed_all_day) {
return workingHour;
}
return null;
},
getHoursAndMinutesUntilNextDayOpen(
openHour, // If the present time is after the closing time of the current day, then the openHour will be the opening hour of the next day else it will be the opening hour of the current day.
openMinutes, // If the present time is after the closing time of the current day, then the openMinutes will be the opening minutes of the next day else it will be the opening minutes of the current day.
closeHour // The closeHour will be the closing hour of the current day. It will be used to calculate the time remaining until the next day's opening hours.
) {
// This code calculates the time remaining until the next day's opening hours,
// given the current time, the opening hours, and the closing hours of the current day.
if (closeHour < openHour) {
openHour += 24;
}
let diffMinutes =
openHour * 60 +
openMinutes -
(this.presentHour * 60 + this.presentMinute);
diffMinutes = diffMinutes < 0 ? diffMinutes + 24 * 60 : diffMinutes;
const [hoursLeft, minutesLeft] = [
Math.floor(diffMinutes / 60),
diffMinutes % 60,
];
// It returns the remaining time in hours and minutes as an object with keys hours and minutes.
return { hoursLeft, minutesLeft };
},
setTimeSlot() {
// It checks if the working hours feature is enabled for the store.
const timeSlots = this.workingHours;
// If the present hour is after the closing hour of the current day,
// then the next day's working hours will be used to calculate the time remaining until the next day's opening hours,
// else the current day's working hours will be used
const currentSlot =
this.presentHour >= this.currentDayTimings.closeHour
? this.nextDayWorkingHours
: this.currentDayWorkingHours;
// It parses the working hours to get the time slots in AM/PM format.
const slots = timeSlotParse(timeSlots).length
? timeSlotParse(timeSlots)
: defaultTimeSlot;
this.timeSlots = slots;
// It finds the time slot for the current slot.
this.timeSlot = this.timeSlots.find(
slot => slot.day === currentSlot.day_of_week
);
},
},
};

View File

@@ -1,10 +0,0 @@
export default {
methods: {
async replaceRoute(name, params = {}) {
if (this.$route.name !== name) {
return this.$router.replace({ name, params });
}
return undefined;
},
},
};

View File

@@ -1,87 +0,0 @@
import { mount } from '@vue/test-utils';
import { defineComponent, h } from 'vue';
import availabilityMixin from '../availability';
import { vi } from 'vitest';
global.chatwootWebChannel = {
workingHoursEnabled: true,
workingHours: [
{
day_of_week: 3,
closed_all_day: false,
open_hour: 8,
open_minutes: 30,
close_hour: 17,
close_minutes: 35,
open_all_day: false,
},
{
day_of_week: 4,
closed_all_day: false,
open_hour: 8,
open_minutes: 30,
close_hour: 17,
close_minutes: 30,
open_all_day: false,
},
],
utcOffset: '-07:00',
};
let Component;
describe('availabilityMixin', () => {
beforeEach(() => {
vi.useRealTimers();
Component = defineComponent({
mixins: [availabilityMixin],
render() {
return h('div');
},
});
});
it('returns valid isInBetweenWorkingHours if in different timezone', () => {
vi.useFakeTimers().setSystemTime(
new Date('Thu Apr 14 2022 06:04:46 GMT+0530')
);
const wrapper = mount(Component);
expect(wrapper.vm.isInBetweenTheWorkingHours).toBe(true);
});
it('returns valid isInBetweenWorkingHours if in same timezone', () => {
global.chatwootWebChannel.utcOffset = '+05:30';
vi.useFakeTimers().setSystemTime(
new Date('Thu Apr 14 2022 09:01:46 GMT+0530')
);
const wrapper = mount(Component);
expect(wrapper.vm.isInBetweenTheWorkingHours).toBe(true);
});
it('returns false if closed all day', () => {
global.chatwootWebChannel.utcOffset = '-07:00';
global.chatwootWebChannel.workingHours = [
{ day_of_week: 3, closed_all_day: true },
];
vi.useFakeTimers().setSystemTime(
new Date('Thu Apr 14 2022 09:01:46 GMT+0530')
);
const wrapper = mount(Component);
expect(wrapper.vm.isInBetweenTheWorkingHours).toBe(false);
});
it('returns true if open all day', () => {
global.chatwootWebChannel.utcOffset = '-07:00';
global.chatwootWebChannel.workingHours = [
{ day_of_week: 3, open_all_day: true },
];
vi.useFakeTimers().setSystemTime(
new Date('Thu Apr 14 2022 09:01:46 GMT+0530')
);
const wrapper = mount(Component);
expect(wrapper.vm.isInBetweenTheWorkingHours).toBe(true);
});
});

View File

@@ -1,402 +0,0 @@
import { defineComponent, h } from 'vue';
import { mount } from '@vue/test-utils';
import nextAvailabilityTimeMixin from '../nextAvailabilityTime';
describe('nextAvailabilityTimeMixin', () => {
const chatwootWebChannel = {
workingHoursEnabled: true,
workingHours: [
{
day_of_week: 0,
open_hour: 9,
closed_all_day: false,
open_minutes: 0,
close_hour: 17,
},
{
day_of_week: 1,
open_hour: 9,
closed_all_day: false,
open_minutes: 0,
close_hour: 17,
},
{
day_of_week: 2,
open_hour: 9,
closed_all_day: false,
open_minutes: 0,
close_hour: 17,
},
{
day_of_week: 3,
open_hour: 9,
closed_all_day: false,
open_minutes: 0,
close_hour: 17,
},
{
day_of_week: 4,
open_hour: 9,
closed_all_day: false,
open_minutes: 0,
close_hour: 17,
},
{
day_of_week: 5,
open_hour: 9,
closed_all_day: false,
open_minutes: 0,
close_hour: 17,
},
{
day_of_week: 6,
open_hour: 9,
closed_all_day: false,
open_minutes: 0,
close_hour: 17,
},
],
};
let Component;
beforeEach(() => {
Component = defineComponent({
mixins: [nextAvailabilityTimeMixin],
render() {
return h('div');
},
});
window.chatwootWebChannel = chatwootWebChannel;
});
afterEach(() => {
delete window.chatwootWebChannel;
});
beforeEach(() => {
vi.useRealTimers();
});
it('should return day names', () => {
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
expect(wrapper.vm.dayNames).toEqual([
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
]);
});
it('should return channelConfig', () => {
const wrapper = mount(Component);
expect(wrapper.vm.channelConfig).toEqual(chatwootWebChannel);
});
it('should return workingHours', () => {
const wrapper = mount(Component);
expect(wrapper.vm.workingHours).toEqual(chatwootWebChannel.workingHours);
});
it('should return currentDayWorkingHours', () => {
const currentDay = new Date().getDay();
const expectedWorkingHours = chatwootWebChannel.workingHours.find(
slot => slot.day_of_week === currentDay
);
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
expect(wrapper.vm.currentDayWorkingHours).toEqual(expectedWorkingHours);
});
it('should return nextDayWorkingHours', () => {
const currentDay = new Date().getDay();
const nextDay = currentDay === 6 ? 0 : currentDay + 1;
const expectedWorkingHours = chatwootWebChannel.workingHours.find(
slot => slot.day_of_week === nextDay
);
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
expect(wrapper.vm.nextDayWorkingHours).toEqual(expectedWorkingHours);
});
it('should return presentHour', () => {
const wrapper = mount(Component);
expect(wrapper.vm.presentHour).toBe(new Date().getHours());
});
it('should return presentMinute', () => {
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
expect(wrapper.vm.presentMinute).toBe(new Date().getMinutes());
});
it('should return currentDay', () => {
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
const date = new Date();
const day = date.getDay();
const currentDay = Object.keys(wrapper.vm.dayNames).find(
key => wrapper.vm.dayNames[key] === wrapper.vm.dayNames[day]
);
expect(wrapper.vm.currentDay).toBe(Number(currentDay));
});
it('should return currentDayTimings', () => {
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
const {
open_hour: openHour,
open_minutes: openMinute,
close_hour: closeHour,
} = wrapper.vm.currentDayWorkingHours;
expect(wrapper.vm.currentDayTimings).toEqual({
openHour,
openMinute,
closeHour,
});
});
it('should return nextDayTimings', () => {
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
const { open_hour: openHour, open_minutes: openMinute } =
wrapper.vm.nextDayWorkingHours;
expect(wrapper.vm.nextDayTimings).toEqual({
openHour,
openMinute,
});
});
it('should return dayDiff', () => {
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
const currentDay = wrapper.vm.currentDay;
const nextDay = wrapper.vm.nextDayWorkingHours.day_of_week;
const totalDays = 6;
const expectedDayDiff =
nextDay > currentDay
? nextDay - currentDay - 1
: totalDays - currentDay + nextDay;
expect(wrapper.vm.dayDiff).toEqual(expectedDayDiff);
});
it('should return dayNameOfNextWorkingDay', () => {
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
const nextDay = wrapper.vm.nextDayWorkingHours.day_of_week;
const expectedDayName = wrapper.vm.dayNames[nextDay];
expect(wrapper.vm.dayNameOfNextWorkingDay).toEqual(expectedDayName);
});
it('should return hoursAndMinutesBackInOnline', () => {
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
const currentDayCloseHour =
chatwootWebChannel.workingHours[wrapper.vm.currentDay].close_hour;
const nextDayOpenHour =
chatwootWebChannel.workingHours[
wrapper.vm.currentDay === 6 ? 0 : wrapper.vm.currentDay + 1
].open_hour;
const nextDayOpenMinute =
chatwootWebChannel.workingHours[
wrapper.vm.currentDay === 6 ? 0 : wrapper.vm.currentDay + 1
].open_minutes;
const expectedHoursAndMinutes =
wrapper.vm.getHoursAndMinutesUntilNextDayOpen(
nextDayOpenHour,
nextDayOpenMinute,
currentDayCloseHour
);
expect(wrapper.vm.hoursAndMinutesBackInOnline).toEqual(
expectedHoursAndMinutes
);
});
it('should return getNextDay', () => {
const wrapper = mount(Component);
expect(wrapper.vm.getNextDay(6)).toBe(0);
});
it('should return in 30 minutes', () => {
vi.useFakeTimers('modern').setSystemTime(
new Date('Thu Apr 14 2022 14:04:46 GMT+0530')
);
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
chatwootWebChannel.workingHours[4].open_hour = 18;
chatwootWebChannel.workingHours[4].open_minutes = 0;
chatwootWebChannel.workingHours[4].close_hour = 23;
expect(wrapper.vm.timeLeftToBackInOnline).toBe('in 30 minutes');
});
it('should return in 2 hours', () => {
vi.useFakeTimers('modern').setSystemTime(
new Date('Thu Apr 14 2022 22:04:46 GMT+0530')
);
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
chatwootWebChannel.workingHours[4].open_hour = 19;
expect(wrapper.vm.timeLeftToBackInOnline).toBe('in 2 hours');
});
it('should return at 09:00 AM', () => {
vi.useFakeTimers('modern').setSystemTime(
new Date('Thu Apr 15 2022 22:04:46 GMT+0530')
);
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
chatwootWebChannel.workingHours[4].open_hour = 10;
expect(wrapper.vm.timeLeftToBackInOnline).toBe('at 09:00 AM');
});
it('should return tomorrow', () => {
vi.useFakeTimers('modern').setSystemTime(
new Date('Thu Apr 1 2022 23:04:46 GMT+0530')
);
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
chatwootWebChannel.workingHours[4].open_hour = 9;
chatwootWebChannel.workingHours[4].close_hour = 16;
expect(wrapper.vm.timeLeftToBackInOnline).toBe('tomorrow');
});
it.skip('should return on Saturday', () => {
vi.useFakeTimers('modern').setSystemTime(
new Date('Thu Apr 14 2022 23:04:46 GMT+0530')
);
const wrapper = mount(Component);
wrapper.vm.dayNames = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
chatwootWebChannel.workingHours[4].open_hour = 9;
chatwootWebChannel.workingHours[4].close_hour = 16;
chatwootWebChannel.workingHours[5].closed_all_day = true;
expect(wrapper.vm.timeLeftToBackInOnline).toBe('on Saturday');
});
});