Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@
]);

const listConditionalOptions = $derived([
{ value: 'in', label: 'In' },
{ value: 'in', label: translate('common.in') },
{ value: 'not in', label: translate('common.not-in') },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is already working and you're just adding it to the FE? All the other files are tests.

{ value: '=', label: translate('common.equal-to') },
{ value: '!=', label: translate('common.not-equal-to') },
...defaultConditionOptions,
Expand Down
2 changes: 2 additions & 0 deletions src/lib/i18n/locales/en/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export const Strings = {
before: 'Before',
between: 'Between',
'in-last': 'In Last',
in: 'In',
'not-in': 'Not In',
'starts-with': 'Starts with',
equals: 'Equals',
'greater-than': 'Greater Than',
Expand Down
40 changes: 40 additions & 0 deletions src/lib/utilities/query/filter-workflow-query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,4 +364,44 @@ describe('toListWorkflowQueryFromFilters', () => {
'`CustomKeywordListField`in("Hello", "World") AND `CustomKeywordListField` is null AND `CustomKeywordListField`="Hello"',
);
});

it('should convert a KeywordList filter with a not in conditional', () => {
const filters = [
{
attribute: 'CustomKeywordListField',
type: 'KeywordList',
conditional: 'not in',
operator: '',
parenthesis: '',
value: '("Hello", "World")',
},
];
const query = toListWorkflowQueryFromFilters(filters);
expect(query).toBe('`CustomKeywordListField`not in("Hello", "World")');
});

it('should combine a not in KeywordList filter with another filter', () => {
const filters = [
{
attribute: 'CustomKeywordListField',
type: 'KeywordList',
conditional: 'not in',
operator: '',
parenthesis: '',
value: '("Hello", "World")',
},
{
attribute: 'WorkflowType',
type: 'Keyword',
conditional: '=',
operator: '',
parenthesis: '',
value: 'ExampleWorkflow',
},
];
const query = toListWorkflowQueryFromFilters(combineFilters(filters));
expect(query).toBe(
'`CustomKeywordListField`not in("Hello", "World") AND `WorkflowType`="ExampleWorkflow"',
);
});
});
43 changes: 43 additions & 0 deletions src/lib/utilities/query/to-list-workflow-filters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const prefixQuery = '`WorkflowType` STARTS_WITH "hello"';
const isEmptyQuery = '`WorkflowType` is null';
const isNotEmptyQuery = '`StartTime` IS NOT NULL';
const keywordListQuery = '`CustomKeywordListField`in("Hello", "World")';
const keywordListNotInQuery =
'`CustomKeywordListField`not in("Hello", "World")';

const attributes = {
CloseTime: 'Datetime',
Expand Down Expand Up @@ -320,6 +322,47 @@ describe('toListWorkflowFilters', () => {
expect(result).toMatchObject(expectedFilters);
});

it('should parse a query with a KeywordList not in conditional', () => {
const result = toListWorkflowFilters(keywordListNotInQuery, attributes);
const expectedFilters = [
{
attribute: 'CustomKeywordListField',
type: 'KeywordList',
conditional: 'not in',
operator: '',
parenthesis: '',
value: '("Hello", "World")',
},
];
expect(result).toMatchObject(expectedFilters);
});

it('should parse a KeywordList not in conditional joined to another filter', () => {
const result = toListWorkflowFilters(
keywordListNotInQuery + ' AND `WorkflowType`="ExampleWorkflow"',
attributes,
);
const expectedFilters = [
{
attribute: 'CustomKeywordListField',
type: 'KeywordList',
conditional: 'not in',
operator: 'AND',
parenthesis: '',
value: '("Hello", "World")',
},
{
attribute: 'WorkflowType',
type: 'Keyword',
conditional: '=',
operator: '',
parenthesis: '',
value: 'ExampleWorkflow',
},
];
expect(result).toMatchObject(expectedFilters);
});

it('should parse a query with a KeywordList type and other types', () => {
const result = toListWorkflowFilters(
keywordListQuery + ' AND ' + workflowQuery4 + ' AND ' + keywordListQuery,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { expect, test } from '@playwright/test';
import { expect, type Page, test } from '@playwright/test';

import { SEARCH_ATTRIBUTE_TYPE } from '$src/lib/types/workflows';
import {
mockClusterApi,
mockWorkflowApis,
mockWorkflowsApis,
waitForWorkflowsApis,
} from '~/test-utilities/mock-apis';
import { mockSearchAttributesApi } from '~/test-utilities/mocks/search-attributes';

test.beforeEach(async ({ page }) => {
await mockWorkflowsApis(page);
Expand Down Expand Up @@ -304,3 +306,69 @@ test('it should resync the filter pills on back and forward navigation', async (
page.getByRole('button', { name: 'HistoryLength = 10' }),
).toBeVisible();
});

test.describe('KeywordList search attributes', () => {
const NOT_IN_QUERY = '`CustomKeywordListField`not in("Hello", "World")';
const NOT_IN_CHIP = 'CustomKeywordListField not in ("Hello", "World")';

test.beforeEach(async ({ page }) => {
await mockSearchAttributesApi(page, {
customAttributes: {
CustomKeywordListField: SEARCH_ATTRIBUTE_TYPE.KEYWORDLIST,
},
});

await page.reload();
await waitForWorkflowsApis(page);
});

const addKeywordListFilter = async (page: Page, conditional: string) => {
await page.getByTestId('add-filter-button').click();
await page
.getByRole('menuitem', { name: 'CustomKeywordListField KeywordList' })
.click();

await page.getByRole('button', { name: conditional, exact: true }).click();

const input = page.locator('#list-filter');
await input.fill('Hello');
await input.press('Enter');
await input.fill('World');
await input.press('Enter');

await page.getByTestId('apply-filter-button').click();
};

test('it should filter a KeywordList with the not in conditional', async ({
page,
}) => {
await addKeywordListFilter(page, 'Not In');

await expect.poll(() => getQueryParam(page.url())).toBe(NOT_IN_QUERY);
await expect(page.getByRole('button', { name: NOT_IN_CHIP })).toBeVisible();
});

test('it should still filter a KeywordList with the in conditional', async ({
page,
}) => {
await addKeywordListFilter(page, 'In');

await expect
.poll(() => getQueryParam(page.url()))
.toBe('`CustomKeywordListField`in("Hello", "World")');
});

test('it should parse a not in KeywordList query back into a filter', async ({
page,
}) => {
await page.getByTestId('toggle-manual-query').click();
await page.getByTestId('workflow-manual-search-input').fill(NOT_IN_QUERY);
await page.getByTestId('workflow-manual-search-button').click();

await expect.poll(() => getQueryParam(page.url())).toBe(NOT_IN_QUERY);

await page.getByTestId('toggle-manual-query').click();

await expect(page.getByRole('button', { name: NOT_IN_CHIP })).toBeVisible();
});
});
Loading