For large accounts, summary report queries can take several seconds to complete, often times hitting the 15-second production request timeout. The existing implementation silently swallows these failures and provides no feedback during loading. Users see stale data with no indication that a fetch is in progress, and if they interact with filters while a request is in flight, they trigger race conditions that can result in mismatched data being displayed. This is a UX-level fix for what is fundamentally a performance problem. While the underlying query performance is addressed separately, users need proper feedback either way ## Approach The PR adds three things: 1. A loading overlay on the table, to provide feedback on loading state 2. Disabled filter inputs during loading so that the user does not request new information that can cause race conditions in updating the store 3. Silent retry before showing an error. The retry exists because these queries often succeed on the second attempt—likely due to database query caching. Rather than immediately showing an error and forcing the user to manually retry, we do it automatically. If the second attempt also fails, we show a toast so the user knows something went wrong. The store previously caught and discarded errors entirely. It now rethrows them after resetting the loading flag, allowing components to handle failures as they see fit. ### Previews #### Double Retry and Error https://github.com/user-attachments/assets/c189b173-8017-44b7-9493-417d65582c95 #### Loading State https://github.com/user-attachments/assets/9f899c20-fbad-469b-93cc-f0d05d0853b0 --------- Co-authored-by: iamsivin <iamsivin@gmail.com>
232 lines
7.3 KiB
JavaScript
232 lines
7.3 KiB
JavaScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import SummaryReportsAPI from 'dashboard/api/summaryReports';
|
|
import store, { initialState } from '../summaryReports';
|
|
|
|
vi.mock('dashboard/api/summaryReports', () => ({
|
|
default: {
|
|
getInboxReports: vi.fn(),
|
|
getAgentReports: vi.fn(),
|
|
getTeamReports: vi.fn(),
|
|
getLabelReports: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
describe('Summary Reports Store', () => {
|
|
let commit;
|
|
|
|
beforeEach(() => {
|
|
// Reset all mocks before each test
|
|
vi.clearAllMocks();
|
|
commit = vi.fn();
|
|
});
|
|
|
|
describe('Initial State', () => {
|
|
it('should have the correct initial state structure', () => {
|
|
expect(initialState).toEqual({
|
|
inboxSummaryReports: [],
|
|
agentSummaryReports: [],
|
|
teamSummaryReports: [],
|
|
labelSummaryReports: [],
|
|
uiFlags: {
|
|
isFetchingInboxSummaryReports: false,
|
|
isFetchingAgentSummaryReports: false,
|
|
isFetchingTeamSummaryReports: false,
|
|
isFetchingLabelSummaryReports: false,
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Getters', () => {
|
|
const state = {
|
|
inboxSummaryReports: [{ id: 1 }],
|
|
agentSummaryReports: [{ id: 2 }],
|
|
teamSummaryReports: [{ id: 3 }],
|
|
labelSummaryReports: [{ id: 4 }],
|
|
uiFlags: { isFetchingInboxSummaryReports: true },
|
|
};
|
|
|
|
it('should return inbox summary reports', () => {
|
|
expect(store.getters.getInboxSummaryReports(state)).toEqual([{ id: 1 }]);
|
|
});
|
|
|
|
it('should return agent summary reports', () => {
|
|
expect(store.getters.getAgentSummaryReports(state)).toEqual([{ id: 2 }]);
|
|
});
|
|
|
|
it('should return team summary reports', () => {
|
|
expect(store.getters.getTeamSummaryReports(state)).toEqual([{ id: 3 }]);
|
|
});
|
|
|
|
it('should return label summary reports', () => {
|
|
expect(store.getters.getLabelSummaryReports(state)).toEqual([{ id: 4 }]);
|
|
});
|
|
|
|
it('should return UI flags', () => {
|
|
expect(store.getters.getUIFlags(state)).toEqual({
|
|
isFetchingInboxSummaryReports: true,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Mutations', () => {
|
|
it('should set inbox summary report', () => {
|
|
const state = { ...initialState };
|
|
const data = [{ id: 1 }];
|
|
|
|
store.mutations.setInboxSummaryReport(state, data);
|
|
expect(state.inboxSummaryReports).toEqual(data);
|
|
});
|
|
|
|
it('should set agent summary report', () => {
|
|
const state = { ...initialState };
|
|
const data = [{ id: 2 }];
|
|
|
|
store.mutations.setAgentSummaryReport(state, data);
|
|
expect(state.agentSummaryReports).toEqual(data);
|
|
});
|
|
|
|
it('should set team summary report', () => {
|
|
const state = { ...initialState };
|
|
const data = [{ id: 3 }];
|
|
|
|
store.mutations.setTeamSummaryReport(state, data);
|
|
expect(state.teamSummaryReports).toEqual(data);
|
|
});
|
|
|
|
it('should set label summary report', () => {
|
|
const state = { ...initialState };
|
|
const data = [{ id: 4 }];
|
|
|
|
store.mutations.setLabelSummaryReport(state, data);
|
|
expect(state.labelSummaryReports).toEqual(data);
|
|
});
|
|
|
|
it('should merge UI flags with existing flags', () => {
|
|
const state = {
|
|
uiFlags: { flag1: true, flag2: false },
|
|
};
|
|
const newFlags = { flag2: true, flag3: true };
|
|
|
|
store.mutations.setUIFlags(state, newFlags);
|
|
expect(state.uiFlags).toEqual({
|
|
flag1: true,
|
|
flag2: true,
|
|
flag3: true,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Actions', () => {
|
|
describe('fetchInboxSummaryReports', () => {
|
|
it('should fetch inbox reports successfully', async () => {
|
|
const params = { date: '2025-01-01' };
|
|
const mockResponse = {
|
|
data: [{ report_id: 1, report_name: 'Test' }],
|
|
};
|
|
|
|
SummaryReportsAPI.getInboxReports.mockResolvedValue(mockResponse);
|
|
|
|
await store.actions.fetchInboxSummaryReports({ commit }, params);
|
|
|
|
expect(commit).toHaveBeenCalledWith('setUIFlags', {
|
|
isFetchingInboxSummaryReports: true,
|
|
});
|
|
expect(SummaryReportsAPI.getInboxReports).toHaveBeenCalledWith(params);
|
|
expect(commit).toHaveBeenCalledWith('setInboxSummaryReport', [
|
|
{ reportId: 1, reportName: 'Test' },
|
|
]);
|
|
expect(commit).toHaveBeenCalledWith('setUIFlags', {
|
|
isFetchingInboxSummaryReports: false,
|
|
});
|
|
});
|
|
|
|
it('should reset uiFlags and rethrow error on failure', async () => {
|
|
SummaryReportsAPI.getInboxReports.mockRejectedValue(
|
|
new Error('API Error')
|
|
);
|
|
|
|
await expect(
|
|
store.actions.fetchInboxSummaryReports({ commit }, {})
|
|
).rejects.toThrow('API Error');
|
|
|
|
expect(commit).toHaveBeenCalledWith('setUIFlags', {
|
|
isFetchingInboxSummaryReports: false,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('fetchAgentSummaryReports', () => {
|
|
it('should fetch agent reports successfully', async () => {
|
|
const params = { agentId: 123 };
|
|
const mockResponse = {
|
|
data: [{ agent_id: 123, agent_name: 'Test Agent' }],
|
|
};
|
|
|
|
SummaryReportsAPI.getAgentReports.mockResolvedValue(mockResponse);
|
|
|
|
await store.actions.fetchAgentSummaryReports({ commit }, params);
|
|
|
|
expect(commit).toHaveBeenCalledWith('setUIFlags', {
|
|
isFetchingAgentSummaryReports: true,
|
|
});
|
|
expect(SummaryReportsAPI.getAgentReports).toHaveBeenCalledWith(params);
|
|
expect(commit).toHaveBeenCalledWith('setAgentSummaryReport', [
|
|
{ agentId: 123, agentName: 'Test Agent' },
|
|
]);
|
|
expect(commit).toHaveBeenCalledWith('setUIFlags', {
|
|
isFetchingAgentSummaryReports: false,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('fetchTeamSummaryReports', () => {
|
|
it('should fetch team reports successfully', async () => {
|
|
const params = { teamId: 456 };
|
|
const mockResponse = {
|
|
data: [{ team_id: 456, team_name: 'Test Team' }],
|
|
};
|
|
|
|
SummaryReportsAPI.getTeamReports.mockResolvedValue(mockResponse);
|
|
|
|
await store.actions.fetchTeamSummaryReports({ commit }, params);
|
|
|
|
expect(commit).toHaveBeenCalledWith('setUIFlags', {
|
|
isFetchingTeamSummaryReports: true,
|
|
});
|
|
expect(SummaryReportsAPI.getTeamReports).toHaveBeenCalledWith(params);
|
|
expect(commit).toHaveBeenCalledWith('setTeamSummaryReport', [
|
|
{ teamId: 456, teamName: 'Test Team' },
|
|
]);
|
|
expect(commit).toHaveBeenCalledWith('setUIFlags', {
|
|
isFetchingTeamSummaryReports: false,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('fetchLabelSummaryReports', () => {
|
|
it('should fetch label reports successfully', async () => {
|
|
const params = { labelId: 789 };
|
|
const mockResponse = {
|
|
data: [{ label_id: 789, label_name: 'Test Label' }],
|
|
};
|
|
|
|
SummaryReportsAPI.getLabelReports.mockResolvedValue(mockResponse);
|
|
|
|
await store.actions.fetchLabelSummaryReports({ commit }, params);
|
|
|
|
expect(commit).toHaveBeenCalledWith('setUIFlags', {
|
|
isFetchingLabelSummaryReports: true,
|
|
});
|
|
expect(SummaryReportsAPI.getLabelReports).toHaveBeenCalledWith(params);
|
|
expect(commit).toHaveBeenCalledWith('setLabelSummaryReport', [
|
|
{ labelId: 789, labelName: 'Test Label' },
|
|
]);
|
|
expect(commit).toHaveBeenCalledWith('setUIFlags', {
|
|
isFetchingLabelSummaryReports: false,
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|