From 3a327e2d92b5035bea1158f77a0432342116dbaa Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Sun, 24 May 2026 15:45:07 +0300 Subject: [PATCH] refactor(GetFonts): tighten mapManagerToParams + add coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Collapse the three duplicated getGroup/map/length-guard chains into a single selectedIn helper. Drop the unnecessary `as string[]` casts — Property.value already yields string at the call site. Add unit tests covering empty query, populated query, missing group, empty group, single + multi selection, unknown group ids, and the combined param shape. --- .../lib/mapper/mapManagerToParams.test.ts | 127 ++++++++++++++++++ .../GetFonts/lib/mapper/mapManagerToParams.ts | 29 ++-- 2 files changed, 139 insertions(+), 17 deletions(-) create mode 100644 src/features/GetFonts/lib/mapper/mapManagerToParams.test.ts diff --git a/src/features/GetFonts/lib/mapper/mapManagerToParams.test.ts b/src/features/GetFonts/lib/mapper/mapManagerToParams.test.ts new file mode 100644 index 0000000..8bb73c0 --- /dev/null +++ b/src/features/GetFonts/lib/mapper/mapManagerToParams.test.ts @@ -0,0 +1,127 @@ +import type { Property } from '$shared/lib'; +import { + describe, + expect, + it, +} from 'vitest'; +import { createFilterManager } from '../../model/store/filterManager/filterManager.svelte'; +import { mapManagerToParams } from './mapManagerToParams'; + +/** + * Build a Property with explicit selection state. + */ +function prop(value: string, selected = false): Property { + return { id: value, name: value, value, selected }; +} + +/** + * Build a filter group with a known id and a list of (value, selected) entries. + */ +function group(id: string, props: Array<[string, boolean]>) { + return { + id, + label: id, + properties: props.map(([value, selected]) => prop(value, selected)), + }; +} + +describe('mapManagerToParams', () => { + describe('search query', () => { + it('omits q when query is empty', () => { + const manager = createFilterManager({ queryValue: '', groups: [] }); + expect(mapManagerToParams(manager).q).toBeUndefined(); + }); + + it('passes the debounced query through as q', () => { + // Constructor seeds both immediate and debounced synchronously. + const manager = createFilterManager({ queryValue: 'roboto', groups: [] }); + expect(mapManagerToParams(manager).q).toBe('roboto'); + }); + }); + + describe('group selections', () => { + it('omits a group entirely when no group with that id exists', () => { + const manager = createFilterManager({ queryValue: '', groups: [] }); + const params = mapManagerToParams(manager); + expect(params.providers).toBeUndefined(); + expect(params.categories).toBeUndefined(); + expect(params.subsets).toBeUndefined(); + }); + + it('omits a group when it exists but has no selections', () => { + const manager = createFilterManager({ + queryValue: '', + groups: [group('providers', [['google', false], ['fontshare', false]])], + }); + expect(mapManagerToParams(manager).providers).toBeUndefined(); + }); + + it('returns the selected values for a single group', () => { + const manager = createFilterManager({ + queryValue: '', + groups: [group('providers', [['google', true], ['fontshare', false]])], + }); + expect(mapManagerToParams(manager).providers).toEqual(['google']); + }); + + it('returns multiple selected values in selection order', () => { + const manager = createFilterManager({ + queryValue: '', + groups: [ + group('categories', [ + ['serif', true], + ['sans-serif', false], + ['display', true], + ['monospace', true], + ]), + ], + }); + expect(mapManagerToParams(manager).categories).toEqual(['serif', 'display', 'monospace']); + }); + + it('maps each of the three recognized group ids independently', () => { + const manager = createFilterManager({ + queryValue: '', + groups: [ + group('providers', [['google', true]]), + group('categories', [['serif', true], ['sans-serif', true]]), + group('subsets', [['latin', true]]), + ], + }); + const params = mapManagerToParams(manager); + expect(params.providers).toEqual(['google']); + expect(params.categories).toEqual(['serif', 'sans-serif']); + expect(params.subsets).toEqual(['latin']); + }); + + it('ignores groups whose id does not match providers/categories/subsets', () => { + const manager = createFilterManager({ + queryValue: '', + groups: [group('weights', [['400', true], ['700', true]])], + }); + const params = mapManagerToParams(manager); + expect(params.providers).toBeUndefined(); + expect(params.categories).toBeUndefined(); + expect(params.subsets).toBeUndefined(); + }); + }); + + describe('combined output', () => { + it('produces a complete param object when query and selections coexist', () => { + const manager = createFilterManager({ + queryValue: 'inter', + groups: [ + group('providers', [['google', true]]), + group('categories', [['sans-serif', true]]), + group('subsets', [['latin', false]]), + ], + }); + expect(mapManagerToParams(manager)).toEqual({ + q: 'inter', + providers: ['google'], + categories: ['sans-serif'], + subsets: undefined, + }); + }); + }); +}); diff --git a/src/features/GetFonts/lib/mapper/mapManagerToParams.ts b/src/features/GetFonts/lib/mapper/mapManagerToParams.ts index 1f7581f..6153bd2 100644 --- a/src/features/GetFonts/lib/mapper/mapManagerToParams.ts +++ b/src/features/GetFonts/lib/mapper/mapManagerToParams.ts @@ -29,25 +29,20 @@ import type { FilterManager } from '../../model'; * ``` */ export function mapManagerToParams(manager: FilterManager): Partial { - const providers = manager.getGroup('providers')?.instance.selectedProperties.map(p => p.value); - const categories = manager.getGroup('categories')?.instance.selectedProperties.map(p => p.value); - const subsets = manager.getGroup('subsets')?.instance.selectedProperties.map(p => p.value); + /** + * Return the list of selected values for a group, or undefined when + * the group is missing or has no selection — matches the API's + * "omit empty filters" contract. + */ + const selectedIn = (id: string): string[] | undefined => { + const values = manager.getGroup(id)?.instance.selectedProperties.map(p => p.value); + return values && values.length > 0 ? values : undefined; + }; return { - // Search query (debounced) q: manager.debouncedQueryValue || undefined, - - // NEW: Support arrays - send all selected values - providers: providers && providers.length > 0 - ? providers as string[] - : undefined, - - categories: categories && categories.length > 0 - ? categories as string[] - : undefined, - - subsets: subsets && subsets.length > 0 - ? subsets as string[] - : undefined, + providers: selectedIn('providers'), + categories: selectedIn('categories'), + subsets: selectedIn('subsets'), }; }