1import { jest } from '@jest/globals';
2import { act, renderHook } from '@testing-library/react-hooks';
3import mockRouter from 'next-router-mock';
4import { PropsWithChildren } from 'react';
5
6import {
7  PageApiVersionProvider,
8  usePageApiVersion,
9  getVersionFromPath,
10  isVersionedPath,
11  replaceVersionInPath,
12} from './page-api-version';
13
14jest.mock('next/router', () => mockRouter);
15
16function renderContext(router = mockRouter) {
17  return renderHook(usePageApiVersion, {
18    wrapper: (props: PropsWithChildren<object>) => (
19      <PageApiVersionProvider router={router}>{props.children}</PageApiVersionProvider>
20    ),
21  });
22}
23
24describe('PageApiVersionContext', () => {
25  it('defaults to latest version', () => {
26    const { result } = renderHook(usePageApiVersion);
27    expect(result.current).toMatchObject({ version: 'latest', hasVersion: false });
28  });
29
30  it('defaults to setVersion throwing error', () => {
31    const { result } = renderHook(usePageApiVersion);
32    expect(() => result.current.setVersion('v44.0.0')).toThrowError(
33      'PageApiVersionContext not found'
34    );
35  });
36});
37
38describe(PageApiVersionProvider, () => {
39  it('uses sdk version from pathname', () => {
40    mockRouter.pathname = '/versions/v44.0.0/sdk/notifications';
41    const { result } = renderContext();
42    expect(result.current).toMatchObject({ version: 'v44.0.0', hasVersion: true });
43  });
44
45  it('uses unversioned version from pathname', () => {
46    mockRouter.pathname = '/versions/unversioned/react-native/view-props';
47    const { result } = renderContext();
48    expect(result.current).toMatchObject({ version: 'unversioned', hasVersion: true });
49  });
50
51  it('uses latest version from pathname', () => {
52    mockRouter.pathname = '/versions/latest/sdk';
53    const { result } = renderContext();
54    expect(result.current).toMatchObject({ version: 'latest', hasVersion: true });
55  });
56
57  it('updates router and version when setting version', () => {
58    mockRouter.pathname = '/versions/latest/sdk';
59    mockRouter.push = jest.fn<typeof mockRouter.push>();
60
61    const { result, rerender } = renderContext();
62    expect(result.current).toMatchObject({ version: 'latest', hasVersion: true });
63    act(() => result.current.setVersion('unversioned'));
64    rerender();
65    expect(mockRouter.push).toBeCalledWith('/versions/unversioned/sdk');
66  });
67});
68
69describe(getVersionFromPath, () => {
70  it('returns unversioned from pathname', () => {
71    expect(getVersionFromPath('/versions/unversioned')).toBe('unversioned');
72  });
73
74  it('returns latest from sdk pathname', () => {
75    expect(getVersionFromPath('/versions/latest/sdk/notifications')).toBe('latest');
76  });
77
78  it('returns v44.0.0 from react-native pathname', () => {
79    expect(getVersionFromPath('/versions/v44.0.0/react-native/view-props')).toBe('v44.0.0');
80  });
81
82  it('returns null for non-versioned pathname', () => {
83    expect(getVersionFromPath('/guides/monorepos/')).toBeNull();
84  });
85});
86
87describe(isVersionedPath, () => {
88  it('returns true for unversioned pathname', () => {
89    expect(isVersionedPath('/versions/unversioned')).toBe(true);
90  });
91
92  it('returns true for sdk pathname', () => {
93    expect(isVersionedPath('/versions/latest/sdk/notifications')).toBe(true);
94  });
95
96  it('returns true for react-native pathname', () => {
97    expect(isVersionedPath('/versions/v44.0.0/react-native/stylesheet/')).toBe(true);
98  });
99
100  it('returns false for non-versioned pathname', () => {
101    expect(isVersionedPath('/build-reference/how-tos/')).toBe(false);
102  });
103});
104
105describe(replaceVersionInPath, () => {
106  it('returns same pathname for non-versioned pathname', () => {
107    expect(replaceVersionInPath('/build-reference/how-tos/', 'latest')).toBe(
108      '/build-reference/how-tos/'
109    );
110  });
111
112  it('returns new pathname for unversioned pathname', () => {
113    expect(replaceVersionInPath('/versions/unversioned', 'latest')).toBe('/versions/latest');
114  });
115
116  it('returns new pathname for sdk pathname', () => {
117    expect(replaceVersionInPath('/versions/latest/sdk/notifications', 'v44.0.0')).toBe(
118      '/versions/v44.0.0/sdk/notifications'
119    );
120  });
121
122  it('returns new pathname for react-native pathname', () => {
123    expect(replaceVersionInPath('/versions/v44.0.0/react-native/stylesheet/', 'v43.0.0')).toBe(
124      '/versions/v43.0.0/react-native/stylesheet/'
125    );
126  });
127});
128