Restructure omni services and add Chatwoot research snapshot
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { useAttachments } from '../useAttachments';
|
||||
import { useStore } from 'vuex';
|
||||
import { computed } from 'vue';
|
||||
|
||||
// Mock Vue's useStore
|
||||
vi.mock('vuex', () => ({
|
||||
useStore: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock Vue's computed
|
||||
vi.mock('vue', () => ({
|
||||
computed: vi.fn(fn => ({ value: fn() })),
|
||||
}));
|
||||
|
||||
describe('useAttachments', () => {
|
||||
let mockStore;
|
||||
let mockGetters;
|
||||
|
||||
beforeEach(() => {
|
||||
// Reset window.chatwootWebChannel
|
||||
delete window.chatwootWebChannel;
|
||||
|
||||
// Create mock store
|
||||
mockGetters = {};
|
||||
mockStore = {
|
||||
getters: mockGetters,
|
||||
};
|
||||
vi.mocked(useStore).mockReturnValue(mockStore);
|
||||
|
||||
// Mock computed to return a reactive-like object
|
||||
vi.mocked(computed).mockImplementation(fn => ({ value: fn() }));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('shouldShowFilePicker', () => {
|
||||
it('returns value from store getter', () => {
|
||||
mockGetters['appConfig/getShouldShowFilePicker'] = true;
|
||||
|
||||
const { shouldShowFilePicker } = useAttachments();
|
||||
|
||||
expect(shouldShowFilePicker.value).toBe(true);
|
||||
});
|
||||
|
||||
it('returns undefined when not set in store', () => {
|
||||
mockGetters['appConfig/getShouldShowFilePicker'] = undefined;
|
||||
|
||||
const { shouldShowFilePicker } = useAttachments();
|
||||
|
||||
expect(shouldShowFilePicker.value).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasAttachmentsEnabled', () => {
|
||||
it('returns true when attachments are enabled in channel config', () => {
|
||||
window.chatwootWebChannel = {
|
||||
enabledFeatures: ['attachments', 'emoji'],
|
||||
};
|
||||
|
||||
const { hasAttachmentsEnabled } = useAttachments();
|
||||
|
||||
expect(hasAttachmentsEnabled.value).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false when attachments are not enabled in channel config', () => {
|
||||
window.chatwootWebChannel = {
|
||||
enabledFeatures: ['emoji'],
|
||||
};
|
||||
|
||||
const { hasAttachmentsEnabled } = useAttachments();
|
||||
|
||||
expect(hasAttachmentsEnabled.value).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false when channel config has no enabled features', () => {
|
||||
window.chatwootWebChannel = {
|
||||
enabledFeatures: [],
|
||||
};
|
||||
|
||||
const { hasAttachmentsEnabled } = useAttachments();
|
||||
|
||||
expect(hasAttachmentsEnabled.value).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false when channel config is missing', () => {
|
||||
window.chatwootWebChannel = undefined;
|
||||
|
||||
const { hasAttachmentsEnabled } = useAttachments();
|
||||
|
||||
expect(hasAttachmentsEnabled.value).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false when enabledFeatures is missing', () => {
|
||||
window.chatwootWebChannel = {};
|
||||
|
||||
const { hasAttachmentsEnabled } = useAttachments();
|
||||
|
||||
expect(hasAttachmentsEnabled.value).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('canHandleAttachments', () => {
|
||||
beforeEach(() => {
|
||||
// Set up a default channel config
|
||||
window.chatwootWebChannel = {
|
||||
enabledFeatures: ['attachments'],
|
||||
};
|
||||
});
|
||||
|
||||
it('prioritizes SDK flag when explicitly set to true', () => {
|
||||
mockGetters['appConfig/getShouldShowFilePicker'] = true;
|
||||
|
||||
const { canHandleAttachments } = useAttachments();
|
||||
|
||||
expect(canHandleAttachments.value).toBe(true);
|
||||
});
|
||||
|
||||
it('prioritizes SDK flag when explicitly set to false', () => {
|
||||
mockGetters['appConfig/getShouldShowFilePicker'] = false;
|
||||
|
||||
const { canHandleAttachments } = useAttachments();
|
||||
|
||||
expect(canHandleAttachments.value).toBe(false);
|
||||
});
|
||||
|
||||
it('falls back to inbox settings when SDK flag is undefined', () => {
|
||||
mockGetters['appConfig/getShouldShowFilePicker'] = undefined;
|
||||
window.chatwootWebChannel = {
|
||||
enabledFeatures: ['attachments'],
|
||||
};
|
||||
|
||||
const { canHandleAttachments } = useAttachments();
|
||||
|
||||
expect(canHandleAttachments.value).toBe(true);
|
||||
});
|
||||
|
||||
it('falls back to inbox settings when SDK flag is undefined and attachments disabled', () => {
|
||||
mockGetters['appConfig/getShouldShowFilePicker'] = undefined;
|
||||
window.chatwootWebChannel = {
|
||||
enabledFeatures: ['emoji'],
|
||||
};
|
||||
|
||||
const { canHandleAttachments } = useAttachments();
|
||||
|
||||
expect(canHandleAttachments.value).toBe(false);
|
||||
});
|
||||
|
||||
it('prioritizes SDK false over inbox settings true', () => {
|
||||
mockGetters['appConfig/getShouldShowFilePicker'] = false;
|
||||
window.chatwootWebChannel = {
|
||||
enabledFeatures: ['attachments'],
|
||||
};
|
||||
|
||||
const { canHandleAttachments } = useAttachments();
|
||||
|
||||
expect(canHandleAttachments.value).toBe(false);
|
||||
});
|
||||
|
||||
it('prioritizes SDK true over inbox settings false', () => {
|
||||
mockGetters['appConfig/getShouldShowFilePicker'] = true;
|
||||
window.chatwootWebChannel = {
|
||||
enabledFeatures: ['emoji'], // no attachments
|
||||
};
|
||||
|
||||
const { canHandleAttachments } = useAttachments();
|
||||
|
||||
expect(canHandleAttachments.value).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasEmojiPickerEnabled', () => {
|
||||
it('returns true when emoji picker is enabled in channel config', () => {
|
||||
window.chatwootWebChannel = {
|
||||
enabledFeatures: ['emoji_picker', 'attachments'],
|
||||
};
|
||||
|
||||
const { hasEmojiPickerEnabled } = useAttachments();
|
||||
|
||||
expect(hasEmojiPickerEnabled.value).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false when emoji picker is not enabled in channel config', () => {
|
||||
window.chatwootWebChannel = {
|
||||
enabledFeatures: ['attachments'],
|
||||
};
|
||||
|
||||
const { hasEmojiPickerEnabled } = useAttachments();
|
||||
|
||||
expect(hasEmojiPickerEnabled.value).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldShowEmojiPicker', () => {
|
||||
it('returns value from store getter', () => {
|
||||
mockGetters['appConfig/getShouldShowEmojiPicker'] = true;
|
||||
|
||||
const { shouldShowEmojiPicker } = useAttachments();
|
||||
|
||||
expect(shouldShowEmojiPicker.value).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('integration test', () => {
|
||||
it('returns all expected properties', () => {
|
||||
mockGetters['appConfig/getShouldShowFilePicker'] = undefined;
|
||||
mockGetters['appConfig/getShouldShowEmojiPicker'] = true;
|
||||
window.chatwootWebChannel = {
|
||||
enabledFeatures: ['attachments', 'emoji_picker'],
|
||||
};
|
||||
|
||||
const result = useAttachments();
|
||||
|
||||
expect(result).toHaveProperty('shouldShowFilePicker');
|
||||
expect(result).toHaveProperty('shouldShowEmojiPicker');
|
||||
expect(result).toHaveProperty('hasAttachmentsEnabled');
|
||||
expect(result).toHaveProperty('hasEmojiPickerEnabled');
|
||||
expect(result).toHaveProperty('canHandleAttachments');
|
||||
expect(Object.keys(result)).toHaveLength(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,125 @@
|
||||
import { ref } from 'vue';
|
||||
import { useAvailability } from '../useAvailability';
|
||||
|
||||
const mockIsOnline = vi.fn();
|
||||
const mockIsInWorkingHours = vi.fn();
|
||||
const mockUseCamelCase = vi.fn(obj => obj);
|
||||
|
||||
vi.mock('widget/helpers/availabilityHelpers', () => ({
|
||||
isOnline: (...args) => mockIsOnline(...args),
|
||||
isInWorkingHours: (...args) => mockIsInWorkingHours(...args),
|
||||
}));
|
||||
|
||||
vi.mock('dashboard/composables/useTransformKeys', () => ({
|
||||
useCamelCase: obj => mockUseCamelCase(obj),
|
||||
}));
|
||||
|
||||
describe('useAvailability', () => {
|
||||
const originalWindow = window.chatwootWebChannel;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// Reset mocks to return true by default
|
||||
mockIsOnline.mockReturnValue(true);
|
||||
mockIsInWorkingHours.mockReturnValue(true);
|
||||
mockUseCamelCase.mockImplementation(obj => obj);
|
||||
|
||||
window.chatwootWebChannel = {
|
||||
workingHours: [],
|
||||
workingHoursEnabled: false,
|
||||
timezone: 'UTC',
|
||||
utcOffset: 'UTC',
|
||||
replyTime: 'in_a_few_minutes',
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.chatwootWebChannel = originalWindow;
|
||||
});
|
||||
|
||||
describe('initial state', () => {
|
||||
it('should initialize with default values', () => {
|
||||
const { availableAgents, hasOnlineAgents, isInWorkingHours, isOnline } =
|
||||
useAvailability();
|
||||
|
||||
expect(availableAgents.value).toEqual([]);
|
||||
expect(hasOnlineAgents.value).toBe(false);
|
||||
expect(isInWorkingHours.value).toBe(true);
|
||||
expect(isOnline.value).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with agents', () => {
|
||||
it('should handle agents array', () => {
|
||||
const agents = [{ id: 1 }, { id: 2 }];
|
||||
const { availableAgents, hasOnlineAgents } = useAvailability(agents);
|
||||
|
||||
expect(availableAgents.value).toEqual(agents);
|
||||
expect(hasOnlineAgents.value).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle reactive agents', () => {
|
||||
const agents = ref([{ id: 1 }]);
|
||||
const { hasOnlineAgents } = useAvailability(agents);
|
||||
|
||||
expect(hasOnlineAgents.value).toBe(true);
|
||||
|
||||
agents.value = [];
|
||||
expect(hasOnlineAgents.value).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('working hours', () => {
|
||||
const workingHours = [{ dayOfWeek: 1, openHour: 9, closeHour: 17 }];
|
||||
|
||||
beforeEach(() => {
|
||||
window.chatwootWebChannel = {
|
||||
workingHours,
|
||||
workingHoursEnabled: true,
|
||||
utcOffset: '+05:30',
|
||||
};
|
||||
});
|
||||
|
||||
it('should check working hours', () => {
|
||||
mockIsInWorkingHours.mockReturnValueOnce(true);
|
||||
const { isInWorkingHours } = useAvailability();
|
||||
const result = isInWorkingHours.value;
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(mockIsInWorkingHours).toHaveBeenCalledWith(
|
||||
expect.any(Date),
|
||||
'+05:30',
|
||||
workingHours
|
||||
);
|
||||
});
|
||||
|
||||
it('should determine online status based on working hours and agents', () => {
|
||||
mockIsOnline.mockReturnValueOnce(true);
|
||||
const { isOnline } = useAvailability([{ id: 1 }]);
|
||||
const result = isOnline.value;
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(mockIsOnline).toHaveBeenCalledWith(
|
||||
true,
|
||||
expect.any(Date),
|
||||
'+05:30',
|
||||
workingHours,
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('config changes', () => {
|
||||
it('should react to window.chatwootWebChannel changes', () => {
|
||||
const { inboxConfig } = useAvailability();
|
||||
|
||||
window.chatwootWebChannel = {
|
||||
...window.chatwootWebChannel,
|
||||
replyTime: 'in_a_day',
|
||||
};
|
||||
|
||||
expect(inboxConfig.value.replyTime).toBe('in_a_day');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { useDarkMode } from '../useDarkMode';
|
||||
import { useMapGetter } from 'dashboard/composables/store';
|
||||
|
||||
vi.mock('dashboard/composables/store', () => ({
|
||||
useMapGetter: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('useDarkMode', () => {
|
||||
let mockDarkMode;
|
||||
|
||||
beforeEach(() => {
|
||||
mockDarkMode = { value: 'light' };
|
||||
vi.mocked(useMapGetter).mockReturnValue(mockDarkMode);
|
||||
});
|
||||
|
||||
it('returns darkMode, prefersDarkMode', () => {
|
||||
const result = useDarkMode();
|
||||
expect(result).toHaveProperty('darkMode');
|
||||
expect(result).toHaveProperty('prefersDarkMode');
|
||||
});
|
||||
|
||||
describe('prefersDarkMode', () => {
|
||||
it('returns false when darkMode is light', () => {
|
||||
const { prefersDarkMode } = useDarkMode();
|
||||
expect(prefersDarkMode.value).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true when darkMode is dark', () => {
|
||||
mockDarkMode.value = 'dark';
|
||||
const { prefersDarkMode } = useDarkMode();
|
||||
expect(prefersDarkMode.value).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true when darkMode is auto and OS prefers dark mode', () => {
|
||||
mockDarkMode.value = 'auto';
|
||||
vi.spyOn(window, 'matchMedia').mockReturnValue({ matches: true });
|
||||
const { prefersDarkMode } = useDarkMode();
|
||||
expect(prefersDarkMode.value).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false when darkMode is auto and OS prefers light mode', () => {
|
||||
mockDarkMode.value = 'auto';
|
||||
vi.spyOn(window, 'matchMedia').mockReturnValue({ matches: false });
|
||||
const { prefersDarkMode } = useDarkMode();
|
||||
expect(prefersDarkMode.value).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user