18d307f52SEvan Baconimport { fs } from 'memfs'; 28d307f52SEvan Baconimport path from 'path'; 38d307f52SEvan Bacon 48d307f52SEvan Baconimport * as ProjectDevices from '../devices'; 58d307f52SEvan Bacon 68d307f52SEvan Bacondescribe('devices info', () => { 78d307f52SEvan Bacon let projectRoot: string; 88d307f52SEvan Bacon 98d307f52SEvan Bacon beforeAll(() => { 108d307f52SEvan Bacon projectRoot = path.join('/', 'tmp', 'xdl-project-settings'); 118d307f52SEvan Bacon }); 128d307f52SEvan Bacon 138d307f52SEvan Bacon afterEach(async () => { 148d307f52SEvan Bacon await ProjectDevices.setDevicesInfoAsync(projectRoot, { devices: [] }); 158d307f52SEvan Bacon }); 168d307f52SEvan Bacon 178d307f52SEvan Bacon afterAll(() => { 188d307f52SEvan Bacon if (projectRoot) { 19*e1bb5bdfSKudo Chien fs.rmdirSync(projectRoot, { recursive: true }); 208d307f52SEvan Bacon } 218d307f52SEvan Bacon }); 228d307f52SEvan Bacon 238d307f52SEvan Bacon it('should persist device info to disk', async () => { 248d307f52SEvan Bacon await ProjectDevices.saveDevicesAsync(projectRoot, 'test-device-id'); 258d307f52SEvan Bacon 268d307f52SEvan Bacon const file = path.join(projectRoot, '.expo', 'devices.json'); 278d307f52SEvan Bacon expect(fs.existsSync(file)).toBe(true); 288d307f52SEvan Bacon 298d307f52SEvan Bacon const { devices } = JSON.parse(fs.readFileSync(file, 'utf8').toString()); 308d307f52SEvan Bacon expect(devices.length).toBe(1); 318d307f52SEvan Bacon expect(devices[0].installationId).toBe('test-device-id'); 328d307f52SEvan Bacon }); 338d307f52SEvan Bacon 348d307f52SEvan Bacon it('should save an array of devices', async () => { 358d307f52SEvan Bacon await ProjectDevices.saveDevicesAsync(projectRoot, ['device-id-1', 'device-id-2']); 368d307f52SEvan Bacon const { devices } = await ProjectDevices.getDevicesInfoAsync(projectRoot); 378d307f52SEvan Bacon expect(devices.length).toBe(2); 388d307f52SEvan Bacon expect(devices.some((device) => device.installationId === 'device-id-1')).toBe(true); 398d307f52SEvan Bacon expect(devices.some((device) => device.installationId === 'device-id-2')).toBe(true); 408d307f52SEvan Bacon }); 418d307f52SEvan Bacon 428d307f52SEvan Bacon it('should save at most 10 devices', async () => { 438d307f52SEvan Bacon const deviceIds = []; 448d307f52SEvan Bacon for (let i = 0; i < 11; i++) { 458d307f52SEvan Bacon deviceIds.push(`device-id-${i}`); 468d307f52SEvan Bacon } 478d307f52SEvan Bacon await ProjectDevices.saveDevicesAsync(projectRoot, deviceIds); 488d307f52SEvan Bacon const { devices } = await ProjectDevices.getDevicesInfoAsync(projectRoot); 498d307f52SEvan Bacon expect(devices.length).toBe(10); 508d307f52SEvan Bacon }); 518d307f52SEvan Bacon 528d307f52SEvan Bacon it('should remove older devices if the total number exceeds 10', async () => { 538d307f52SEvan Bacon const currentTime = new Date().getTime(); 548d307f52SEvan Bacon const earlierTime = currentTime - 10; 558d307f52SEvan Bacon const earliestTime = currentTime - 20; 568d307f52SEvan Bacon 578d307f52SEvan Bacon const devicesInfo = [{ installationId: 'oldest-device', lastUsed: earliestTime }]; 588d307f52SEvan Bacon for (let i = 0; i < 9; i++) { 598d307f52SEvan Bacon devicesInfo.push({ installationId: `device-id-${i}`, lastUsed: earlierTime }); 608d307f52SEvan Bacon } 618d307f52SEvan Bacon await ProjectDevices.setDevicesInfoAsync(projectRoot, { 628d307f52SEvan Bacon devices: devicesInfo, 638d307f52SEvan Bacon }); 648d307f52SEvan Bacon 658d307f52SEvan Bacon await ProjectDevices.saveDevicesAsync(projectRoot, 'newest-device'); 668d307f52SEvan Bacon const { devices } = await ProjectDevices.getDevicesInfoAsync(projectRoot); 678d307f52SEvan Bacon expect(devices.length).toBe(10); 688d307f52SEvan Bacon expect(devices[0].installationId).toBe('newest-device'); 698d307f52SEvan Bacon expect(devices.some((device) => device.installationId === 'oldest-device')).toBe(false); 708d307f52SEvan Bacon }); 718d307f52SEvan Bacon 728d307f52SEvan Bacon it('should remove any devices last used before 30 days ago', async () => { 738d307f52SEvan Bacon const currentTime = new Date().getTime(); 748d307f52SEvan Bacon const time30DaysAnd1SecondAgo = currentTime - 30 * 24 * 60 * 60 * 1000 - 1000; 758d307f52SEvan Bacon await ProjectDevices.setDevicesInfoAsync(projectRoot, { 768d307f52SEvan Bacon devices: [{ installationId: 'very-old-device-id', lastUsed: time30DaysAnd1SecondAgo }], 778d307f52SEvan Bacon }); 788d307f52SEvan Bacon await ProjectDevices.saveDevicesAsync(projectRoot, 'new-device-id'); 798d307f52SEvan Bacon const { devices } = await ProjectDevices.getDevicesInfoAsync(projectRoot); 808d307f52SEvan Bacon expect(devices.length).toBe(1); 818d307f52SEvan Bacon expect(devices[0].installationId).toBe('new-device-id'); 828d307f52SEvan Bacon }); 838d307f52SEvan Bacon 848d307f52SEvan Bacon it('should remove old devices when reading for the first time from disk', async () => { 858d307f52SEvan Bacon const currentTime = new Date().getTime(); 868d307f52SEvan Bacon const time30DaysAnd1SecondAgo = currentTime - 30 * 24 * 60 * 60 * 1000 - 1000; 878d307f52SEvan Bacon await ProjectDevices.setDevicesInfoAsync(projectRoot, { 888d307f52SEvan Bacon devices: [ 898d307f52SEvan Bacon { installationId: 'very-old-device-id', lastUsed: time30DaysAnd1SecondAgo }, 908d307f52SEvan Bacon { installationId: 'new-device-id', lastUsed: currentTime }, 918d307f52SEvan Bacon ], 928d307f52SEvan Bacon }); 938d307f52SEvan Bacon // use readDeviceInfoAsync to bypass memoized devices and read from disk 948d307f52SEvan Bacon const { devices } = await ProjectDevices.readDevicesInfoAsync(projectRoot); 958d307f52SEvan Bacon expect(devices.length).toBe(1); 968d307f52SEvan Bacon expect(devices[0].installationId).toBe('new-device-id'); 978d307f52SEvan Bacon }); 988d307f52SEvan Bacon}); 99