Skip to content

Commit a08c979

Browse files
Implement using local storage and restructure code
Signed-off-by: Adhitya Mamallan <adhitya.mamallan@uber.com>
1 parent 85fe408 commit a08c979

12 files changed

+369
-148
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
import React, { Suspense } from 'react';
2+
3+
import { renderHook, waitFor, act } from '@/test-utils/rtl';
4+
5+
import { type UseSuspenseConfigValueResult } from '@/hooks/use-config-value/use-config-value.types';
6+
import useSuspenseConfigValue from '@/hooks/use-config-value/use-suspense-config-value';
7+
import * as localStorageModule from '@/utils/local-storage';
8+
import workflowHistoryUserPreferencesConfig from '@/views/workflow-history/config/workflow-history-user-preferences.config';
9+
10+
import useIsWorkflowHistoryV2Enabled from '../use-is-workflow-history-v2-enabled';
11+
12+
jest.mock('@/hooks/use-config-value/use-suspense-config-value');
13+
jest.mock('@/utils/local-storage', () => ({
14+
getLocalStorageValue: jest.fn(),
15+
setLocalStorageValue: jest.fn(),
16+
clearLocalStorageValue: jest.fn(),
17+
}));
18+
19+
const mockUseSuspenseConfigValue =
20+
useSuspenseConfigValue as jest.MockedFunction<any>;
21+
22+
describe(useIsWorkflowHistoryV2Enabled.name, () => {
23+
afterEach(() => {
24+
jest.restoreAllMocks();
25+
});
26+
27+
it('should return true when config value is ENABLED', async () => {
28+
const { result } = setup({ configValue: 'ENABLED' });
29+
30+
await waitFor(() => {
31+
expect(result.current[0]).toBe(true);
32+
});
33+
});
34+
35+
it('should return true when config value is OPT_OUT', async () => {
36+
const { result } = setup({ configValue: 'OPT_OUT' });
37+
38+
await waitFor(() => {
39+
expect(result.current[0]).toBe(true);
40+
});
41+
});
42+
43+
it('should return false when config value is DISABLED', async () => {
44+
const { result } = setup({ configValue: 'DISABLED' });
45+
46+
await waitFor(() => {
47+
expect(result.current[0]).toBe(false);
48+
});
49+
});
50+
51+
it('should return false when config value is OPT_IN and localStorage has no value', async () => {
52+
const { result } = setup({ configValue: 'OPT_IN' });
53+
54+
await waitFor(() => {
55+
expect(result.current[0]).toBe(false);
56+
});
57+
});
58+
59+
it('should return true when config value is OPT_IN and localStorage has true', async () => {
60+
const { result } = setup({
61+
configValue: 'OPT_IN',
62+
localStorageValue: true,
63+
});
64+
65+
await waitFor(() => {
66+
expect(result.current[0]).toBe(true);
67+
});
68+
});
69+
70+
it('should return false when config value is OPT_IN and localStorage has false', async () => {
71+
const { result } = setup({
72+
configValue: 'OPT_IN',
73+
localStorageValue: false,
74+
});
75+
76+
await waitFor(() => {
77+
expect(result.current[0]).toBe(false);
78+
});
79+
});
80+
81+
it('should not allow setting value when config is DISABLED', async () => {
82+
const { result, mockSetLocalStorageValue } = setup({
83+
configValue: 'DISABLED',
84+
});
85+
86+
await waitFor(() => {
87+
expect(result.current[0]).toBe(false);
88+
});
89+
90+
act(() => {
91+
result.current[1](true);
92+
});
93+
94+
expect(result.current[0]).toBe(false);
95+
expect(mockSetLocalStorageValue).not.toHaveBeenCalled();
96+
});
97+
98+
it('should not allow setting value when config is ENABLED', async () => {
99+
const { result, mockSetLocalStorageValue } = setup({
100+
configValue: 'ENABLED',
101+
});
102+
103+
await waitFor(() => {
104+
expect(result.current[0]).toBe(true);
105+
});
106+
107+
act(() => {
108+
result.current[1](false);
109+
});
110+
111+
expect(result.current[0]).toBe(true);
112+
expect(mockSetLocalStorageValue).not.toHaveBeenCalled();
113+
});
114+
115+
it('should allow setting value when config is OPT_OUT', async () => {
116+
const { result, mockSetLocalStorageValue } = setup({
117+
configValue: 'OPT_OUT',
118+
});
119+
120+
await waitFor(() => {
121+
expect(result.current[0]).toBe(true);
122+
});
123+
124+
act(() => {
125+
result.current[1](false);
126+
});
127+
128+
expect(result.current[0]).toBe(false);
129+
expect(mockSetLocalStorageValue).not.toHaveBeenCalled();
130+
});
131+
132+
it('should allow setting value and update localStorage when config is OPT_IN', async () => {
133+
const { result, mockSetLocalStorageValue } = setup({
134+
configValue: 'OPT_IN',
135+
localStorageValue: false,
136+
});
137+
138+
await waitFor(() => {
139+
expect(result.current[0]).toBe(false);
140+
});
141+
142+
act(() => {
143+
result.current[1](true);
144+
});
145+
146+
expect(result.current[0]).toBe(true);
147+
expect(mockSetLocalStorageValue).toHaveBeenCalledWith(
148+
workflowHistoryUserPreferencesConfig.historyV2ViewEnabled.key,
149+
'true'
150+
);
151+
});
152+
153+
it('should update localStorage when setting value to false in OPT_IN mode', async () => {
154+
const { result, mockSetLocalStorageValue } = setup({
155+
configValue: 'OPT_IN',
156+
localStorageValue: true,
157+
});
158+
159+
await waitFor(() => {
160+
expect(result.current[0]).toBe(true);
161+
});
162+
163+
act(() => {
164+
result.current[1](false);
165+
});
166+
167+
expect(result.current[0]).toBe(false);
168+
expect(mockSetLocalStorageValue).toHaveBeenCalledWith(
169+
workflowHistoryUserPreferencesConfig.historyV2ViewEnabled.key,
170+
'false'
171+
);
172+
});
173+
});
174+
175+
function setup({
176+
configValue,
177+
localStorageValue,
178+
}: {
179+
configValue: 'DISABLED' | 'OPT_IN' | 'OPT_OUT' | 'ENABLED';
180+
localStorageValue?: boolean | null;
181+
}) {
182+
mockUseSuspenseConfigValue.mockReturnValue({
183+
data: configValue,
184+
} satisfies Pick<
185+
UseSuspenseConfigValueResult<'HISTORY_PAGE_V2_ENABLED'>,
186+
'data'
187+
>);
188+
189+
const mockGetLocalStorageValue = jest.fn(() => localStorageValue ?? null);
190+
const mockSetLocalStorageValue = jest.fn();
191+
192+
jest
193+
.spyOn(localStorageModule, 'getLocalStorageValue')
194+
.mockImplementation(mockGetLocalStorageValue);
195+
jest
196+
.spyOn(localStorageModule, 'setLocalStorageValue')
197+
.mockImplementation(mockSetLocalStorageValue);
198+
199+
const { result } = renderHook(
200+
() => useIsWorkflowHistoryV2Enabled(),
201+
undefined,
202+
{
203+
wrapper: ({ children }: { children: React.ReactNode }) => (
204+
<Suspense>{children}</Suspense>
205+
),
206+
}
207+
);
208+
209+
return { result, mockGetLocalStorageValue, mockSetLocalStorageValue };
210+
}

