Skip to content

Commit 0b276ee

Browse files
Copilothotlong
andcommitted
test(crm): add query parameter injection and filter bar integration tests
Add 7 integration tests that verify CRM metadata compatibility with RecordPickerDialog enterprise features: - lookup_filters → $filter query parameter conversion (eq, $in, $ne, $nin) - lookup_columns type hints → filter bar configuration (select, number, boolean) - All CRM filters produce valid non-empty $filter records Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 23db6bc commit 0b276ee

1 file changed

Lines changed: 121 additions & 0 deletions

File tree

examples/crm/src/__tests__/crm-metadata.test.ts

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,127 @@ describe('CRM Metadata Spec Compliance', () => {
561561
expect(contacts.lookup_page_size).toBe(15);
562562
});
563563
});
564+
565+
// ------------------------------------------------------------------
566+
// Enterprise Query Parameter Injection & Filter Bar Integration
567+
// ------------------------------------------------------------------
568+
569+
describe('Enterprise Query Parameter & Filter Bar Compatibility', () => {
570+
/**
571+
* Simulate RecordPickerDialog's lookupFiltersToRecord conversion.
572+
* This mirrors the internal function in RecordPickerDialog.tsx to verify
573+
* that CRM metadata produces correct $filter query parameters.
574+
*/
575+
function lookupFiltersToRecord(
576+
filters: Array<{ field: string; operator: string; value: unknown }>,
577+
): Record<string, any> {
578+
const result: Record<string, any> = {};
579+
for (const f of filters) {
580+
switch (f.operator) {
581+
case 'eq': result[f.field] = f.value; break;
582+
case 'ne': result[f.field] = { $ne: f.value }; break;
583+
case 'gt': result[f.field] = { $gt: f.value }; break;
584+
case 'lt': result[f.field] = { $lt: f.value }; break;
585+
case 'gte': result[f.field] = { $gte: f.value }; break;
586+
case 'lte': result[f.field] = { $lte: f.value }; break;
587+
case 'contains': result[f.field] = { $contains: f.value }; break;
588+
case 'in': result[f.field] = { $in: f.value }; break;
589+
case 'notIn': result[f.field] = { $nin: f.value }; break;
590+
}
591+
}
592+
return result;
593+
}
594+
595+
/**
596+
* Simulate LookupField's mapFieldTypeToFilterType conversion.
597+
* This mirrors the internal function in LookupField.tsx to verify
598+
* CRM lookup_columns produce valid filter bar configurations.
599+
*/
600+
function mapFieldTypeToFilterType(fieldType: string): string | undefined {
601+
const mapping: Record<string, string> = {
602+
text: 'text', number: 'number', currency: 'number',
603+
percent: 'number', select: 'select', status: 'select',
604+
date: 'date', datetime: 'date', boolean: 'boolean',
605+
};
606+
return mapping[fieldType];
607+
}
608+
609+
it('account.owner lookup_filters produce correct $filter for active users', () => {
610+
const owner = (AccountObject.fields as any).owner;
611+
const $filter = lookupFiltersToRecord(owner.lookup_filters);
612+
expect($filter).toEqual({ active: true });
613+
});
614+
615+
it('contact.account lookup_filters produce $in for type restriction', () => {
616+
const account = (ContactObject.fields as any).account;
617+
const $filter = lookupFiltersToRecord(account.lookup_filters);
618+
expect($filter).toEqual({ type: { $in: ['Customer', 'Partner'] } });
619+
});
620+
621+
it('order_item.order lookup_filters produce $ne for cancelled exclusion', () => {
622+
const order = (OrderItemObject.fields as any).order;
623+
const $filter = lookupFiltersToRecord(order.lookup_filters);
624+
expect($filter).toEqual({ status: { $ne: 'cancelled' } });
625+
});
626+
627+
it('opportunity_contact.opportunity lookup_filters produce $nin for closed stages', () => {
628+
const opp = (OpportunityContactObject.fields as any).opportunity;
629+
const $filter = lookupFiltersToRecord(opp.lookup_filters);
630+
expect($filter).toEqual({ stage: { $nin: ['closed_won', 'closed_lost'] } });
631+
});
632+
633+
it('typed lookup_columns produce valid filter bar configurations', () => {
634+
const product = (OrderItemObject.fields as any).product;
635+
const cols = product.lookup_columns as Array<{ field: string; type?: string; label?: string }>;
636+
637+
const filterColumns = cols
638+
.filter((c) => c.type)
639+
.map((c) => ({
640+
field: c.field,
641+
label: c.label,
642+
type: mapFieldTypeToFilterType(c.type!),
643+
}))
644+
.filter((c) => c.type !== undefined);
645+
646+
// Product lookup should produce filter bar entries for select, currency(→number), number, boolean
647+
expect(filterColumns.length).toBeGreaterThanOrEqual(3);
648+
const types = filterColumns.map((c) => c.type);
649+
expect(types).toContain('select'); // category
650+
expect(types).toContain('number'); // price, stock
651+
expect(types).toContain('boolean'); // is_active
652+
});
653+
654+
it('opportunity.account typed columns map to valid filter bar types', () => {
655+
const account = (OpportunityObject.fields as any).account;
656+
const cols = account.lookup_columns as Array<{ field: string; type?: string }>;
657+
658+
const filterTypes = cols
659+
.filter((c) => c.type)
660+
.map((c) => mapFieldTypeToFilterType(c.type!))
661+
.filter(Boolean);
662+
663+
// account columns have select + currency(→number) types
664+
expect(filterTypes).toContain('select');
665+
expect(filterTypes).toContain('number');
666+
});
667+
668+
it('all CRM lookup_filters convert to valid $filter records without errors', () => {
669+
for (const obj of allObjects) {
670+
const lookups = Object.entries(obj.fields).filter(
671+
([, f]: [string, any]) => f.type === 'lookup' || f.type === 'master_detail',
672+
);
673+
for (const [fieldName, field] of lookups) {
674+
const f = field as any;
675+
if (!f.lookup_filters) continue;
676+
const $filter = lookupFiltersToRecord(f.lookup_filters);
677+
expect(
678+
Object.keys($filter).length,
679+
`${obj.name}.${fieldName} lookup_filters produced empty $filter`,
680+
).toBeGreaterThan(0);
681+
}
682+
}
683+
});
684+
});
564685
});
565686

566687
// ====================================================================

0 commit comments

Comments
 (0)