diff --git a/frontend/src/helpers/filters.test.ts b/frontend/src/helpers/filters.test.ts index 2fc095521..72d3946ab 100644 --- a/frontend/src/helpers/filters.test.ts +++ b/frontend/src/helpers/filters.test.ts @@ -187,6 +187,26 @@ describe('Filter Transformation', () => { expect(transformed).toBe('project = 1') }) + + it('should correctly resolve project in parentheses', () => { + const transformed = transformFilterStringForApi( + '( project = Filtertest )', + nullTitleToIdResolver, + (title: string) => title === 'Filtertest' ? 123 : null, + ) + + expect(transformed).toBe('( project = 123 )') + }) + + it('should correctly resolve project with OR in parentheses', () => { + const transformed = transformFilterStringForApi( + '( labels = label || project = Filtertest )', + (title: string) => title === 'label' ? 456 : null, + (title: string) => title === 'Filtertest' ? 123 : null, + ) + + expect(transformed).toBe('( labels = 456 || project = 123 )') + }) }) describe('Special Characters', () => { diff --git a/frontend/src/helpers/filters.ts b/frontend/src/helpers/filters.ts index 4925488a4..b94855f38 100644 --- a/frontend/src/helpers/filters.ts +++ b/frontend/src/helpers/filters.ts @@ -81,7 +81,7 @@ export function hasFilterQuery(filter: string): boolean { } export function getFilterFieldRegexPattern(field: string): RegExp { - return new RegExp('\\b(' + field + ')\\s*' + FILTER_OPERATORS_REGEX + '\\s*(?:(["\'])((?:\\\\.|(?!\\3)[^\\\\])*?)\\3|([^&|()<]+?))(?=\\s*(?:&&|\\||$))', 'g') + return new RegExp('\\b(' + field + ')\\s*' + FILTER_OPERATORS_REGEX + '\\s*(?:(["\'])((?:\\\\.|(?!\\3)[^\\\\])*?)\\3|([^&|()<]+?))(?=\\s*(?:&&|\\||\\)|$))', 'g') } export function transformFilterStringForApi( diff --git a/pkg/models/task_collection_filter_test.go b/pkg/models/task_collection_filter_test.go index 06819de21..e8954e752 100644 --- a/pkg/models/task_collection_filter_test.go +++ b/pkg/models/task_collection_filter_test.go @@ -284,4 +284,31 @@ func TestParseFilter(t *testing.T) { assert.Equal(t, 0, date.Year()) } }) + t.Run("project with parentheses", func(t *testing.T) { + result, err := getTaskFiltersFromFilterString("( project = 1 )", "UTC") + + require.NoError(t, err) + require.Len(t, result, 1) + require.Len(t, result[0].value, 1) + + firstSet := result[0].value.([]*taskFilter) + assert.Equal(t, "project_id", firstSet[0].field) + assert.Equal(t, taskFilterComparatorEquals, firstSet[0].comparator) + assert.Equal(t, int64(1), firstSet[0].value) + }) + t.Run("project with OR in parentheses", func(t *testing.T) { + result, err := getTaskFiltersFromFilterString("( done = false || project = 1 )", "UTC") + + require.NoError(t, err) + require.Len(t, result, 1) + require.Len(t, result[0].value, 2) + + firstSet := result[0].value.([]*taskFilter) + assert.Equal(t, "done", firstSet[0].field) + assert.Equal(t, taskFilterComparatorEquals, firstSet[0].comparator) + assert.Equal(t, false, firstSet[0].value) + assert.Equal(t, "project_id", firstSet[1].field) + assert.Equal(t, taskFilterComparatorEquals, firstSet[1].comparator) + assert.Equal(t, int64(1), firstSet[1].value) + }) }