Update selected item color in Pass menu
[ProtonMail-WebClient.git] / packages / pass / lib / api / images.spec.ts
blob473230f62df35c90c3595a740657c709974f06f9
1 import type { Api } from '@proton/pass/types';
2 import { ApiError } from '@proton/shared/lib/fetch/ApiError';
4 import * as cache from './cache';
5 import { createAbortResponse, createNetworkError } from './fetch-controller';
6 import { createImageProxyHandler } from './images';
8 const { withMaxAgeHeaders } = cache;
10 describe('createImageProxyHandler', () => {
11     const mockUrl = '/test/image';
12     const mockApi = jest.fn();
13     const mockCache = { match: jest.fn(), put: jest.fn().mockImplementation(() => Promise.resolve()) };
14     const imageProxy = createImageProxyHandler(mockApi as unknown as Api);
15     const apiParams = { url: mockUrl, output: 'raw', sideEffects: false };
17     beforeEach(() => {
18         jest.spyOn(cache, 'getCache').mockImplementation(() => Promise.resolve(mockCache as unknown as Cache));
19         mockApi.mockClear();
20         mockCache.match.mockClear();
21         mockCache.put.mockClear();
22     });
24     it('should return cached response if available and not expired', async () => {
25         const response = new Response('cached', { headers: withMaxAgeHeaders(new Response(), 100) });
26         mockCache.match.mockResolvedValue(response);
28         const result = await imageProxy(mockUrl);
29         await expect(result).toMatchResponse(response);
30         expect(mockApi).not.toHaveBeenCalled();
31     });
33     it('should fetch from network if cache is empty', async () => {
34         const response = new Response('from_network', { status: 200 });
35         mockApi.mockResolvedValue(response);
36         mockCache.match.mockResolvedValue(undefined);
38         const result = await imageProxy(mockUrl);
39         const [url, cached] = mockCache.put.mock.lastCall!;
41         expect(mockApi).toHaveBeenCalledWith(expect.objectContaining(apiParams));
42         expect(mockCache.put).toHaveBeenCalled();
43         expect(result.status).toBe(200);
44         expect(url).toEqual(mockUrl);
45         await expect(cached).toMatchResponse(result);
46     });
48     it('should handle AbortError', async () => {
49         mockCache.match.mockResolvedValue(undefined);
50         mockApi.mockRejectedValue({ name: 'AbortError' });
52         const result = await imageProxy(mockUrl);
54         expect(mockApi).toHaveBeenCalledWith(expect.objectContaining(apiParams));
55         expect(mockCache.put).not.toHaveBeenCalled();
56         await expect(result).toMatchResponse(createAbortResponse());
57     });
59     it('should handle ApiError with 422 status', async () => {
60         mockCache.match.mockResolvedValue(undefined);
61         mockApi.mockRejectedValue(new ApiError('Unprocessable Content', 422, 'StatusCodeError'));
63         const result = await imageProxy(mockUrl);
64         const [url, cached] = mockCache.put.mock.lastCall!;
66         expect(mockApi).toHaveBeenCalledWith(expect.objectContaining(apiParams));
67         expect(result.status).toEqual(422);
68         expect(result.headers.get('Cache-Control')).toBeDefined();
69         expect(url).toEqual(mockUrl);
70         await expect(cached).toMatchResponse(result);
71     });
73     it('should handle network error', async () => {
74         mockCache.match.mockResolvedValue(undefined);
75         mockApi.mockRejectedValue(new Error('Network error'));
77         const result = await imageProxy(mockUrl);
79         expect(mockApi).toHaveBeenCalledWith(expect.objectContaining(apiParams));
80         expect(mockCache.put).not.toHaveBeenCalled();
81         await expect(result).toMatchResponse(createNetworkError(408));
82     });
84     it('should return cached response while revalidating', async () => {
85         const staleHeaders = new Headers();
86         staleHeaders.set('Date', 'Fri, 14 Dec 1990 09:39:46 GMT');
87         staleHeaders.set('Cache-Control', `max-age=0`);
89         const cachedResponse = new Response('cached', { headers: staleHeaders });
90         const response = new Response('from_network', { status: 200 });
92         mockCache.match.mockResolvedValue(cachedResponse);
93         mockApi.mockResolvedValue(response);
95         const result = await imageProxy(mockUrl);
96         await new Promise(process.nextTick); // let cache side-effect happen
97         const [url, cached] = mockCache.put.mock.lastCall!;
99         expect(mockApi).toHaveBeenCalledWith(expect.objectContaining(apiParams));
100         expect(url).toEqual(mockUrl);
101         expect(mockCache.put).toHaveBeenCalled();
102         expect(cached.status).toEqual(200);
103         expect(cached.headers.get('Cache-Control')).toBeDefined();
104         await expect(result).toMatchResponse(cachedResponse);
105     });