QueryBlock.utils.test.ts159 lines · main
| 1 | import { describe, expect, it } from 'vitest' |
| 2 | |
| 3 | import { |
| 4 | checkHasNonPositiveValues, |
| 5 | formatLogTick, |
| 6 | getCumulativeResults, |
| 7 | } from '@/components/ui/QueryBlock/QueryBlock.utils' |
| 8 | |
| 9 | describe('checkHasNonPositiveValues', () => { |
| 10 | it('returns false for an empty array', () => { |
| 11 | expect(checkHasNonPositiveValues([], 'value')).toBe(false) |
| 12 | }) |
| 13 | |
| 14 | it('returns false when all values are positive', () => { |
| 15 | const data = [{ value: 1 }, { value: 2 }, { value: 100 }] |
| 16 | expect(checkHasNonPositiveValues(data, 'value')).toBe(false) |
| 17 | }) |
| 18 | |
| 19 | it('returns true when a value is zero', () => { |
| 20 | const data = [{ value: 1 }, { value: 0 }, { value: 3 }] |
| 21 | expect(checkHasNonPositiveValues(data, 'value')).toBe(true) |
| 22 | }) |
| 23 | |
| 24 | it('returns true when a value is negative', () => { |
| 25 | const data = [{ value: 5 }, { value: -1 }, { value: 3 }] |
| 26 | expect(checkHasNonPositiveValues(data, 'value')).toBe(true) |
| 27 | }) |
| 28 | |
| 29 | it('returns true when all values are non-positive', () => { |
| 30 | const data = [{ value: -5 }, { value: 0 }, { value: -1 }] |
| 31 | expect(checkHasNonPositiveValues(data, 'value')).toBe(true) |
| 32 | }) |
| 33 | |
| 34 | it('checks only the specified key', () => { |
| 35 | const data = [ |
| 36 | { x: -1, y: 5 }, |
| 37 | { x: 2, y: 10 }, |
| 38 | ] |
| 39 | expect(checkHasNonPositiveValues(data, 'y')).toBe(false) |
| 40 | expect(checkHasNonPositiveValues(data, 'x')).toBe(true) |
| 41 | }) |
| 42 | |
| 43 | it('returns false when key is absent (undefined cast to NaN is not <= 0)', () => { |
| 44 | const data = [{ value: 1 }, { value: 2 }] |
| 45 | expect(checkHasNonPositiveValues(data, 'missing')).toBe(false) |
| 46 | }) |
| 47 | }) |
| 48 | |
| 49 | describe('formatLogTick', () => { |
| 50 | it('formats values below 1,000 as plain locale strings', () => { |
| 51 | expect(formatLogTick(0)).toBe('0') |
| 52 | expect(formatLogTick(1)).toBe('1') |
| 53 | expect(formatLogTick(999)).toBe('999') |
| 54 | expect(formatLogTick(500)).toBe('500') |
| 55 | }) |
| 56 | |
| 57 | it('formats values >= 1,000 with a "k" suffix', () => { |
| 58 | expect(formatLogTick(1_000)).toBe('1k') |
| 59 | expect(formatLogTick(1_500)).toBe('1.5k') |
| 60 | expect(formatLogTick(10_000)).toBe('10k') |
| 61 | expect(formatLogTick(999_999)).toBe('1,000k') |
| 62 | }) |
| 63 | |
| 64 | it('formats values >= 1,000,000 with an "M" suffix', () => { |
| 65 | expect(formatLogTick(1_000_000)).toBe('1M') |
| 66 | expect(formatLogTick(1_500_000)).toBe('1.5M') |
| 67 | expect(formatLogTick(10_000_000)).toBe('10M') |
| 68 | expect(formatLogTick(1_234_567)).toBe('1.2M') |
| 69 | }) |
| 70 | |
| 71 | it('respects maximumFractionDigits of 1', () => { |
| 72 | // 1,050 → 1.05k, but max 1 decimal → "1.1k" (rounded) |
| 73 | expect(formatLogTick(1_050)).toBe('1.1k') |
| 74 | // 1,049 → 1.049k → "1k" (rounded down) |
| 75 | expect(formatLogTick(1_049)).toBe('1k') |
| 76 | }) |
| 77 | }) |
| 78 | |
| 79 | describe('getCumulativeResults', () => { |
| 80 | it('returns empty array when results are empty', () => { |
| 81 | expect( |
| 82 | getCumulativeResults( |
| 83 | { rows: [] }, |
| 84 | { type: 'bar', xKey: 'x', yKey: 'y', cumulative: false, showLabels: false, showGrid: false } |
| 85 | ) |
| 86 | ).toEqual([]) |
| 87 | }) |
| 88 | |
| 89 | it('returns empty array when results are undefined', () => { |
| 90 | expect( |
| 91 | getCumulativeResults(undefined as any, { |
| 92 | type: 'bar', |
| 93 | xKey: 'x', |
| 94 | yKey: 'y', |
| 95 | cumulative: false, |
| 96 | showLabels: false, |
| 97 | showGrid: false, |
| 98 | }) |
| 99 | ).toEqual([]) |
| 100 | }) |
| 101 | |
| 102 | it('accumulates yKey values across rows', () => { |
| 103 | const results = { |
| 104 | rows: [ |
| 105 | { x: 'a', y: 10 }, |
| 106 | { x: 'b', y: 20 }, |
| 107 | { x: 'c', y: 5 }, |
| 108 | ], |
| 109 | } |
| 110 | const config = { |
| 111 | type: 'bar' as const, |
| 112 | xKey: 'x', |
| 113 | yKey: 'y', |
| 114 | cumulative: true, |
| 115 | showLabels: false, |
| 116 | showGrid: false, |
| 117 | } |
| 118 | const output = getCumulativeResults(results, config) |
| 119 | expect(output).toEqual([ |
| 120 | { x: 'a', y: 10 }, |
| 121 | { x: 'b', y: 30 }, |
| 122 | { x: 'c', y: 35 }, |
| 123 | ]) |
| 124 | }) |
| 125 | |
| 126 | it('preserves other keys on each row', () => { |
| 127 | const results = { |
| 128 | rows: [ |
| 129 | { x: 'a', y: 1, label: 'foo' }, |
| 130 | { x: 'b', y: 2, label: 'bar' }, |
| 131 | ], |
| 132 | } |
| 133 | const config = { |
| 134 | type: 'bar' as const, |
| 135 | xKey: 'x', |
| 136 | yKey: 'y', |
| 137 | cumulative: true, |
| 138 | showLabels: false, |
| 139 | showGrid: false, |
| 140 | } |
| 141 | const output = getCumulativeResults(results, config) |
| 142 | expect(output[0]).toMatchObject({ x: 'a', y: 1, label: 'foo' }) |
| 143 | expect(output[1]).toMatchObject({ x: 'b', y: 3, label: 'bar' }) |
| 144 | }) |
| 145 | |
| 146 | it('handles a single row', () => { |
| 147 | const results = { rows: [{ x: 'a', y: 42 }] } |
| 148 | const config = { |
| 149 | type: 'bar' as const, |
| 150 | xKey: 'x', |
| 151 | yKey: 'y', |
| 152 | cumulative: true, |
| 153 | showLabels: false, |
| 154 | showGrid: false, |
| 155 | } |
| 156 | const output = getCumulativeResults(results, config) |
| 157 | expect(output).toEqual([{ x: 'a', y: 42 }]) |
| 158 | }) |
| 159 | }) |