1import { vol } from 'memfs'; 2 3import { getConfigFilePaths, modifyConfigAsync } from '../Config'; 4import { getDynamicConfig, getStaticConfig } from '../getConfig'; 5 6const mockConfigContext = {} as any; 7 8jest.mock('fs'); 9 10describe(modifyConfigAsync, () => { 11 beforeAll(async () => { 12 vol.fromJSON( 13 { 14 'package.json': JSON.stringify({}), 15 }, 16 'no-config' 17 ); 18 vol.fromJSON( 19 { 20 'package.json': JSON.stringify({}), 21 'app.config.js': 'export default {};', 22 'app.config.json': JSON.stringify({}), 23 }, 24 'dynamic-and-static' 25 ); 26 vol.fromJSON( 27 { 28 'app.config.json': JSON.stringify( 29 { 30 name: 'app-config-json', 31 slug: 'app-config-json', 32 version: '1.0.0', 33 platforms: [], 34 }, 35 null, 36 2 37 ), 38 'app.json': JSON.stringify( 39 { 40 name: 'app-json', 41 }, 42 null, 43 2 44 ), 45 'package.json': JSON.stringify({}), 46 }, 47 'static-override' 48 ); 49 }); 50 51 afterAll(() => { 52 vol.reset(); 53 }); 54 55 it(`can write to a static only config`, async () => { 56 const { type, config } = await modifyConfigAsync( 57 'static-override', 58 { foo: 'bar' } as any, 59 { skipSDKVersionRequirement: true }, 60 { dryRun: true } 61 ); 62 expect(type).toBe('success'); 63 // @ts-ignore: foo property is not defined 64 expect(config.foo).toBe('bar'); 65 }); 66 it(`cannot write to a dynamic config`, async () => { 67 const { type, config } = await modifyConfigAsync( 68 'dynamic-and-static', 69 {}, 70 { skipSDKVersionRequirement: true }, 71 { dryRun: true } 72 ); 73 expect(type).toBe('warn'); 74 expect(config).toBe(null); 75 }); 76 it(`cannot write to a project without a config`, async () => { 77 const { type, config } = await modifyConfigAsync( 78 'no-config', 79 {}, 80 { skipSDKVersionRequirement: true }, 81 { dryRun: true } 82 ); 83 expect(type).toBe('fail'); 84 expect(config).toBe(null); 85 }); 86}); 87 88const invalidConfig = `/* eslint-disable */ 89module.exports = { 90 name: 'app', 91 > 92 slug: 'app', 93}`; 94 95describe(getDynamicConfig, () => { 96 beforeAll(async () => { 97 vol.fromJSON( 98 { 99 'exports-function.app.config.js': `export default function (config) { 100 return { ...config }; 101 }`, 102 'exports-object.app.config.js': `export default {};`, 103 }, 104 'dynamic-export-types' 105 ); 106 vol.fromJSON( 107 { 108 // This file exists to test that an invalid app.config.ts 109 // gets used instead of defaulting to a valid app.config.js 110 'app.config.js': `export default {};`, 111 // This is a syntax error: 112 'app.config.ts': invalidConfig, 113 }, 114 'syntax-error' 115 ); 116 vol.fromJSON( 117 { 118 // This is a missing import 119 'app.config.ts': [`import 'foobar'`, 'module.exports = {}'].join('\n'), 120 }, 121 'missing-import-error' 122 ); 123 }); 124 125 afterAll(() => { 126 vol.reset(); 127 }); 128 129 it(`exports a function`, () => { 130 expect( 131 getDynamicConfig('dynamic-export-types/exports-function.app.config.js', mockConfigContext) 132 .exportedObjectType 133 ).toBe('function'); 134 }); 135 136 it(`exports an object`, () => { 137 expect( 138 getDynamicConfig('dynamic-export-types/exports-object.app.config.js', mockConfigContext) 139 .exportedObjectType 140 ).toBe('object'); 141 }); 142 143 // This tests error are thrown properly and ensures that a more specific 144 // config is used instead of defaulting to a valid substitution. 145 it(`throws a useful error for dynamic configs with a syntax error`, () => { 146 const paths = getConfigFilePaths('syntax-error'); 147 expect(() => getDynamicConfig(paths.dynamicConfigPath!, mockConfigContext)).toThrowError( 148 /Error .* \(5:7\)/ 149 ); 150 }); 151 // This tests error are thrown properly and ensures that a more specific 152 // config is used instead of defaulting to a valid substitution. 153 it(`throws a useful error for dynamic configs with a missing import`, () => { 154 const paths = getConfigFilePaths('missing-import-error'); 155 expect(() => getDynamicConfig(paths.dynamicConfigPath!, mockConfigContext)).toThrowError( 156 /Require stack/ 157 ); 158 }); 159}); 160 161describe(getStaticConfig, () => { 162 beforeAll(async () => { 163 vol.fromJSON( 164 { 165 'package.json': JSON.stringify({}), 166 'app.json': JSON.stringify( 167 { 168 name: 'app-json', 169 }, 170 null, 171 2 172 ), 173 'app.config.json': JSON.stringify( 174 { 175 name: 'app-config-json', 176 slug: 'app-config-json', 177 version: '1.0.0', 178 platforms: [], 179 }, 180 null, 181 2 182 ), 183 }, 184 'static-override' 185 ); 186 }); 187 188 afterAll(() => { 189 vol.reset(); 190 }); 191 192 // This tests error are thrown properly and ensures that a more specific 193 // config is used instead of defaulting to a valid substitution. 194 it(`uses app.config.json instead of app.json if both exist`, () => { 195 const paths = getConfigFilePaths('static-override'); 196 expect(paths.staticConfigPath).toMatch(/app\.config\.json/); 197 198 expect(getStaticConfig(paths.staticConfigPath!).name).toBe('app-config-json'); 199 }); 200}); 201