import { Platform } from '@unimodules/core'; const mockFontMetadata = { hash: 'cafecafecafecafecafecafecafecafe', name: 'test', type: 'ttf', scales: [1], httpServerLocation: '/assets', }; describe('selectAssetSource', () => { beforeEach(() => { _mockConstants({ experienceUrl: 'https://example.com/app/expo-manifest.json', manifest: { assetMapOverride: { d00dd00dd00dd00dd00dd00dd00dd00d: { name: 'overridden', type: 'mp4' }, }, }, }); }); afterEach(() => { jest.resetModules(); }); it(`returns a production CDN URI using the asset file hash`, () => { const AssetSources = require('../AssetSources'); let source = AssetSources.selectAssetSource(mockFontMetadata); expect(source.uri).toBe( 'https://d1wp6m56sqw74a.cloudfront.net/~assets/cafecafecafecafecafecafecafecafe' ); expect(source.hash).toBe('cafecafecafecafecafecafecafecafe'); }); it(`returns a URI based on the bundle's URL in development`, () => { _mockConstants({ manifest: { developer: {}, bundleUrl: 'https://exp.direct:19001/src/App.js', }, }); const AssetSources = require('../AssetSources'); let source = AssetSources.selectAssetSource(mockFontMetadata); expect(source.uri).toBe( `https://exp.direct:19001/assets/test.ttf?platform=${Platform.OS}&hash=cafecafecafecafecafecafecafecafe` ); expect(source.hash).toBe('cafecafecafecafecafecafecafecafe'); }); it(`returns a file URI from the asset metadata if specified`, () => { const AssetSources = require('../AssetSources'); let source = AssetSources.selectAssetSource({ ...mockFontMetadata, fileUris: ['https://example.com/example.ttf'], }); expect(source.uri).toBe('https://example.com/example.ttf'); expect(source.hash).toBe('cafecafecafecafecafecafecafecafe'); }); it(`returns a URI based on an absolute server location if specified`, () => { const AssetSources = require('../AssetSources'); let source = AssetSources.selectAssetSource({ ...mockFontMetadata, httpServerLocation: 'https://example.com', }); expect(source.uri).toBe( `https://example.com/test.ttf?platform=${Platform.OS}&hash=cafecafecafecafecafecafecafecafe` ); expect(source.hash).toBe('cafecafecafecafecafecafecafecafe'); }); it(`chooses the image with the best scale`, () => { const AssetSources = require('../AssetSources'); // The mocked pixel ratio is from a retina device or higher let source = AssetSources.selectAssetSource({ hash: 'cafecafecafecafecafecafecafecafe', name: 'test', type: 'png', scales: [1, 2, 100], fileHashes: [ 'facefacefacefacefacefacefaceface', 'c0dec0dec0dec0dec0dec0dec0dec0de', 'babebabebabebabebabebabebabebabe', ], httpServerLocation: '/assets', }); expect(source.uri).toBe( 'https://d1wp6m56sqw74a.cloudfront.net/~assets/c0dec0dec0dec0dec0dec0dec0dec0de' ); expect(source.hash).toBe('c0dec0dec0dec0dec0dec0dec0dec0de'); }); it(`applies overrides if an asset's hash matches`, () => { const AssetSources = require('../AssetSources'); let source = AssetSources.selectAssetSource({ hash: 'd00dd00dd00dd00dd00dd00dd00dd00d', name: 'test', type: 'ttf', scales: [1], httpServerLocation: 'https://example.com', }); expect(source.uri).toBe( `https://example.com/overridden.mp4?platform=${Platform.OS}&hash=d00dd00dd00dd00dd00dd00dd00dd00d` ); expect(source.hash).toBe('d00dd00dd00dd00dd00dd00dd00dd00d'); }); }); describe('resolveUri', () => { afterEach(() => { jest.resetModules(); }); it(`returns URLs as-is when there is no base URL`, () => { _mockConstants({}); const Constants = require('expo-constants'); const AssetSources = require('../AssetSources'); let url = AssetSources.resolveUri('./icon.png'); expect(Constants.experienceUrl).not.toBeDefined(); expect(url).toBe('./icon.png'); }); it(`returns absolute URLs as-is`, () => { _mockConstants({ experienceUrl: 'https://example.com/app/expo-manifest.json' }); const AssetSources = require('../AssetSources'); let url = AssetSources.resolveUri('https://example.com/icon.png?q=1#hash'); expect(url).toBe('https://example.com/icon.png?q=1#hash'); }); it(`resolves URLs relative to the manifest's base URL`, () => { _mockConstants({ experienceUrl: 'https://expo.io/@user/app/index.exp' }); const AssetSources = require('../AssetSources'); let url = AssetSources.resolveUri('./icon.png'); expect(url).toBe('https://expo.io/@user/app/icon.png'); }); it(`resolves . and .. in URLs`, () => { _mockConstants({ experienceUrl: 'https://example.com/app/expo-manifest.json' }); const AssetSources = require('../AssetSources'); let url = AssetSources.resolveUri('.././test/../icon.png'); expect(url).toBe('https://example.com/icon.png'); }); it(`is resilient against extra .. in URLs`, () => { _mockConstants({ experienceUrl: 'https://example.com/app/expo-manifest.json' }); const AssetSources = require('../AssetSources'); let url = AssetSources.resolveUri('../../../../../icon.png'); expect(url).toBe('https://example.com/icon.png'); }); it(`resolves URLs starting with / relative to the domain root`, () => { _mockConstants({ experienceUrl: 'https://example.com/app/expo-manifest.json' }); const AssetSources = require('../AssetSources'); let url = AssetSources.resolveUri('/icon.png'); expect(url).toBe('https://example.com/icon.png'); }); it(`preserves query parameters and fragments`, () => { _mockConstants({ experienceUrl: 'https://example.com/app/expo-manifest.json' }); const AssetSources = require('../AssetSources'); let url = AssetSources.resolveUri('./icon.png?q=1#hash'); expect(url).toBe('https://example.com/app/icon.png?q=1#hash'); }); }); function _mockConstants(constants: { [key: string]: any }): void { jest.doMock('expo-constants', () => { const Constants = require.requireActual('expo-constants'); return { ...Constants, ...constants, manifest: { ...Constants.manifest, ...constants.manifest }, }; }); }