1import { vol } from 'memfs';
2
3import { installAsync } from '../../install/installAsync';
4import { copyAsync } from '../../utils/dir';
5import { queryAndGenerateAsync, selectAndGenerateAsync } from '../generate';
6import { selectTemplatesAsync } from '../templates';
7
8const asMock = <T extends (...args: any[]) => any>(fn: T): jest.MockedFunction<T> =>
9  fn as jest.MockedFunction<T>;
10
11jest.mock('../../log');
12jest.mock('../templates', () => {
13  const templates = jest.requireActual('../templates');
14  return {
15    ...templates,
16    selectTemplatesAsync: jest.fn(),
17  };
18});
19jest.mock('../../install/installAsync', () => ({ installAsync: jest.fn() }));
20jest.mock('../../utils/dir', () => ({ copyAsync: jest.fn() }));
21
22describe(queryAndGenerateAsync, () => {
23  it(`asserts an invalid file`, async () => {
24    await expect(
25      queryAndGenerateAsync('/', {
26        files: ['file1', 'file2'],
27        props: {
28          webStaticPath: 'web',
29        },
30        extras: [],
31      })
32    ).rejects.toThrowErrorMatchingInlineSnapshot(
33      `"Invalid files: file1, file2. Allowed: babel.config.js, webpack.config.js, metro.config.js, web/serve.json, web/index.html, tsconfig.json"`
34    );
35  });
36  it(`does nothing`, async () => {
37    await queryAndGenerateAsync('/', {
38      files: [],
39      props: {
40        webStaticPath: 'web',
41      },
42      extras: ['foobar'],
43    });
44    expect(copyAsync).not.toBeCalled();
45    expect(installAsync).not.toBeCalled();
46  });
47  it(`queries a file, generates, and installs`, async () => {
48    await queryAndGenerateAsync('/', {
49      files: ['babel.config.js'],
50      props: {
51        webStaticPath: 'web',
52      },
53      extras: ['foobar'],
54    });
55    expect(copyAsync).toBeCalledWith(
56      expect.stringMatching(/@expo\/cli\/static\/template\/babel\.config\.js/),
57      '/babel.config.js',
58      { overwrite: true, recursive: true }
59    );
60    expect(installAsync).toBeCalledWith(['babel-preset-expo'], {}, ['--dev', 'foobar']);
61  });
62});
63
64describe(selectAndGenerateAsync, () => {
65  afterEach(() => {
66    vol.reset();
67  });
68
69  it(`exits when no items are selected`, async () => {
70    asMock(selectTemplatesAsync).mockResolvedValue([]);
71
72    await expect(
73      selectAndGenerateAsync('/', {
74        props: {
75          webStaticPath: 'web',
76        },
77        extras: [],
78      })
79    ).rejects.toThrow(/EXIT/);
80
81    expect(copyAsync).not.toBeCalled();
82    expect(installAsync).not.toBeCalled();
83  });
84
85  it(`selects a file, generates, and installs`, async () => {
86    vol.fromJSON(
87      {
88        'node_modules/@expo/webpack-config/template/webpack.config.js': '',
89      },
90      '/'
91    );
92
93    asMock(selectTemplatesAsync).mockResolvedValue([1]);
94
95    await selectAndGenerateAsync('/', {
96      props: {
97        webStaticPath: 'web',
98      },
99      extras: [],
100    });
101
102    expect(copyAsync).toBeCalledWith(
103      expect.stringMatching(/@expo\/webpack-config\/template\/webpack\.config\.js/),
104      '/webpack.config.js',
105      { overwrite: true, recursive: true }
106    );
107    expect(installAsync).not.toBeCalled();
108  });
109
110  it(`selects a file from installed, and generates`, async () => {
111    vol.fromJSON({}, '/');
112
113    asMock(selectTemplatesAsync).mockResolvedValue([1]);
114
115    await selectAndGenerateAsync('/', {
116      props: {
117        webStaticPath: 'web',
118      },
119      extras: [],
120    });
121
122    // NOTE(EvanBacon): This logic makes no sense anymore.
123    // Why install a package after not using the versioned template?
124    // This isn't high priority since the file never changes and we should drop Webpack.
125    expect(copyAsync).toBeCalledWith(
126      expect.stringMatching(/@expo\/cli\/static\/template\/webpack\.config\.js/),
127      '/webpack.config.js',
128      { overwrite: true, recursive: true }
129    );
130    expect(installAsync).toBeCalledWith(['@expo/webpack-config'], {}, ['--dev']);
131  });
132});
133