src/views/workflow-history-v2/hooks/__tests__/use-suspense-is-workflow-history-v2-enabled.test.tsx

Lines changed: 0 additions & 72 deletions
This file was deleted.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { useCallback, useState } from 'react';
2+
3+
import useSuspenseConfigValue from '@/hooks/use-config-value/use-suspense-config-value';
4+
import {
5+
getLocalStorageValue,
6+
setLocalStorageValue,
7+
} from '@/utils/local-storage';
8+
import workflowHistoryUserPreferencesConfig from '@/views/workflow-history/config/workflow-history-user-preferences.config';
9+
10+
export default function useIsWorkflowHistoryV2Enabled(): [
11+
boolean,
12+
(v: boolean) => void,
13+
] {
14+
const { data: historyPageV2Config } = useSuspenseConfigValue(
15+
'HISTORY_PAGE_V2_ENABLED'
16+
);
17+
18+
const [isEnabled, setIsEnabled] = useState(() => {
19+
switch (historyPageV2Config) {
20+
case 'DISABLED':
21+
return false;
22+
case 'ENABLED':
23+
case 'OPT_OUT':
24+
return true;
25+
case 'OPT_IN':
26+
const userPreference = getLocalStorageValue(
27+
workflowHistoryUserPreferencesConfig.historyV2ViewEnabled.key,
28+
workflowHistoryUserPreferencesConfig.historyV2ViewEnabled.schema
29+
);
30+
return userPreference ?? false;
31+
}
32+
});
33+
34+
const setIsWorkflowHistoryV2Enabled = useCallback(
35+
(v: boolean) => {
36+
if (
37+
historyPageV2Config === 'DISABLED' ||
38+
historyPageV2Config === 'ENABLED'
39+
) {
40+
return;
41+
}
42+
43+
setIsEnabled(v);
44+
45+
if (historyPageV2Config === 'OPT_IN') {
46+
setLocalStorageValue(
47+
workflowHistoryUserPreferencesConfig.historyV2ViewEnabled.key,
48+
String(v)
49+
);
50+
}
51+
},
52+
[historyPageV2Config]
53+
);
54+
55+
return [isEnabled, setIsWorkflowHistoryV2Enabled];
56+
}

src/views/workflow-history-v2/hooks/use-suspense-is-workflow-history-v2-enabled.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/views/workflow-history/config/workflow-history-user-preferences.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ const workflowHistoryUserPreferencesConfig = {
1010
.refine((val) => val === 'true' || val === 'false')
1111
.transform((val) => val === 'true'),
1212
},
13+
historyV2ViewEnabled: {
14+
key: 'history-v2-view-enabled',
15+
schema: z
16+
.string()
17+
.refine((val) => val === 'true' || val === 'false')
18+
.transform((val) => val === 'true'),
19+
},
1320
} as const satisfies Record<string, WorkflowHistoryUserPreferenceConfig<any>>;
1421

1522
export default workflowHistoryUserPreferencesConfig;

0 commit comments

Comments
 (0)