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 },
};
});
}