1import { fs } from 'memfs'; 2import path from 'path'; 3 4import * as ProjectDevices from '../devices'; 5 6describe('devices info', () => { 7 let projectRoot: string; 8 9 beforeAll(() => { 10 projectRoot = path.join('/', 'tmp', 'xdl-project-settings'); 11 }); 12 13 afterEach(async () => { 14 await ProjectDevices.setDevicesInfoAsync(projectRoot, { devices: [] }); 15 }); 16 17 afterAll(() => { 18 if (projectRoot) { 19 fs.rmdirSync(projectRoot); 20 } 21 }); 22 23 it('should persist device info to disk', async () => { 24 await ProjectDevices.saveDevicesAsync(projectRoot, 'test-device-id'); 25 26 const file = path.join(projectRoot, '.expo', 'devices.json'); 27 expect(fs.existsSync(file)).toBe(true); 28 29 const { devices } = JSON.parse(fs.readFileSync(file, 'utf8').toString()); 30 expect(devices.length).toBe(1); 31 expect(devices[0].installationId).toBe('test-device-id'); 32 }); 33 34 it('should save an array of devices', async () => { 35 await ProjectDevices.saveDevicesAsync(projectRoot, ['device-id-1', 'device-id-2']); 36 const { devices } = await ProjectDevices.getDevicesInfoAsync(projectRoot); 37 expect(devices.length).toBe(2); 38 expect(devices.some((device) => device.installationId === 'device-id-1')).toBe(true); 39 expect(devices.some((device) => device.installationId === 'device-id-2')).toBe(true); 40 }); 41 42 it('should save at most 10 devices', async () => { 43 const deviceIds = []; 44 for (let i = 0; i < 11; i++) { 45 deviceIds.push(`device-id-${i}`); 46 } 47 await ProjectDevices.saveDevicesAsync(projectRoot, deviceIds); 48 const { devices } = await ProjectDevices.getDevicesInfoAsync(projectRoot); 49 expect(devices.length).toBe(10); 50 }); 51 52 it('should remove older devices if the total number exceeds 10', async () => { 53 const currentTime = new Date().getTime(); 54 const earlierTime = currentTime - 10; 55 const earliestTime = currentTime - 20; 56 57 const devicesInfo = [{ installationId: 'oldest-device', lastUsed: earliestTime }]; 58 for (let i = 0; i < 9; i++) { 59 devicesInfo.push({ installationId: `device-id-${i}`, lastUsed: earlierTime }); 60 } 61 await ProjectDevices.setDevicesInfoAsync(projectRoot, { 62 devices: devicesInfo, 63 }); 64 65 await ProjectDevices.saveDevicesAsync(projectRoot, 'newest-device'); 66 const { devices } = await ProjectDevices.getDevicesInfoAsync(projectRoot); 67 expect(devices.length).toBe(10); 68 expect(devices[0].installationId).toBe('newest-device'); 69 expect(devices.some((device) => device.installationId === 'oldest-device')).toBe(false); 70 }); 71 72 it('should remove any devices last used before 30 days ago', async () => { 73 const currentTime = new Date().getTime(); 74 const time30DaysAnd1SecondAgo = currentTime - 30 * 24 * 60 * 60 * 1000 - 1000; 75 await ProjectDevices.setDevicesInfoAsync(projectRoot, { 76 devices: [{ installationId: 'very-old-device-id', lastUsed: time30DaysAnd1SecondAgo }], 77 }); 78 await ProjectDevices.saveDevicesAsync(projectRoot, 'new-device-id'); 79 const { devices } = await ProjectDevices.getDevicesInfoAsync(projectRoot); 80 expect(devices.length).toBe(1); 81 expect(devices[0].installationId).toBe('new-device-id'); 82 }); 83 84 it('should remove old devices when reading for the first time from disk', async () => { 85 const currentTime = new Date().getTime(); 86 const time30DaysAnd1SecondAgo = currentTime - 30 * 24 * 60 * 60 * 1000 - 1000; 87 await ProjectDevices.setDevicesInfoAsync(projectRoot, { 88 devices: [ 89 { installationId: 'very-old-device-id', lastUsed: time30DaysAnd1SecondAgo }, 90 { installationId: 'new-device-id', lastUsed: currentTime }, 91 ], 92 }); 93 // use readDeviceInfoAsync to bypass memoized devices and read from disk 94 const { devices } = await ProjectDevices.readDevicesInfoAsync(projectRoot); 95 expect(devices.length).toBe(1); 96 expect(devices[0].installationId).toBe('new-device-id'); 97 }); 98}); 99