From 3b7ea140267e70cc51ff0e18634e851160fa67ae Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 22 Sep 2017 14:45:33 -0700 Subject: [PATCH] Added start of JS unit tests --- .../__mocks__/react-native-mapbox-gl.mock.js | 66 ++++++ __tests__/interface.test.js | 40 ++++ __tests__/utils/MapboxStyleSheet.test.js | 219 ++++++++++++++++++ 3 files changed, 325 insertions(+) create mode 100644 __tests__/__mocks__/react-native-mapbox-gl.mock.js create mode 100644 __tests__/interface.test.js create mode 100644 __tests__/utils/MapboxStyleSheet.test.js diff --git a/__tests__/__mocks__/react-native-mapbox-gl.mock.js b/__tests__/__mocks__/react-native-mapbox-gl.mock.js new file mode 100644 index 000000000..f264d6743 --- /dev/null +++ b/__tests__/__mocks__/react-native-mapbox-gl.mock.js @@ -0,0 +1,66 @@ +jest.mock('NativeModules', () => { + function keyMirror (keys) { + let obj = {}; + keys.forEach((key) => obj[key] = key); + return obj; + } + + // Mock of what the native code puts on the JS object + const MapboxGLNativeMock = { + // constants + UserTrackingModes: keyMirror([ + 'None', + 'Follow', + 'FollowWithCourse', + 'FollowWithHeading', + ]), + StyleURL: keyMirror([ + 'Street', + 'Dark', + 'Light', + 'Outdoors', + 'Satellite', + 'SatelliteStreet', + 'TrafficDay', + 'TrafficNight', + ]), + EventTypes: keyMirror([ + 'MapClick', + 'MapLongClick', + 'RegionWillChange', + 'RegionIsChanging', + 'RegionDidChange', + 'WillStartLoadingMap', + 'DidFinishLoadingMap', + 'DidFailLoadingMap', + 'WillStartRenderingFrame', + 'DidFinishRenderingFrame', + 'DidFinishRenderingFrameFully', + 'DidFinishLoadingStyle', + 'SetCameraComplete', + ]), + CameraModes: keyMirror([ + 'Flight', + 'Ease', + 'None', + ]), + StyleSource: keyMirror([ + 'DefaultSourceID', + ]), + InterpolationMode: keyMirror([ + 'Exponential', + 'Categorical', + 'Interval', + 'Identity', + ]), + + // methods + setAccessToken: jest.fn(), + getAccessToken: () => Promise.resolve('test-token'), + requestPermissions: () => Promise.resolve(true), + }; + + return { + MGLModule: MapboxGLNativeMock, + }; +}); diff --git a/__tests__/interface.test.js b/__tests__/interface.test.js new file mode 100644 index 000000000..72fb47e01 --- /dev/null +++ b/__tests__/interface.test.js @@ -0,0 +1,40 @@ +import MapboxGL from '../javascript'; + +describe('Public Interface', () => { + it('should contain all expected components and utils', () => { + const actualKeys = Object.keys(MapboxGL); + const expectedKeys = [ + // components + 'MapView', + 'StyleSheet', + + // layers + 'FillLayer', + 'FillExtrusionLayer', + 'CircleLayer', + 'LineLayer', + 'SymbolLayer', + + // sources + 'VectorSource', + 'ShapeSource', + + // constants + 'UserTrackingModes', + 'StyleURL', + 'EventTypes', + 'CameraModes', + 'StyleSource', + 'InterpolationMode', + + // methods + 'setAccessToken', + 'getAccessToken', + 'requestPermissions', + + // utils + 'geoUtils', + ]; + actualKeys.forEach((key) => expect(expectedKeys).toContain(key)); + }); +}); diff --git a/__tests__/utils/MapboxStyleSheet.test.js b/__tests__/utils/MapboxStyleSheet.test.js new file mode 100644 index 000000000..f93605260 --- /dev/null +++ b/__tests__/utils/MapboxStyleSheet.test.js @@ -0,0 +1,219 @@ +import MapboxGL from '../../javascript'; + +describe('MapboxStyleSheet', () => { + const BLUE_INT = 4278190335; + const GREEN_INT = 4278222848; + + it('should create constant item', () => { + verifyStyleSheetsMatch({ fillOpacity: 0.84 }, { + __MAPBOX_STYLESHEET__: true, + fillOpacity: { + type: 'constant', + payload: { value: 0.84 }, + }, + }); + }); + + it('should create image item', () => { + verifyStyleSheetsMatch({ fillPattern: 'test' }, { + __MAPBOX_STYLESHEET__: true, + fillPattern: { + type: 'constant', + payload: { value: 'test', image: true }, + }, + }); + }); + + it('should create translate item', () => { + verifyStyleSheetsMatch({ fillTranslation: { x: 1, y: 2 } }, { + __MAPBOX_STYLESHEET__: true, + fillTranslation: { + type: 'translation', + payload: { value: [1, 2] }, + }, + }); + }); + + it('should create camera function', () => { + const stops = { 1: 'blue', 2: 'green' }; + const styleFunction = MapboxGL.StyleSheet.camera(stops, 'mode'); + + verifyStyleSheetsMatch({ fillColor: styleFunction }, { + fillColor: { + type: 'function', + payload: { + fn: 'camera', + stops: { + 1: { type: 'color', payload: { value: BLUE_INT } }, + 2: { type: 'color', payload: { value: GREEN_INT } }, + }, + mode: 'mode', + }, + }, + __MAPBOX_STYLESHEET__: true, + }); + }); + + it('should create composite function', () => { + const stops = { + 1: [0, 'blue'], + 16: [3, 'green'], + 20: [5, 'blue'], + }; + const styleFunction = MapboxGL.StyleSheet.composite(stops, 'rating', 'mode'); + + verifyStyleSheetsMatch({ fillColor: styleFunction }, { + fillColor: { + type: 'function', + payload: { + fn: 'composite', + mode: 'mode', + attributeName: 'rating', + stops: { + 1: { type: 'color', payload: { value: BLUE_INT, propertyValue: 0 } }, + 16: { type: 'color', payload: { value: GREEN_INT, propertyValue: 3 } }, + 20: { type: 'color', payload: { value: BLUE_INT, propertyValue: 5 } }, + }, + }, // payload + }, // fillPattern + __MAPBOX_STYLESHEET__: true, + }); + + }); + + it('should create source function', () => { + const stops = { bergan: 'blue', hudson: 'green' }; + const styleFunction = MapboxGL.StyleSheet.source(stops, 'county', 'mode'); + + verifyStyleSheetsMatch({ fillColor: styleFunction }, { + fillColor: { + type: 'function', + payload: { + fn: 'source', + mode: 'mode', + attributeName: 'county', + stops: { + bergan: { type: 'color', payload: { value: BLUE_INT } }, + hudson: { type: 'color', payload: { value: GREEN_INT } }, + }, + }, // payload + }, // fillColor + __MAPBOX_STYLESHEET__: true, + }); + }); + + it('should create stylesheet with multiple constant style fields', () => { + const styles = { + fillOpacity: 0.84, + fillPattern: 'test', + }; + + verifyStyleSheetsMatch(styles, { + __MAPBOX_STYLESHEET__: true, + fillOpacity: { type: 'constant', payload: { value: 0.84 } }, + fillPattern: { type: 'constant', payload: { value: 'test', image: true } }, + }); + }); + + it('should create stylesheet with a mix of stlye functions and constants', () => { + const styles = { + fillColor: MapboxGL.StyleSheet.source({ bergan: 'blue' }, 'county', 'mode'), + fillOpacity: 0.84, + }; + + verifyStyleSheetsMatch(styles, { + __MAPBOX_STYLESHEET__: true, + fillColor: { + type: 'function', + payload: { + fn: 'source', + mode: 'mode', + attributeName: 'county', + stops: { + bergan: { type: 'color', payload: { value: BLUE_INT } }, + }, + }, + }, + fillOpacity: { type: 'constant', payload: { value: 0.84 } }, + }); + + }); + + it('should create config nested one level deep', () => { + const styles = { + water: { + fillColor: 'green', + fillOpacity: 0.40, + }, + building: { + fillColor: 'blue', + fillOpacity: 0.84, + }, + }; + + verifyStyleSheetsMatch(styles, { + __MAPBOX_STYLESHEET__: true, + water: { + fillColor: { type: 'color', payload: { value: GREEN_INT } }, + fillOpacity: { type: 'constant', payload: { value: 0.40 } }, + }, + building: { + fillColor: { type: 'color', payload: { value: BLUE_INT } }, + fillOpacity: { type: 'constant', payload: { value: 0.84 } }, + }, + }); + }); + + it('should not recreate a stylesheet if it is already a stylesheet', () => { + const styleSheet = MapboxGL.StyleSheet.create({ fillOpacity: 0.84 }); + expect(styleSheet).toEqual(MapboxGL.StyleSheet.create(styleSheet)); + }); + + it('should throw error for invalid property', () => { + expect(() => { + MapboxGL.StyleSheet.create({ fakeProperty: 0.84 }); + }).toThrow(); + + expect(() => { + MapboxGL.StyleSheet.create({ fakeProperty: { idk: "?" } }); + }).toThrow(); + + expect(() => { + MapboxGL.StyleSheet.create({ building: { fakeProperty: 0.84 } }); + }).toThrow(); + }); + + it('should throw error for undefined or null style values', () => { + expect(() => { + MapboxGL.StyleSheet.create({ fillOpacity: undefined }); + }).toThrow(); + + expect(() => { + MapboxGL.StyleSheet.create({ fillOpacity: null }); + }).toThrow(); + }); + + it('should throw error for passing in undefined or null', () => { + expect(() => MapboxGL.StyleSheet.create()).toThrow(); + expect(() => MapboxGL.StyleSheet.create(null)).toThrow(); + }); + + it('should create an identity source function', () => { + expect(MapboxGL.StyleSheet.create({ fillExtrusionHeight: MapboxGL.StyleSheet.identity('height') } )).toEqual({ + __MAPBOX_STYLESHEET__: true, + fillExtrusionHeight: { + type: 'function', + payload: { + fn: 'source', + stops: {}, + attributeName: 'height', + mode: MapboxGL.InterpolationMode.Identity, + }, + }, + }); + }); +}); + +function verifyStyleSheetsMatch (styles, expectedStyleSheet) { + expect(MapboxGL.StyleSheet.create(styles)).toEqual(expectedStyleSheet); +}