diff --git a/DESIGN.md b/DESIGN.md index ae54ae5..ff0e8dc 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -102,7 +102,7 @@ const filters = parseFilters( | # | 决策 | |---|------| -| 40 | 支持 **`setFilters()` / `loadFilters()`** 回填 | +| 40 | 回填能力在 **component 侧**(`setFilters()` / `loadFilters()` / `reset()` ref API,见 component DESIGN 输出协议);middleware 只做单向解析 | | 41 | 多函数导出:`parseFilters` / `toTree` / `mapOperators` / `splitMultiValue` 等 | ### 导出函数 diff --git a/test/parseFilters.test.js b/test/parseFilters.test.js index 81dfc69..a31762f 100644 --- a/test/parseFilters.test.js +++ b/test/parseFilters.test.js @@ -106,6 +106,80 @@ describe('parseFilters', () => { ); expect(out[0].logic).toBeUndefined(); }); + + it('supports tree mode', () => { + const out = parseFilters( + [ + { field: 'name', value: 'a', query: 'EQ' }, + { field: 'status', value: 'b', query: 'EQ', logic: 'OR' }, + ], + { allowedFields, mode: 'tree' }, + ); + expect(out).toHaveLength(1); + expect(out[0].logic).toBe('OR'); + expect(out[0].children).toHaveLength(2); + }); + + it('skips unknown fields when unknownField is skip', () => { + const out = parseFilters( + [ + { field: 'unknown', value: 'x', query: 'EQ' }, + { field: 'name', value: 'a', query: 'EQ' }, + ], + { allowedFields, unknownField: 'skip' }, + ); + expect(out).toHaveLength(1); + expect(out[0].field).toBe('name'); + }); + + it('throws aggregated errors in strict mode', () => { + expect(() => + parseFilters( + [ + { value: 'x', query: 'EQ' }, // missing field + 'not-an-object', + ], + { allowedFields, strict: true }, + ), + ).toThrow(/validation error/); + }); + + it('merges custom operatorMap', () => { + const out = parseFilters( + [{ field: 'name', value: 'x', query: 'EQ' }], + { allowedFields, operatorMap: { EQ: 'equals' } }, + ); + expect(out[0].op).toBe('equals'); + }); + + it('respects custom skipFlags', () => { + const out = parseFilters( + [ + { field: 'name', value: 'a', query: 'EQ', readOnly: true }, + { field: 'status', value: 'b', query: 'EQ' }, + ], + { allowedFields, skipFlags: ['readOnly'] }, + ); + expect(out).toHaveLength(1); + expect(out[0].field).toBe('status'); + }); + + it('does not emit values for scalar queries', () => { + const out = parseFilters( + [{ field: 'name', value: 'xx', query: 'EQ' }], + { allowedFields }, + ); + expect(out[0].values).toBeUndefined(); + }); + + it('parses IS_NULL with null value without values', () => { + const out = parseFilters( + [{ field: 'status', value: null, query: 'IS_NULL' }], + { allowedFields }, + ); + expect(out[0]).toMatchObject({ field: 'status', query: 'IS_NULL', op: 'IS NULL', value: null }); + expect(out[0].values).toBeUndefined(); + }); }); describe('toTree', () => {