|
| 1 | +// Mock useProjectFlag to avoid deep dependency chain with legacy JS files |
| 2 | +jest.mock('common/services/useProjectFlag', () => ({ |
| 3 | + FEATURES_PAGE_SIZE: 100, |
| 4 | +})) |
| 5 | + |
| 6 | +import { |
| 7 | + buildUrlParams, |
| 8 | + buildApiFilterParams, |
| 9 | + getFiltersFromParams, |
| 10 | + hasActiveFilters, |
| 11 | +} from 'common/utils/featureFilterParams' |
| 12 | +import { SortOrder } from 'common/types/requests' |
| 13 | +import { TagStrategy } from 'common/types/responses' |
| 14 | +import type { FilterState } from 'common/types/featureFilters' |
| 15 | + |
| 16 | +const createDefaultFilters = ( |
| 17 | + overrides?: Partial<FilterState>, |
| 18 | +): FilterState => ({ |
| 19 | + group_owners: [], |
| 20 | + is_enabled: null, |
| 21 | + owners: [], |
| 22 | + search: null, |
| 23 | + showArchived: false, |
| 24 | + sort: { |
| 25 | + label: 'Name', |
| 26 | + sortBy: 'name', |
| 27 | + sortOrder: SortOrder.ASC, |
| 28 | + }, |
| 29 | + tag_strategy: TagStrategy.INTERSECTION, |
| 30 | + tags: [], |
| 31 | + value_search: '', |
| 32 | + ...overrides, |
| 33 | +}) |
| 34 | + |
| 35 | +describe('featureFilterParams', () => { |
| 36 | + describe('buildUrlParams', () => { |
| 37 | + it.each` |
| 38 | + showArchived | expected |
| 39 | + ${false} | ${'false'} |
| 40 | + ${true} | ${'true'} |
| 41 | + `( |
| 42 | + 'sets is_archived to "$expected" when showArchived is $showArchived', |
| 43 | + ({ expected, showArchived }) => { |
| 44 | + const result = buildUrlParams(createDefaultFilters({ showArchived }), 1) |
| 45 | + expect(result.is_archived).toBe(expected) |
| 46 | + }, |
| 47 | + ) |
| 48 | + |
| 49 | + it('always includes is_archived (never undefined)', () => { |
| 50 | + const result = buildUrlParams(createDefaultFilters(), 1) |
| 51 | + expect(result.is_archived).toBeDefined() |
| 52 | + }) |
| 53 | + |
| 54 | + it('includes page number', () => { |
| 55 | + const result = buildUrlParams(createDefaultFilters(), 5) |
| 56 | + expect(result.page).toBe(5) |
| 57 | + }) |
| 58 | + |
| 59 | + it('includes sort parameters', () => { |
| 60 | + const filters = createDefaultFilters({ |
| 61 | + sort: { |
| 62 | + label: 'Created', |
| 63 | + sortBy: 'created_date', |
| 64 | + sortOrder: SortOrder.DESC, |
| 65 | + }, |
| 66 | + }) |
| 67 | + const result = buildUrlParams(filters, 1) |
| 68 | + expect(result.sortBy).toBe('created_date') |
| 69 | + expect(result.sortOrder).toBe('desc') |
| 70 | + }) |
| 71 | + |
| 72 | + it('includes tags when present', () => { |
| 73 | + const result = buildUrlParams( |
| 74 | + createDefaultFilters({ tags: [1, 2, 3] }), |
| 75 | + 1, |
| 76 | + ) |
| 77 | + expect(result.tags).toBe('1,2,3') |
| 78 | + }) |
| 79 | + |
| 80 | + it('excludes empty arrays and search', () => { |
| 81 | + const filters = createDefaultFilters({ owners: [], search: '', tags: [] }) |
| 82 | + const result = buildUrlParams(filters, 1) |
| 83 | + expect(result.tags).toBeUndefined() |
| 84 | + expect(result.owners).toBeUndefined() |
| 85 | + expect(result.search).toBeUndefined() |
| 86 | + }) |
| 87 | + |
| 88 | + it('includes search when present', () => { |
| 89 | + const result = buildUrlParams(createDefaultFilters({ search: 'test' }), 1) |
| 90 | + expect(result.search).toBe('test') |
| 91 | + }) |
| 92 | + }) |
| 93 | + |
| 94 | + describe('buildApiFilterParams', () => { |
| 95 | + const mockResolver = (apiKey: string) => |
| 96 | + apiKey === 'test-key' ? 123 : undefined |
| 97 | + |
| 98 | + it.each` |
| 99 | + showArchived | expected |
| 100 | + ${false} | ${'false'} |
| 101 | + ${true} | ${'true'} |
| 102 | + `( |
| 103 | + 'sets is_archived to "$expected" when showArchived is $showArchived', |
| 104 | + ({ expected, showArchived }) => { |
| 105 | + const result = buildApiFilterParams( |
| 106 | + createDefaultFilters({ showArchived }), |
| 107 | + 1, |
| 108 | + 'test-key', |
| 109 | + 1, |
| 110 | + mockResolver, |
| 111 | + ) |
| 112 | + expect(result?.is_archived).toBe(expected) |
| 113 | + }, |
| 114 | + ) |
| 115 | + |
| 116 | + it('always includes is_archived (never undefined)', () => { |
| 117 | + const result = buildApiFilterParams( |
| 118 | + createDefaultFilters(), |
| 119 | + 1, |
| 120 | + 'test-key', |
| 121 | + 1, |
| 122 | + mockResolver, |
| 123 | + ) |
| 124 | + expect(result).not.toBeNull() |
| 125 | + expect(result?.is_archived).toBeDefined() |
| 126 | + }) |
| 127 | + |
| 128 | + it('returns null when environment ID cannot be resolved', () => { |
| 129 | + const result = buildApiFilterParams( |
| 130 | + createDefaultFilters(), |
| 131 | + 1, |
| 132 | + 'invalid-key', |
| 133 | + 1, |
| 134 | + mockResolver, |
| 135 | + ) |
| 136 | + expect(result).toBeNull() |
| 137 | + }) |
| 138 | + |
| 139 | + it('includes environmentId and projectId', () => { |
| 140 | + const result = buildApiFilterParams( |
| 141 | + createDefaultFilters(), |
| 142 | + 1, |
| 143 | + 'test-key', |
| 144 | + 42, |
| 145 | + mockResolver, |
| 146 | + ) |
| 147 | + expect(result?.environmentId).toBe('123') |
| 148 | + expect(result?.projectId).toBe(42) |
| 149 | + }) |
| 150 | + }) |
| 151 | + |
| 152 | + describe('getFiltersFromParams', () => { |
| 153 | + it.each` |
| 154 | + is_archived | expected |
| 155 | + ${'true'} | ${true} |
| 156 | + ${'false'} | ${false} |
| 157 | + ${undefined} | ${false} |
| 158 | + `( |
| 159 | + 'parses is_archived=$is_archived to showArchived=$expected', |
| 160 | + ({ expected, is_archived }) => { |
| 161 | + const result = getFiltersFromParams(is_archived ? { is_archived } : {}) |
| 162 | + expect(result.showArchived).toBe(expected) |
| 163 | + }, |
| 164 | + ) |
| 165 | + |
| 166 | + it.each` |
| 167 | + page | expected |
| 168 | + ${'3'} | ${3} |
| 169 | + ${undefined} | ${1} |
| 170 | + `('parses page=$page to $expected', ({ expected, page }) => { |
| 171 | + const result = getFiltersFromParams(page ? { page } : {}) |
| 172 | + expect(result.page).toBe(expected) |
| 173 | + }) |
| 174 | + |
| 175 | + it('parses tags as array of numbers', () => { |
| 176 | + const result = getFiltersFromParams({ tags: '1,2,3' }) |
| 177 | + expect(result.tags).toEqual([1, 2, 3]) |
| 178 | + }) |
| 179 | + |
| 180 | + it('parses sort order', () => { |
| 181 | + const result = getFiltersFromParams({ |
| 182 | + sortBy: 'created_date', |
| 183 | + sortOrder: 'desc', |
| 184 | + }) |
| 185 | + expect(result.sort.sortBy).toBe('created_date') |
| 186 | + expect(result.sort.sortOrder).toBe(SortOrder.DESC) |
| 187 | + }) |
| 188 | + }) |
| 189 | + |
| 190 | + describe('hasActiveFilters', () => { |
| 191 | + it('returns false for default filters', () => { |
| 192 | + expect(hasActiveFilters(createDefaultFilters())).toBe(false) |
| 193 | + }) |
| 194 | + |
| 195 | + it.each` |
| 196 | + scenario | overrides |
| 197 | + ${'tags present'} | ${{ tags: [1] }} |
| 198 | + ${'showArchived'} | ${{ showArchived: true }} |
| 199 | + ${'search present'} | ${{ search: 'test' }} |
| 200 | + ${'is_enabled set'} | ${{ is_enabled: true }} |
| 201 | + ${'owners present'} | ${{ owners: [1] }} |
| 202 | + `('returns true when $scenario', ({ overrides }) => { |
| 203 | + expect(hasActiveFilters(createDefaultFilters(overrides))).toBe(true) |
| 204 | + }) |
| 205 | + }) |
| 206 | +}) |
0 commit comments