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