Skip to content

Commit 4864607

Browse files
committed
Track selected event in url (#806)
* track selected event in url * address comments * describe useInitialSelectedEvent hook
1 parent bb63445 commit 4864607

12 files changed

+485
-31
lines changed

src/views/workflow-history/__tests__/workflow-history.test.tsx

Lines changed: 116 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,27 @@ import { Suspense } from 'react';
33
import { HttpResponse } from 'msw';
44
import { VirtuosoMockContext } from 'react-virtuoso';
55

6-
import { act, render, screen, userEvent } from '@/test-utils/rtl';
6+
import {
7+
act,
8+
render,
9+
screen,
10+
userEvent,
11+
waitForElementToBeRemoved,
12+
} from '@/test-utils/rtl';
713

14+
import * as usePageFiltersModule from '@/components/page-filters/hooks/use-page-filters';
815
import { type Props as PageFiltersToggleProps } from '@/components/page-filters/page-filters-toggle/page-filters-toggle.types';
916
import { type GetWorkflowHistoryResponse } from '@/route-handlers/get-workflow-history/get-workflow-history.types';
1017
import { describeWorkflowResponse } from '@/views/workflow-page/__fixtures__/describe-workflow-response';
1118

1219
import { completedActivityTaskEvents } from '../__fixtures__/workflow-history-activity-events';
20+
import { completedDecisionTaskEvents } from '../__fixtures__/workflow-history-decision-events';
1321
import WorkflowHistory from '../workflow-history';
1422

23+
jest.mock('@/hooks/use-page-query-params/use-page-query-params', () =>
24+
jest.fn(() => [{ historySelectedEventId: '1' }, jest.fn()])
25+
);
26+
1527
jest.mock(
1628
'../workflow-history-compact-event-card/workflow-history-compact-event-card',
1729
() => jest.fn(() => <div>Compact group Card</div>)
@@ -51,6 +63,11 @@ jest.mock('@/components/page-filters/hooks/use-page-filters', () =>
5163

5264
jest.mock('../config/workflow-history-filters.config', () => []);
5365

66+
jest.mock(
67+
'@/components/section-loading-indicator/section-loading-indicator',
68+
() => jest.fn(() => <div>keep loading events</div>)
69+
);
70+
5471
describe('WorkflowHistory', () => {
5572
it('renders page correctly', async () => {
5673
setup({});
@@ -74,7 +91,7 @@ describe('WorkflowHistory', () => {
7491

7592
it('throws an error if the request fails', async () => {
7693
try {
77-
await act(() => setup({ error: true }));
94+
await act(async () => await setup({ error: true }));
7895
} catch (error) {
7996
expect((error as Error)?.message).toBe(
8097
'Failed to fetch workflow history'
@@ -84,7 +101,7 @@ describe('WorkflowHistory', () => {
84101

85102
it('throws an error if the workflow summary request fails', async () => {
86103
try {
87-
await act(() => setup({ summaryError: true }));
104+
await act(async () => await setup({ summaryError: true }));
88105
} catch (error) {
89106
expect((error as Error)?.message).toBe(
90107
'Failed to fetch workflow summary'
@@ -98,7 +115,7 @@ describe('WorkflowHistory', () => {
98115
});
99116

100117
it('should hide filters on executing toggle button onClick', async () => {
101-
const { user } = setup({});
118+
const { user } = await setup({});
102119
const toggleButton = await screen.findByText('Filter Toggle');
103120

104121
await user.click(toggleButton);
@@ -107,25 +124,83 @@ describe('WorkflowHistory', () => {
107124
});
108125

109126
it('should show timeline when the Timeline button is clicked', async () => {
110-
const { user } = setup({});
127+
const { user } = await setup({});
111128
const timelineButton = await screen.findByText('Timeline');
112129

113130
await user.click(timelineButton);
114131

115132
expect(screen.queryByText('Timeline chart')).toBeInTheDocument();
116133
});
134+
135+
it('should show loading while searching for initial selectedEventId', async () => {
136+
const { getRequestResolver } = await setup({
137+
resolveLoadMoreManually: true,
138+
pageQueryParamsValues: { historySelectedEventId: '3' },
139+
hasNextPage: true,
140+
});
141+
142+
await act(() => {
143+
const resolver = getRequestResolver();
144+
resolver({
145+
history: {
146+
events: [completedDecisionTaskEvents[0]],
147+
},
148+
archived: false,
149+
nextPageToken: 'mock-next-page-token',
150+
rawHistory: [],
151+
});
152+
});
153+
154+
expect(await screen.findByText('keep loading events')).toBeInTheDocument();
155+
156+
const secondPageResolver = getRequestResolver();
157+
secondPageResolver({
158+
history: {
159+
events: [completedDecisionTaskEvents[1]],
160+
},
161+
archived: false,
162+
nextPageToken: 'mock-next-page-token',
163+
rawHistory: [],
164+
});
165+
166+
expect(
167+
await screen.findByText('keep loading events')
168+
).not.toBeInTheDocument();
169+
});
117170
});
118171

119-
function setup({
172+
async function setup({
120173
error,
121174
summaryError,
175+
resolveLoadMoreManually,
176+
pageQueryParamsValues = {},
177+
hasNextPage,
122178
}: {
123179
error?: boolean;
124180
summaryError?: boolean;
181+
resolveLoadMoreManually?: boolean;
182+
pageQueryParamsValues?: Record<string, string>;
183+
hasNextPage?: boolean;
125184
}) {
126185
const user = userEvent.setup();
186+
187+
if (pageQueryParamsValues) {
188+
jest.spyOn(usePageFiltersModule, 'default').mockReturnValue({
189+
queryParams: pageQueryParamsValues,
190+
setQueryParams: jest.fn(),
191+
activeFiltersCount: 0,
192+
resetAllFilters: jest.fn(),
193+
});
194+
}
195+
196+
type ReqResolver = (r: GetWorkflowHistoryResponse) => void;
197+
let requestResolver: ReqResolver = () => {};
198+
let requestRejector = () => {};
199+
const getRequestResolver = () => requestResolver;
200+
const getRequestRejector = () => requestRejector;
201+
let requestIndex = -1;
127202
const renderResult = render(
128-
<Suspense>
203+
<Suspense fallback={'Suspense placeholder'}>
129204
<WorkflowHistory
130205
params={{
131206
domain: 'test-domain',
@@ -141,25 +216,41 @@ function setup({
141216
{
142217
path: '/api/domains/:domain/:cluster/workflows/:workflowId/:runId/history',
143218
httpMethod: 'GET',
144-
...(error
145-
? {
146-
httpResolver: () => {
147-
return HttpResponse.json(
148-
{ message: 'Failed to fetch workflow history' },
149-
{ status: 500 }
219+
mockOnce: false,
220+
httpResolver: async () => {
221+
requestIndex = requestIndex + 1;
222+
if (requestIndex > 0 && resolveLoadMoreManually) {
223+
await new Promise((resolve, reject) => {
224+
requestResolver = (result: GetWorkflowHistoryResponse) =>
225+
resolve(HttpResponse.json(result, { status: 200 }));
226+
requestRejector = () =>
227+
reject(
228+
HttpResponse.json(
229+
{ message: 'Failed to fetch workflow history' },
230+
{ status: 500 }
231+
)
150232
);
151-
},
152-
}
153-
: {
154-
jsonResponse: {
233+
});
234+
} else {
235+
if (error)
236+
return HttpResponse.json(
237+
{ message: 'Failed to fetch workflow history' },
238+
{ status: 500 }
239+
);
240+
241+
return HttpResponse.json(
242+
{
155243
history: {
156244
events: completedActivityTaskEvents,
157245
},
158246
archived: false,
159-
nextPageToken: '',
247+
nextPageToken: hasNextPage ? 'mock-next-page-token' : '',
160248
rawHistory: [],
161249
} satisfies GetWorkflowHistoryResponse,
162-
}),
250+
{ status: 200 }
251+
);
252+
}
253+
},
163254
},
164255
{
165256
path: '/api/domains/:domain/:cluster/workflows/:workflowId/:runId',
@@ -182,13 +273,17 @@ function setup({
182273
{
183274
wrapper: ({ children }) => (
184275
<VirtuosoMockContext.Provider
185-
value={{ viewportHeight: 1000, itemHeight: 100 }}
276+
value={{ viewportHeight: 300, itemHeight: 100 }}
186277
>
187278
{children}
188279
</VirtuosoMockContext.Provider>
189280
),
190281
}
191282
);
283+
if (!error && !summaryError)
284+
await waitForElementToBeRemoved(() =>
285+
screen.queryAllByText('Suspense placeholder')
286+
);
192287

193-
return { user, ...renderResult };
288+
return { user, getRequestResolver, getRequestRejector, ...renderResult };
194289
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { renderHook } from '@/test-utils/rtl';
2+
3+
import { completedActivityTaskEvents } from '../../__fixtures__/workflow-history-activity-events';
4+
import { completedDecisionTaskEvents } from '../../__fixtures__/workflow-history-decision-events';
5+
import useInitialSelectedEvent from '../use-initial-selected-event';
6+
7+
jest.mock('../../helpers/get-history-event-group-id');
8+
9+
describe('useInitialSelectedEvent', () => {
10+
const events = [...completedDecisionTaskEvents];
11+
const filteredEventGroupsEntries: [string, any][] = [
12+
['group1', completedDecisionTaskEvents],
13+
];
14+
15+
it('should return shouldSearchForInitialEvent as true when initialEventId is defined', () => {
16+
const { result } = renderHook(() =>
17+
useInitialSelectedEvent({
18+
selectedEventId: '2',
19+
events,
20+
filteredEventGroupsEntries,
21+
})
22+
);
23+
24+
expect(result.current.shouldSearchForInitialEvent).toBe(true);
25+
});
26+
27+
it('should return shouldSearchForInitialEvent as false when initialEventId is undefined', () => {
28+
const { result } = renderHook(() =>
29+
useInitialSelectedEvent({
30+
selectedEventId: undefined,
31+
events,
32+
filteredEventGroupsEntries,
33+
})
34+
);
35+
36+
expect(result.current.shouldSearchForInitialEvent).toBe(false);
37+
});
38+
39+
it('should return initialEventGroupIndex as undefined when initialEventId is defined & group is not found', () => {
40+
const { result } = renderHook(() =>
41+
useInitialSelectedEvent({
42+
selectedEventId: '500',
43+
events,
44+
filteredEventGroupsEntries: [],
45+
})
46+
);
47+
48+
expect(result.current.initialEventGroupIndex).toBe(undefined);
49+
});
50+
51+
it('should return initialEventFound as false when initialEventId is defined & event is not found', () => {
52+
const { result } = renderHook(() =>
53+
useInitialSelectedEvent({
54+
selectedEventId: '500',
55+
events,
56+
filteredEventGroupsEntries,
57+
})
58+
);
59+
60+
expect(result.current.initialEventFound).toBe(false);
61+
});
62+
});

0 commit comments

Comments
 (0)