UNPKG

d2

Version:

Javascript library for DHIS2

689 lines (575 loc) 23 kB
import fixtures from '../../__fixtures__/fixtures' import MockApi from '../../api/Api' import System from '../System' import SystemConfiguration from '../SystemConfiguration' import SystemSettings from '../SystemSettings' jest.mock('../../api/Api') describe('System', () => { let system let apiMock beforeEach(() => { apiMock = new MockApi() system = new System(new SystemSettings(), new SystemConfiguration()) }) afterEach(() => { MockApi.mockReset() }) it('should be an instance of System', () => { expect(system).toBeInstanceOf(System) }) it('should not be allowed to be called without new', () => { expect(() => System()).toThrowErrorMatchingSnapshot() }) it('should contain an instance of SystemConfiguration', () => { expect(system.configuration).toBeInstanceOf(SystemConfiguration) }) it('should contain an instance of SystemSettings', () => { expect(system.settings).toBeInstanceOf(SystemSettings) }) describe('loadInstalledApps()', () => { let appsFromApi beforeEach(() => { appsFromApi = [ { version: '0.4.8', name: 'Data Approval', description: 'Approvals app for PEPFAR', icons: { 48: 'img/icons/dataapproval.png', }, developer: { url: 'http://www.dhis2.org', name: 'Mark Polak', company: 'DHIS2 Core Team', email: 'markpo@ifi.uio.no', }, activities: { dhis: { href: 'http://localhost:8080/dhis', }, }, folderName: 'approvals', launchUrl: 'http://localhost:8080/dhis/api/apps/approvals/index.html?v=0.4.8', key: 'approvals', launch_path: 'index.html?v=0.4.8', default_locale: 'en', }, { version: '0.0.1', name: 'Data Export Log', description: 'Data export log viewer', icons: { 48: 'icons/export.png', }, developer: { url: '', name: 'Mark Polak', }, activities: { dhis: { href: 'http://localhost:8080/dhis', }, }, folderName: 'data-export-log', launchUrl: 'http://localhost:8080/dhis/api/apps/data-export-log/index.html?1.0.0-rc1', key: 'data-export-log', launch_path: 'index.html?1.0.0-rc1', default_locale: 'en', }, ] apiMock.get = jest .fn() .mockReturnValue(Promise.resolve(appsFromApi)) }) it('should set the list of installed apps onto the Settings object', () => system.loadInstalledApps().then(() => { expect(system.installedApps).toEqual(appsFromApi) })) it('should reject the promise if the request fails', () => { apiMock.get = jest .fn() .mockReturnValue(Promise.reject('Apps can not be loaded')) return system .loadInstalledApps() .catch(error => error) .then(message => { expect(message).toBe('Apps can not be loaded') }) }) it('should resolve with the returned list of apps', () => system.loadInstalledApps().then(apps => { expect(apps).toEqual(appsFromApi) })) }) describe('uploadApp()', () => { let appendSpy let formData beforeEach(() => { jest.spyOn(apiMock, 'post').mockReturnValue(Promise.resolve()) // Fake FormData object appendSpy = jest.fn() formData = { append: appendSpy, } // Fake formData global constructor global.FormData = function FormData() { return formData } }) afterEach(() => { global.FormData = undefined }) it('should be a function on the system object', () => { expect(typeof system.uploadApp).toBe('function') }) it('should call the post with the correct options', () => { const xhrOptions = { contentType: false, processData: false, xhr: undefined, } system.uploadApp('ZipFile') expect(apiMock.post).toBeCalledWith('apps', formData, xhrOptions) }) it('should call append on the formData object to add the file to upload', () => { system.uploadApp('ZipFile') expect(formData.append).toBeCalledWith('file', 'ZipFile') }) describe('xhr', () => { let progressCallbackSpy let xhrMock beforeEach(() => { progressCallbackSpy = jest.fn() xhrMock = { upload: {}, } global.XMLHttpRequest = function XMLHttpRequest() { return xhrMock } }) afterEach(() => { global.XMLHttpRequest = undefined }) it('should pass custom XMLHttpRequest Object with an on progress callback as an option', () => { system.uploadApp('ZipFile', progressCallbackSpy) expect(typeof apiMock.post.mock.calls[0][2].xhr).toBe( 'function' ) expect(apiMock.post.mock.calls[0][2].xhr.call()).toBe(xhrMock) }) it('should define the onprogress function onto the upload object of the xhr', () => { system.uploadApp('ZipFile', progressCallbackSpy) expect(typeof xhrMock.upload.onprogress).toBe('function') }) it('should not call the callback if the progress can not be computed', () => { system.uploadApp('ZipFile', progressCallbackSpy) xhrMock.upload.onprogress({}) expect(progressCallbackSpy).not.toBeCalled() }) it('should call the callback spy if the progress can be computed', () => { system.uploadApp('ZipFile', progressCallbackSpy) xhrMock.upload.onprogress({ lengthComputable: true, loaded: 10, total: 50, }) expect(progressCallbackSpy).toBeCalledWith(0.2) }) }) }) describe('loadAppStore()', () => { // Useful constants! const sysVersionMinusTwo = '2.26' const sysVersionMinusOne = '2.27' const sysVersion = '2.28' const sysVersionPlusOne = '2.29' const sysVersionPlusTwo = '2.30' beforeEach(() => { system.setSystemInfo({ version: sysVersion, }) jest.spyOn(apiMock, 'get').mockReturnValue( Promise.resolve(fixtures.get('/appStore')) ) }) it('should be a function on the system object', () => { expect(typeof system.loadAppStore).toBe('function') }) it('should return a promise', () => { expect(system.loadAppStore()).toBeInstanceOf(Promise) }) it('should request the api for the appStore', () => { system.loadAppStore() expect(apiMock.get).toBeCalledWith('appStore') }) it('should return the compatible apps from the api', () => { // Apps 3 and 6 are compatible with 2.28 const expectedApps = fixtures .get('/appStore') .filter((app, i) => [3, 6].includes(i)) return system.loadAppStore().then(apps => { expect(apps).toEqual(expectedApps) }) }) describe('with version 2.24', () => { beforeEach(() => { system.setSystemInfo({ version: '2.24', }) }) it('should return the compatible apps from the API', () => { // Apps 2, 4 and 6 are compatible with 2.24 const expectedApps = fixtures .get('/appStore') .filter((app, i) => [2, 4, 6].includes(i)) return system.loadAppStore().then(apps => { expect(apps).toEqual(expectedApps) }) }) }) describe('with version 2.25', () => { beforeEach(() => { system.setSystemInfo({ version: '2.25', }) }) it('should return the compatible apps from the API', () => { // Apps 1, 2, 4, 5 and 6 are compatible with 2.25 const expectedApps = fixtures .get('/appStore') .filter((app, i) => [1, 2, 4, 5, 6].includes(i)) return system.loadAppStore().then(apps => { expect(apps).toEqual(expectedApps) }) }) }) it('should return the compatible apps', () => { // const returnedApps = fixtures.get('/appStore'); const returnedApps = [ { versions: [ // One version compatible { min_platform_version: sysVersionMinusTwo, max_platform_version: sysVersionMinusOne, }, { min_platform_version: sysVersionMinusOne }, // compatible ], }, { versions: [ // Both incompatible { min_platform_version: sysVersionPlusOne, max_platform_version: sysVersionPlusTwo, }, { min_platform_version: sysVersionPlusTwo }, ], }, { versions: [ // Compatible { min_platform_version: sysVersionMinusOne }, ], }, { versions: [ // Incompatible { max_platform_version: sysVersionMinusOne }, ], }, { versions: [ // One version compatible { minDhisVersion: sysVersionMinusTwo, maxDhisVersion: sysVersionMinusOne, }, { minDhisVersion: sysVersionMinusOne }, // compatible ], }, { versions: [ // Both incompatible { minDhisVersion: sysVersionPlusOne, maxDhisVersion: sysVersionPlusTwo, }, { minDhisVersion: sysVersionPlusTwo }, ], }, { versions: [ // Compatible { minDhisVersion: sysVersionMinusOne }, ], }, { versions: [ // Incompatible { maxDhisVersion: sysVersionMinusOne }, ], }, ] apiMock.get.mockReturnValue(Promise.resolve(returnedApps)) return system.loadAppStore().then(apps => { expect(apps.length).toBe(4) }) }) it('should return all the apps when compatibility flag is set to false', () => system.loadAppStore(false).then(apps => { expect(apps.length).toBe(fixtures.get('/appStore').length) })) it('should reject the promise when the request fails', () => { apiMock.get.mockReturnValue( Promise.reject('Request for appStore failed') ) return system .loadAppStore() .catch(error => error) .then(error => { expect(error).toBe('Request for appStore failed') }) }) it('should reject the promise when system.version is not set', () => { system.version = undefined return system .loadAppStore() .catch(error => error) .then(error => { expect(error.message).toBe( "Cannot read property 'major' of undefined" ) }) }) }) describe('installAppVersion()', () => { beforeEach(() => { apiMock.post.mockReturnValue(Promise.resolve('')) }) it('should be a function on the system object', () => { expect(typeof system.installAppVersion).toBe('function') }) it('should reject the promise when the request fails', () => { apiMock.post.mockReturnValue( Promise.reject('Request for installation failed') ) return system .installAppVersion('PyYnjVl5iGt') .catch(error => error) .then(errorMessage => { expect(errorMessage).toBe('Request for installation failed') }) }) it('should call the api with the correct url', () => system.installAppVersion('PyYnjVl5iGt').then(() => { expect(apiMock.post).toBeCalledWith( 'appStore/PyYnjVl5iGt', '', { dataType: 'text' } ) })) it('should resolve the promise without a value', () => system.installAppVersion('PyYnjVl5iGt').then(response => { expect(response).toBeUndefined() })) }) describe('uninstallApp()', () => { beforeEach(() => { apiMock.delete = jest.fn().mockReturnValue(Promise.resolve({})) }) it('should be a function on the system object', () => { expect(typeof system.uninstallApp).toBe('function') }) it('should call the api.delete method with the correct url', () => system.uninstallApp('PyYnjVl5iGt').then(() => { expect(apiMock.delete).toBeCalledWith('apps/PyYnjVl5iGt') })) it('should resolve the request even when the api request fails', () => { apiMock.delete = jest.fn().mockReturnValue(Promise.reject({})) return system.uninstallApp('PyYnjVl5iGt') }) }) describe('reloadApps()', () => { beforeEach(() => { jest.spyOn(apiMock, 'update').mockReturnValue(Promise.resolve()) jest.spyOn(system, 'loadInstalledApps').mockReturnValueOnce( Promise.resolve() ) }) it('should be a function on the system object', () => { expect(typeof system.reloadApps).toBe('function') }) it('should call the update method on the api', () => system.reloadApps().then(() => { expect(apiMock.update.mock.calls[0][0]).toBe('apps') })) it('should call system.loadInstalledApps on success ', () => system.reloadApps().then(() => { expect(system.loadInstalledApps).toBeCalled() })) it('should chain the promise from loadInstalledApps', () => { const loadInstalledAppsPromise = Promise.resolve('Apps loaded') system.loadInstalledApps.mockReset() system.loadInstalledApps.mockReturnValue(loadInstalledAppsPromise) return system .reloadApps() .then(message => expect(message).toBe('Apps loaded')) }) it('should not call loadInstalledApps when the update request fails', () => { apiMock.update.mockReturnValue(Promise.reject()) jest.spyOn(system, 'loadInstalledApps') return system .reloadApps() .catch(message => message) .then(() => { expect(system.loadInstalledApps).not.toBeCalled() }) }) }) describe('compareVersions()', () => { let systemVersion let appVersion beforeEach(() => { systemVersion = { major: 2, minor: 23, snapshot: true, } appVersion = { major: 2, minor: 23, snapshot: true, } }) it('should be a function on the system class', () => { expect(typeof System.compareVersions).toBe('function') }) it('should return 0 for equal versions', () => { expect(System.compareVersions(systemVersion, appVersion)).toBe(0) }) it('should return 1 for a larger major system version', () => { systemVersion.major = 3 expect(System.compareVersions(systemVersion, appVersion)).toBe(1) }) it('should return 1 for a larger minor version', () => { systemVersion.minor = 24 expect(System.compareVersions(systemVersion, appVersion)).toBe(1) }) it('should return 1 when the app is a snapshot version', () => { systemVersion.snapshot = false appVersion.snapshot = true expect(System.compareVersions(systemVersion, appVersion)).toBe(1) }) it('should return -1 when the app is not a snapshot', () => { systemVersion.snapshot = true appVersion.snapshot = false expect(System.compareVersions(systemVersion, appVersion)).toBe(-1) }) it('should do correct comparison when a string is passed as a version', () => { expect(System.compareVersions('2.15', '2.16')).toBe(-1) expect(System.compareVersions('2.20-SNAPSHOT', '2.16')).toBe(4) }) }) describe('isVersionCompatible()', () => { let appVersion let systemVersion beforeEach(() => { jest.spyOn(System, 'compareVersions') appVersion = { min_platform_version: '2.23', max_platform_version: '2.23', } systemVersion = '2.23' }) afterEach(() => { System.compareVersions.mockRestore() }) it('should return false when the app is too new', () => { expect( System.isVersionCompatible( systemVersion, Object.assign(appVersion, { min_platform_version: '2.24' }) ) ).toBe(false) }) it('should return false when the app is too old', () => { expect( System.isVersionCompatible( systemVersion, Object.assign(appVersion, { max_platform_version: '2.22' }) ) ).toBe(false) }) it('should return true when the system version is within the app version range', () => { appVersion.min_platform_version = '2.20' appVersion.max_platform_version = '2.25' expect(System.isVersionCompatible(systemVersion, appVersion)) }) it('should return true when no version bounds are given', () => { appVersion = {} expect(System.isVersionCompatible(systemVersion, appVersion)).toBe( true ) }) it('should return false when the version is not compatible', () => { expect( System.isVersionCompatible('2.22', { min_platform_version: '2.17', max_platform_version: '2.20', }) ).toBe(false) }) describe('with 2.28 app version format', () => { beforeEach(() => { appVersion = { minDhisVersion: '2.22', maxDhisVersion: '2.23-SNAPSHOT', } systemVersion = '2.23' }) it('should return false when the app is too new', () => { expect( System.isVersionCompatible( systemVersion, Object.assign(appVersion, { minDhisVersion: '2.24' }) ) ).toBe(false) }) it('should return false when the app is too old', () => { expect( System.isVersionCompatible( systemVersion, Object.assign(appVersion, { maxDhisVersion: '2.22' }) ) ).toBe(false) }) it('should return true when the system version is within the app version range', () => { appVersion.minDhisVersion = '2.20' appVersion.maxDhisVersion = '2.25' expect(System.isVersionCompatible(systemVersion, appVersion)) }) it('should return true when no version bounds are given', () => { appVersion = {} expect( System.isVersionCompatible(systemVersion, appVersion) ).toBe(true) }) it('should return false when the version is not compatible', () => { expect( System.isVersionCompatible('2.22', { minDhisVersion: '2.17', maxDhisVersion: '2.20', }) ).toBe(false) }) }) }) describe('getSystem', () => { it('should return the same instance on consecutive requests', () => { expect(System.getSystem()).toBe(System.getSystem()) }) }) })