- 基于 grill-me Q1-Q61 决策 - Vitest 测试与 CI workflow - middleware: parseFilters 核心实现 - component: Vue3 TransForm 基础组件 + Storybook Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
DEFAULT_OPERATOR_MAP,
|
||||
mapOperators,
|
||||
parseFilters,
|
||||
splitMultiValue,
|
||||
toTree,
|
||||
} from '../src/index.js';
|
||||
|
||||
describe('mapOperators', () => {
|
||||
it('maps known query codes', () => {
|
||||
expect(mapOperators('EQ')).toBe('=');
|
||||
expect(mapOperators('NOT_IN')).toBe('NOT IN');
|
||||
});
|
||||
|
||||
it('allows custom overrides', () => {
|
||||
expect(mapOperators('EQ', { EQ: 'equals' })).toBe('equals');
|
||||
});
|
||||
});
|
||||
|
||||
describe('splitMultiValue', () => {
|
||||
it('splits IN values', () => {
|
||||
expect(splitMultiValue('IN', 'a,b,c')).toEqual(['a', 'b', 'c']);
|
||||
});
|
||||
|
||||
it('splits BETWEEN values', () => {
|
||||
expect(splitMultiValue('BETWEEN', '2024-01-01,2024-12-31')).toEqual([
|
||||
'2024-01-01',
|
||||
'2024-12-31',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseFilters', () => {
|
||||
const allowedFields = ['name', 'status', 'tags', 'created'];
|
||||
|
||||
it('requires allowedFields', () => {
|
||||
expect(() => parseFilters([], {})).toThrow(/allowedFields/);
|
||||
});
|
||||
|
||||
it('parses basic conditions with logic pass-through', () => {
|
||||
const input = [
|
||||
{ field: 'name', value: 'xx', query: 'EQ' },
|
||||
{ field: 'tags', value: 'a,b', query: 'IN', logic: 'OR' },
|
||||
];
|
||||
const out = parseFilters(input, { allowedFields });
|
||||
expect(out).toEqual([
|
||||
{
|
||||
field: 'name',
|
||||
query: 'EQ',
|
||||
op: '=',
|
||||
value: 'xx',
|
||||
},
|
||||
{
|
||||
field: 'tags',
|
||||
query: 'IN',
|
||||
op: 'IN',
|
||||
value: 'a,b',
|
||||
values: ['a', 'b'],
|
||||
logic: 'OR',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('preserves empty markers by default', () => {
|
||||
const out = parseFilters(
|
||||
[{ field: 'name', value: null, query: 'EQ', empty: true }],
|
||||
{ allowedFields },
|
||||
);
|
||||
expect(out[0].empty).toBe(true);
|
||||
});
|
||||
|
||||
it('skips disabled and hidden by default', () => {
|
||||
const out = parseFilters(
|
||||
[
|
||||
{ field: 'name', value: 'a', query: 'EQ', disabled: true },
|
||||
{ field: 'status', value: 'b', query: 'EQ', hidden: true },
|
||||
{ field: 'tags', value: 'c', query: 'EQ' },
|
||||
],
|
||||
{ allowedFields },
|
||||
);
|
||||
expect(out).toHaveLength(1);
|
||||
expect(out[0].field).toBe('tags');
|
||||
});
|
||||
|
||||
it('can skip empty items when skipEmpty is true', () => {
|
||||
const out = parseFilters(
|
||||
[{ field: 'name', value: null, query: 'EQ', empty: true }],
|
||||
{ allowedFields, skipEmpty: true },
|
||||
);
|
||||
expect(out).toEqual([]);
|
||||
});
|
||||
|
||||
it('throws on unknown field by default', () => {
|
||||
expect(() =>
|
||||
parseFilters([{ field: 'unknown', value: 'x', query: 'EQ' }], {
|
||||
allowedFields,
|
||||
}),
|
||||
).toThrow(/unknown field/);
|
||||
});
|
||||
|
||||
it('supports flat mode', () => {
|
||||
const out = parseFilters(
|
||||
[{ field: 'name', value: 'x', query: 'EQ', logic: 'AND' }],
|
||||
{ allowedFields, mode: 'flat' },
|
||||
);
|
||||
expect(out[0].logic).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('toTree', () => {
|
||||
it('nests flat filters', () => {
|
||||
const flat = [
|
||||
{ field: 'a', query: 'EQ', op: '=', value: '1' },
|
||||
{ field: 'b', query: 'EQ', op: '=', value: '2', logic: 'OR' },
|
||||
];
|
||||
const tree = toTree(flat);
|
||||
expect(tree[0]).toMatchObject({
|
||||
logic: 'OR',
|
||||
children: expect.any(Array),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('DEFAULT_OPERATOR_MAP', () => {
|
||||
it('includes IS_NULL operators', () => {
|
||||
expect(DEFAULT_OPERATOR_MAP.IS_NULL).toBe('IS NULL');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user