@@ -5,6 +5,7 @@ import { ChartType, DataQuality, PriceChartType } from '~/components/Charts/util
55import {
66 getCalculatedPricePercentChange ,
77 getDisplayedPricePercentChange ,
8+ toStrictlyAscendingByTime ,
89 useTokenPriceChartData ,
910} from '~/hooks/useTokenPriceChartData'
1011import { renderHook } from '~/test-utils/render'
@@ -245,6 +246,33 @@ describe('useTokenPriceChartData', () => {
245246 expect ( result . current . dataQuality ) . toBe ( DataQuality . VALID )
246247 expect ( result . current . entries [ 0 ] . value ) . toBe ( 9 )
247248 } )
249+
250+ it ( 'produces strictly ascending timestamps when CoinGecko returns a duplicate trailing timestamp' , ( ) => {
251+ // Upstream CoinGecko history can end with two points at the same second. lightweight-charts
252+ // requires strictly-ascending times; a zero delta breaks curved-line interpolation and paints
253+ // a spurious diagonal line/wedge across the chart.
254+ mockUseTokenPriceHistoryQuery . mockReturnValue (
255+ makeCoinGeckoResult ( [
256+ priceHistoryEntry ( 1000 , 20 ) ,
257+ priceHistoryEntry ( 2000 , 21 ) ,
258+ priceHistoryEntry ( 3000 , 22 ) ,
259+ priceHistoryEntry ( 3000 , 22 ) ,
260+ ] ) ,
261+ )
262+
263+ const { result } = renderHook ( ( ) =>
264+ useTokenPriceChartData ( {
265+ variables : BASE_VARIABLES ,
266+ skip : false ,
267+ priceChartType : PriceChartType . LINE ,
268+ } ) ,
269+ )
270+
271+ const times = result . current . entries . map ( ( entry ) => entry . time )
272+ for ( let i = 1 ; i < times . length ; i ++ ) {
273+ expect ( times [ i ] ) . toBeGreaterThan ( times [ i - 1 ] )
274+ }
275+ } )
248276} )
249277
250278function point ( time : number , close : number ) : PriceChartData {
@@ -286,3 +314,21 @@ describe('getDisplayedPricePercentChange', () => {
286314 ) . toBe ( 300 )
287315 } )
288316} )
317+
318+ describe ( 'toStrictlyAscendingByTime' , ( ) => {
319+ it ( 'collapses duplicate timestamps, keeping the latest value' , ( ) => {
320+ const result = toStrictlyAscendingByTime ( [ point ( 1000 , 10 ) , point ( 2000 , 11 ) , point ( 3000 , 12 ) , point ( 3000 , 13 ) ] )
321+ expect ( result . map ( ( entry ) => entry . time ) ) . toEqual ( [ 1000 , 2000 , 3000 ] )
322+ expect ( result [ result . length - 1 ] . value ) . toBe ( 13 )
323+ } )
324+
325+ it ( 'drops out-of-order timestamps' , ( ) => {
326+ const result = toStrictlyAscendingByTime ( [ point ( 1000 , 10 ) , point ( 3000 , 12 ) , point ( 2000 , 11 ) ] )
327+ expect ( result . map ( ( entry ) => entry . time ) ) . toEqual ( [ 1000 , 3000 ] )
328+ } )
329+
330+ it ( 'leaves already-ascending data untouched' , ( ) => {
331+ const entries = [ point ( 1000 , 10 ) , point ( 2000 , 11 ) , point ( 3000 , 12 ) ]
332+ expect ( toStrictlyAscendingByTime ( entries ) ) . toEqual ( entries )
333+ } )
334+ } )
0 commit comments