fix: Incorrect date parsing in matchesFilter (#11679)
The `matchesFilter` is a utility that checks the incoming payload against a filter and returns `true` or `false`. For the `greater_than` and `less_than` filter specifically, the date parsing would fail when the timestamp was a 10 digit number. This PR solves this by adding a `coerceToDate` method that tries to parse the given value to a Date object as correctly as possible before comparing. Ref: https://github.com/chatwoot/utils/pull/53
This commit is contained in:
@@ -463,6 +463,241 @@ describe('filterHelpers', () => {
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test conversation with 10-digit timestamp (seconds) vs standard date filter
|
||||
it('should match conversation with 10-digit timestamp against date string filter', () => {
|
||||
const conversation = { created_at: 1647777600 }; // March 20, 2022 in seconds (10 digits)
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19', // Standard YYYY-MM-DD format
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test conversation with 13-digit timestamp (milliseconds) vs standard date filter
|
||||
it('should match conversation with 13-digit timestamp against date string filter', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022 in milliseconds (13 digits)
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19', // Standard YYYY-MM-DD format
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test conversation with string timestamp vs standard date filter
|
||||
it('should match conversation with string 10-digit timestamp against date string filter', () => {
|
||||
const conversation = { created_at: '1647777600' }; // March 20, 2022 as string (10 digits)
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19', // Standard YYYY-MM-DD format
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test conversation with string 13-digit timestamp vs standard date filter
|
||||
it('should match conversation with string 13-digit timestamp against date string filter', () => {
|
||||
const conversation = { created_at: '1647777600000' }; // March 20, 2022 as string (13 digits)
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19', // Standard YYYY-MM-DD format
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test conversation with mixed format vs standard date filter with time
|
||||
it('should match conversation with numeric timestamp against ISO date string filter', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022 12:00:00 GMT (numeric)
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19T10:30:00Z', // Standard ISO format from filter
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test parseDate with date string without time (should default to 00:00:00)
|
||||
it('should match conversation with is_greater_than operator using date string without time', () => {
|
||||
const conversation = { created_at: 1647820800000 }; // March 21, 2022 00:00:00 GMT
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-20', // March 20, 2022 (should become 00:00:00)
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test parseDate with ISO date string
|
||||
it('should match conversation with is_greater_than operator using ISO date string', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19T00:00:00.000Z', // March 19, 2022 ISO format
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test parseDate with null/undefined values
|
||||
it('should handle null filter values in date comparison', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: null,
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle undefined filter values in date comparison', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: undefined,
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(false);
|
||||
});
|
||||
|
||||
// Test parseDate with invalid date strings
|
||||
it('should handle invalid date strings in date comparison', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: 'invalid-date-string',
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle non-date string values in date comparison', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: 'not-a-date',
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(false);
|
||||
});
|
||||
|
||||
// Test is_less_than with various date formats
|
||||
it('should match conversation with is_less_than operator using numeric timestamp', () => {
|
||||
const conversation = { created_at: 1647691200000 }; // March 19, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_less_than',
|
||||
values: 1647777600, // March 20, 2022 as 10-digit timestamp
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
it('should not match conversation with is_less_than operator when date is later', () => {
|
||||
const conversation = { created_at: 1647864000000 }; // March 21, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_less_than',
|
||||
values: '2022-03-20T12:00:00Z', // March 20, 2022 with time
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(false);
|
||||
});
|
||||
|
||||
// Edge case: Test with conversation having string timestamp
|
||||
it('should handle conversation with string timestamp value', () => {
|
||||
const conversation = { created_at: '1647777600000' }; // March 20, 2022 as string
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19',
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Edge case: Test with conversation having 10-digit timestamp
|
||||
it('should handle conversation with 10-digit timestamp value', () => {
|
||||
const conversation = { created_at: 1647777600 }; // March 20, 2022 as seconds (10 digits)
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19',
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test date string with different time formats
|
||||
it('should handle date string with space-separated time', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19 10:30:00', // Date with space-separated time
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test parseDate with object input (should return null and fail comparison)
|
||||
it('should handle non-string, non-number filter values', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: { date: '2022-03-19' }, // Object instead of string/number
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(false);
|
||||
});
|
||||
|
||||
describe('days_before operator', () => {
|
||||
beforeEach(() => {
|
||||
// Set the date to March 25, 2022
|
||||
|
||||
Reference in New Issue
Block a user