import { ConfigProvider } from '@dhis2/app-service-config' import type { Config } from '@dhis2/app-service-config' import { DataEngine, RestAPILink } from '@dhis2/data-engine' import { render } from '@testing-library/react' import React from 'react' import { DataContext } from '../context/DataContext' import { DataProvider } from './DataProvider' type ProviderProps = { baseUrl?: string apiVersion?: number } /* * Once app-platform renders the app-runtime provider, the config object is * stable across renders, so these helpers reuse a single config reference * unless a test explicitly swaps it out. */ const stableConfig: Config = { baseUrl: 'http://localhost:8080', apiVersion: 42, } const renderProvider = ( config: Config = stableConfig, props: ProviderProps = {} ) => { const consumer = jest.fn(() => null) const ui = (nextConfig: Config, nextProps: ProviderProps) => ( {consumer} ) const { rerender } = render(ui(config, props)) return { rerender: ( nextConfig: Config = config, nextProps: ProviderProps = props ) => rerender(ui(nextConfig, nextProps)), contexts: () => consumer.mock.calls.map((call: any) => call[0]), engines: () => consumer.mock.calls.map((call: any) => call[0].engine), latestLink: () => consumer.mock.calls[consumer.mock.calls.length - 1][0].engine.link, } } const unique = (values: unknown[]) => new Set(values).size describe('DataProvider', () => { it('Should pass a new engine and RestAPILink to consumers', () => { const renderFunction = jest.fn() render( {renderFunction} ) expect(renderFunction).toHaveBeenCalled() const context = renderFunction.mock.calls.pop()[0] expect(context).not.toBeUndefined() expect(context.engine).toBeInstanceOf(DataEngine) expect(context.engine.link).toBeInstanceOf(RestAPILink) }) describe('stability', () => { it('reuses the same engine across re-renders', () => { const { rerender, engines } = renderProvider() rerender() rerender() expect(engines()).toHaveLength(3) expect(unique(engines())).toBe(1) }) it('reuses the same context value across re-renders', () => { const { rerender, contexts } = renderProvider() rerender() rerender() expect(contexts()).toHaveLength(3) expect(unique(contexts())).toBe(1) }) it('reuses the same engine when the props stay equal', () => { const { rerender, engines } = renderProvider(stableConfig, { baseUrl: 'http://example.com', apiVersion: 39, }) rerender(stableConfig, { baseUrl: 'http://example.com', apiVersion: 39, }) expect(unique(engines())).toBe(1) }) }) describe('rebuilding', () => { it('builds a new engine when the baseUrl prop changes', () => { const { rerender, engines, latestLink } = renderProvider( stableConfig, { baseUrl: 'http://one.com' } ) rerender(stableConfig, { baseUrl: 'http://two.com' }) expect(unique(engines())).toBe(2) expect(latestLink().config.baseUrl).toBe('http://two.com') }) it('builds a new engine when the apiVersion prop changes', () => { const { rerender, engines, latestLink } = renderProvider( stableConfig, { apiVersion: 38 } ) rerender(stableConfig, { apiVersion: 39 }) expect(unique(engines())).toBe(2) expect(latestLink().config.apiVersion).toBe(39) }) it('builds a new engine when the config object changes', () => { const { rerender, engines, latestLink } = renderProvider() rerender({ baseUrl: 'http://elsewhere.com', apiVersion: 40 }) expect(unique(engines())).toBe(2) expect(latestLink().config.baseUrl).toBe('http://elsewhere.com') }) }) describe('config resolution', () => { it('uses the config values when no props are supplied', () => { const { latestLink } = renderProvider() expect(latestLink().config.baseUrl).toBe('http://localhost:8080') expect(latestLink().config.apiVersion).toBe(42) }) it('lets props take precedence over the config', () => { const { latestLink } = renderProvider(stableConfig, { baseUrl: 'http://override.com', apiVersion: 38, }) expect(latestLink().config.baseUrl).toBe('http://override.com') expect(latestLink().config.apiVersion).toBe(38) }) it('falls back to the config when props are explicitly undefined', () => { const { latestLink } = renderProvider(stableConfig, { baseUrl: undefined, apiVersion: undefined, }) expect(latestLink().config.baseUrl).toBe('http://localhost:8080') expect(latestLink().config.apiVersion).toBe(42) }) it('does not leak children into the link config', () => { const { latestLink } = renderProvider() expect(latestLink().config).not.toHaveProperty('children') }) }) })