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