Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 31 additions & 14 deletions adapter/src/components/ServerVersionProvider.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Provider } from '@dhis2/app-runtime'
import { getBaseUrlByAppName, setBaseUrlByAppName } from '@dhis2/pwa'
import PropTypes from 'prop-types'
import React, { useEffect, useState } from 'react'
import React, { useEffect, useMemo, useState } from 'react'
import { get } from '../utils/api.js'
import { parseDHIS2ServerVersion, parseVersion } from '../utils/parseVersion.js'
import { LoadingMask } from './LoadingMask.js'
Expand Down Expand Up @@ -43,6 +43,35 @@ export const ServerVersionProvider = ({
const { systemInfo } = systemInfoState
const { userInfo } = userInfoState
const { baseUrl } = baseUrlState
const serverVersion = useMemo(
() =>
systemInfo?.version
? parseDHIS2ServerVersion(systemInfo.version)
: undefined,
[systemInfo?.version]
)
const config = useMemo(
() => ({
appName,
appUrlSlug,
appVersion: parseVersion(appVersion),
baseUrl,
apiVersion: apiVersion || serverVersion?.minor,
serverVersion,
systemInfo,
pwaEnabled,
}),
[
appName,
appUrlSlug,
appVersion,
baseUrl,
apiVersion,
serverVersion,
systemInfo,
pwaEnabled,
]
)

useEffect(() => {
// if URL prop is not set, set state to error to show login modal.
Expand Down Expand Up @@ -204,21 +233,9 @@ export const ServerVersionProvider = ({
return <LoadingMask />
}

const serverVersion = parseDHIS2ServerVersion(systemInfo.version)
const realApiVersion = serverVersion.minor

return (
<Provider
config={{
appName,
appUrlSlug,
appVersion: parseVersion(appVersion),
baseUrl,
apiVersion: apiVersion || realApiVersion,
serverVersion,
systemInfo,
pwaEnabled,
}}
config={config}
userInfo={userInfo}
offlineInterface={loginApp ? null : offlineInterface}
plugin={plugin}
Expand Down
108 changes: 108 additions & 0 deletions adapter/src/components/__tests__/ServerVersionProvider.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { Provider } from '@dhis2/app-runtime'
import { render, waitFor } from '@testing-library/react'
import React from 'react'
import { get } from '../../utils/api.js'
import { ServerVersionProvider } from '../ServerVersionProvider.js'

jest.mock('@dhis2/app-runtime', () => ({
Provider: jest.fn(({ children }) => children),
}))

jest.mock('@dhis2/pwa', () => ({
getBaseUrlByAppName: jest.fn(() => Promise.resolve(undefined)),
setBaseUrlByAppName: jest.fn(() => Promise.resolve()),
OfflineInterface: jest.fn(),
}))

jest.mock('../../utils/api.js', () => ({
get: jest.fn(),
}))

// LoginModal imports the generated `src/locales` module, which is gitignored and
// absent in CI. It is not exercised here, so keep it out of the module graph.
jest.mock('../LoginModal.js', () => ({
LoginModal: () => null,
}))

const abortableResolve = (value) => {
const promise = Promise.resolve(value)
promise.abort = jest.fn()
return promise
}

const props = {
appName: 'test-app',
appUrlSlug: 'test-app',
appVersion: '1.2.3',
url: 'http://localhost:8080',
pwaEnabled: false,
}

const lastConfig = () =>
Provider.mock.calls[Provider.mock.calls.length - 1][0].config

describe('ServerVersionProvider', () => {
beforeEach(() => {
jest.clearAllMocks()
get.mockImplementation((url) =>
url.endsWith('/api/me')
? abortableResolve({ id: 'user-id' })
: abortableResolve({ version: '2.41.1' })
)
})

it('provides the same config object across re-renders', async () => {
const { rerender } = render(
<ServerVersionProvider {...props}>
<div />
</ServerVersionProvider>
)

await waitFor(() => expect(Provider).toHaveBeenCalled())
const config = lastConfig()
const callCount = Provider.mock.calls.length

rerender(
<ServerVersionProvider {...props}>
<div />
</ServerVersionProvider>
)

// The re-render must actually reach Provider, or the test proves nothing
expect(Provider.mock.calls.length).toBeGreaterThan(callCount)
expect(lastConfig()).toBe(config)
})

it('parses the server version into the config', async () => {
render(
<ServerVersionProvider {...props}>
<div />
</ServerVersionProvider>
)

await waitFor(() => expect(Provider).toHaveBeenCalled())
expect(lastConfig()).toMatchObject({
apiVersion: 41,
appVersion: { major: 1, minor: 2, patch: 3 },
serverVersion: { major: 2, minor: 41, patch: 1 },
})
})

it('does not warn about the server version while system info loads', async () => {
const warn = jest.spyOn(console, 'warn').mockImplementation(() => {})

render(
<ServerVersionProvider {...props}>
<div />
</ServerVersionProvider>
)

// Nothing to parse yet — must not warn about an undefined version
expect(warn).not.toHaveBeenCalled()

await waitFor(() => expect(Provider).toHaveBeenCalled())
expect(warn).not.toHaveBeenCalled()

warn.mockRestore()
})
})
Loading