mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
fix: Fix flaky E2E tests (#2013)
## Summary This PR reduces flakiness in some E2E tests by 1. Updating Search filters to have test ids which are specific to both the column/key and the value, since identical values across columns caused strict mode failures 2. Updating the Saved Search tests to use more web first assertions ### Screenshots or video ### How to test locally or on Vercel ### References - Linear Issue: - Related PRs:
This commit is contained in:
parent
e6a0455aa5
commit
853da16ad3
6 changed files with 140 additions and 93 deletions
5
.changeset/shiny-cats-design.md
Normal file
5
.changeset/shiny-cats-design.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@hyperdx/app": patch
|
||||
---
|
||||
|
||||
fix: Fix flaky E2E tests
|
||||
|
|
@ -100,6 +100,7 @@ export function cleanedFacetName(key: string): string {
|
|||
}
|
||||
|
||||
type FilterCheckboxProps = {
|
||||
columnName: string;
|
||||
label: string;
|
||||
value?: 'included' | 'excluded' | false;
|
||||
pinned: boolean;
|
||||
|
|
@ -160,6 +161,7 @@ const FilterPercentage = ({ percentage, isLoading }: FilterPercentageProps) => {
|
|||
};
|
||||
|
||||
const FilterCheckbox = ({
|
||||
columnName,
|
||||
value,
|
||||
label,
|
||||
pinned,
|
||||
|
|
@ -171,10 +173,11 @@ const FilterCheckbox = ({
|
|||
percentage,
|
||||
isPercentageLoading,
|
||||
}: FilterCheckboxProps) => {
|
||||
const testIdPrefix = `filter-checkbox-${columnName}-${label}`;
|
||||
return (
|
||||
<div
|
||||
className={cx(classes.filterCheckbox, className)}
|
||||
data-testid={`filter-checkbox-${label}`}
|
||||
data-testid={testIdPrefix}
|
||||
>
|
||||
<Group
|
||||
gap={8}
|
||||
|
|
@ -189,7 +192,7 @@ const FilterCheckbox = ({
|
|||
// taken care by the onClick in the group
|
||||
}}
|
||||
indeterminate={value === 'excluded'}
|
||||
data-testid={`filter-checkbox-input-${label}`}
|
||||
data-testid={`${testIdPrefix}-input`}
|
||||
/>
|
||||
<Tooltip
|
||||
openDelay={label.length > 22 ? 0 : 1500}
|
||||
|
|
@ -234,14 +237,14 @@ const FilterCheckbox = ({
|
|||
<TextButton
|
||||
onClick={onClickOnly}
|
||||
label="Only"
|
||||
data-testid={`filter-only-${label}`}
|
||||
data-testid={`${testIdPrefix}-only`}
|
||||
/>
|
||||
)}
|
||||
{onClickExclude && (
|
||||
<TextButton
|
||||
onClick={onClickExclude}
|
||||
label="Exclude"
|
||||
data-testid={`filter-exclude-${label}`}
|
||||
data-testid={`${testIdPrefix}-exclude`}
|
||||
/>
|
||||
)}
|
||||
<ActionIcon
|
||||
|
|
@ -252,14 +255,14 @@ const FilterCheckbox = ({
|
|||
aria-label={pinned ? 'Unpin field' : 'Pin field'}
|
||||
role="checkbox"
|
||||
aria-checked={pinned}
|
||||
data-testid={`filter-pin-${label}`}
|
||||
data-testid={`${testIdPrefix}-pin`}
|
||||
>
|
||||
{pinned ? <IconPinFilled size={12} /> : <IconPin size={12} />}
|
||||
</ActionIcon>
|
||||
</div>
|
||||
{pinned && (
|
||||
<Center me="1px">
|
||||
<IconPinFilled size={12} data-testid={`filter-pin-${label}-pinned`} />
|
||||
<IconPinFilled size={12} data-testid={`${testIdPrefix}-pin-pinned`} />
|
||||
</Center>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -755,6 +758,7 @@ export const FilterGroup = ({
|
|||
{displayedOptions.map(option => (
|
||||
<FilterCheckbox
|
||||
key={option.value.toString()}
|
||||
columnName={name}
|
||||
label={option.label}
|
||||
pinned={isPinned(option.value)}
|
||||
className={
|
||||
|
|
|
|||
|
|
@ -411,7 +411,7 @@ describe('FilterGroup', () => {
|
|||
it('should sort options alphabetically by default', () => {
|
||||
renderWithMantine(<FilterGroup {...defaultProps} />);
|
||||
|
||||
const options = screen.getAllByTestId(/filter-checkbox-input/);
|
||||
const options = screen.getAllByTestId(/filter-checkbox-.+-input/);
|
||||
expect(options).toHaveLength(3);
|
||||
const labels = screen.getAllByText(/apple|banana|zebra/);
|
||||
expect(labels[0]).toHaveTextContent('apple');
|
||||
|
|
@ -430,7 +430,7 @@ describe('FilterGroup', () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
const options = screen.getAllByTestId(/filter-checkbox-input/);
|
||||
const options = screen.getAllByTestId(/filter-checkbox-.+-input/);
|
||||
expect(options).toHaveLength(3);
|
||||
const labels = screen.getAllByText(/apple|banana|zebra/);
|
||||
expect(labels[0]).toHaveTextContent('apple');
|
||||
|
|
@ -459,7 +459,7 @@ describe('FilterGroup', () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
const options = screen.getAllByTestId(/filter-checkbox-input/);
|
||||
const options = screen.getAllByTestId(/filter-checkbox-.+-input/);
|
||||
expect(options).toHaveLength(3);
|
||||
const labels = screen.getAllByText(/apple|banana|zebra/);
|
||||
expect(labels[0]).toHaveTextContent('banana'); // Selected
|
||||
|
|
@ -492,7 +492,7 @@ describe('FilterGroup', () => {
|
|||
);
|
||||
await userEvent.click(showPercentages);
|
||||
|
||||
const options = screen.getAllByTestId(/filter-checkbox-input/);
|
||||
const options = screen.getAllByTestId(/filter-checkbox-.+-input/);
|
||||
expect(options).toHaveLength(3);
|
||||
const labels = screen.getAllByText(/%/);
|
||||
expect(labels[0]).toHaveTextContent('~99%'); // apple
|
||||
|
|
@ -511,7 +511,7 @@ describe('FilterGroup', () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
const options = screen.getAllByTestId(/filter-checkbox-input/);
|
||||
const options = screen.getAllByTestId(/filter-checkbox-.+-input/);
|
||||
expect(options).toHaveLength(3);
|
||||
const labels = screen.getAllByText(/apple|banana|zebra/);
|
||||
expect(labels[0]).toHaveTextContent('apple'); // included first
|
||||
|
|
@ -542,7 +542,7 @@ describe('FilterGroup', () => {
|
|||
);
|
||||
|
||||
// Should show MAX_FILTER_GROUP_ITEMS (10) by default
|
||||
let options = screen.getAllByTestId(/filter-checkbox-input/);
|
||||
let options = screen.getAllByTestId(/filter-checkbox-.+-input/);
|
||||
expect(options).toHaveLength(10);
|
||||
|
||||
// Selected items should be visible even if they would be beyond MAX_FILTER_GROUP_ITEMS
|
||||
|
|
@ -555,7 +555,7 @@ describe('FilterGroup', () => {
|
|||
await userEvent.click(showMoreButton);
|
||||
|
||||
// Should show all items
|
||||
options = screen.getAllByTestId(/filter-checkbox-input/);
|
||||
options = screen.getAllByTestId(/filter-checkbox-.+-input/);
|
||||
expect(options).toHaveLength(15);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -49,50 +49,59 @@ export class FilterComponent {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get checkbox for a specific filter value
|
||||
* Get checkbox for a specific filter value within a column
|
||||
* @param columnName - e.g., 'ServiceName', 'SeverityText'
|
||||
* @param valueName - e.g., 'info', 'error', 'debug'
|
||||
*/
|
||||
getFilterCheckbox(valueName: string) {
|
||||
return this.page.getByTestId(`filter-checkbox-${valueName}`);
|
||||
getFilterCheckbox(columnName: string, valueName: string) {
|
||||
return this.page.getByTestId(`filter-checkbox-${columnName}-${valueName}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get checkbox input element
|
||||
* Get checkbox input element within a column
|
||||
*/
|
||||
getFilterCheckboxInput(valueName: string) {
|
||||
return this.page.getByTestId(`filter-checkbox-input-${valueName}`);
|
||||
getFilterCheckboxInput(columnName: string, valueName: string) {
|
||||
return this.page.getByTestId(
|
||||
`filter-checkbox-${columnName}-${valueName}-input`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply/select a filter value
|
||||
*/
|
||||
async applyFilter(valueName: string) {
|
||||
const checkbox = this.getFilterCheckbox(valueName);
|
||||
async applyFilter(columnName: string, valueName: string) {
|
||||
const checkbox = this.getFilterCheckbox(columnName, valueName);
|
||||
await checkbox.click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude a filter value (invert the filter)
|
||||
*/
|
||||
async excludeFilter(valueName: string) {
|
||||
const filterCheckbox = this.getFilterCheckbox(valueName);
|
||||
await this.scrollAndClick(filterCheckbox, `filter-exclude-${valueName}`);
|
||||
async excludeFilter(columnName: string, valueName: string) {
|
||||
const filterCheckbox = this.getFilterCheckbox(columnName, valueName);
|
||||
await this.scrollAndClick(
|
||||
filterCheckbox,
|
||||
`filter-checkbox-${columnName}-${valueName}-exclude`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pin a filter value to persist it
|
||||
*/
|
||||
async pinFilter(valueName: string) {
|
||||
const filterCheckbox = this.getFilterCheckbox(valueName);
|
||||
await this.scrollAndClick(filterCheckbox, `filter-pin-${valueName}`);
|
||||
async pinFilter(columnName: string, valueName: string) {
|
||||
const filterCheckbox = this.getFilterCheckbox(columnName, valueName);
|
||||
await this.scrollAndClick(
|
||||
filterCheckbox,
|
||||
`filter-checkbox-${columnName}-${valueName}-pin`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear/unselect a filter
|
||||
*/
|
||||
async clearFilter(valueName: string) {
|
||||
const input = this.getFilterCheckboxInput(valueName);
|
||||
const checkbox = this.getFilterCheckbox(valueName);
|
||||
async clearFilter(columnName: string, valueName: string) {
|
||||
const input = this.getFilterCheckboxInput(columnName, valueName);
|
||||
const checkbox = this.getFilterCheckbox(columnName, valueName);
|
||||
await checkbox.click();
|
||||
await input.click();
|
||||
}
|
||||
|
|
@ -160,8 +169,11 @@ export class FilterComponent {
|
|||
/**
|
||||
* Check if filter checkbox is indeterminate (excluded state)
|
||||
*/
|
||||
async isFilterExcluded(valueName: string): Promise<boolean> {
|
||||
const input = this.getFilterCheckboxInput(valueName);
|
||||
async isFilterExcluded(
|
||||
columnName: string,
|
||||
valueName: string,
|
||||
): Promise<boolean> {
|
||||
const input = this.getFilterCheckboxInput(columnName, valueName);
|
||||
const indeterminate = await input.getAttribute('data-indeterminate');
|
||||
return indeterminate === 'true';
|
||||
}
|
||||
|
|
@ -170,7 +182,9 @@ export class FilterComponent {
|
|||
* Get all filter values for a specific filter group
|
||||
*/
|
||||
getFilterValues(filterGroupName: string) {
|
||||
return this.page.getByTestId(`filter-checkbox-${filterGroupName}`);
|
||||
return this.page.getByTestId(
|
||||
new RegExp(`^filter-checkbox-${filterGroupName}-`),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -223,7 +237,9 @@ export class FilterComponent {
|
|||
// Wait for initial facet options to load
|
||||
const group = this.getFilterGroup(filterGroupName);
|
||||
await group
|
||||
.locator('[data-testid^="filter-checkbox-input-"]')
|
||||
.locator(
|
||||
`[data-testid^="filter-checkbox-${filterGroupName}-"][data-testid$="-input"]`,
|
||||
)
|
||||
.first()
|
||||
.waitFor({ state: 'visible', timeout: 10000 });
|
||||
|
||||
|
|
@ -232,7 +248,7 @@ export class FilterComponent {
|
|||
const visible: string[] = [];
|
||||
for (const value of candidates) {
|
||||
if (visible.length >= count) break;
|
||||
const input = this.getFilterCheckboxInput(value);
|
||||
const input = this.getFilterCheckboxInput(filterGroupName, value);
|
||||
if (await input.isVisible()) visible.push(value);
|
||||
}
|
||||
if (visible.length < count) {
|
||||
|
|
|
|||
|
|
@ -65,9 +65,9 @@ test.describe('Saved Search Functionality', () => {
|
|||
|
||||
await test.step('Verify custom SELECT is preserved', async () => {
|
||||
const selectEditor = searchPage.getSELECTEditor();
|
||||
const selectContent = await selectEditor.textContent();
|
||||
|
||||
expect(selectContent).toContain('upper(ServiceName) as service_name');
|
||||
await expect(selectEditor).toContainText(
|
||||
'upper(ServiceName) as service_name',
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
@ -107,10 +107,10 @@ test.describe('Saved Search Functionality', () => {
|
|||
|
||||
await test.step('Verify different source has its own default SELECT', async () => {
|
||||
const selectEditor = searchPage.getSELECTEditor();
|
||||
const selectContent = await selectEditor.textContent();
|
||||
|
||||
expect(selectContent).not.toContain('lower(Body) as body_lower');
|
||||
expect(selectContent).toMatch(/Timestamp/i);
|
||||
await expect(selectEditor).not.toContainText(
|
||||
'lower(Body) as body_lower',
|
||||
);
|
||||
await expect(selectEditor).toContainText('Timestamp');
|
||||
});
|
||||
|
||||
await test.step('Navigate back to saved search', async () => {
|
||||
|
|
@ -120,11 +120,11 @@ test.describe('Saved Search Functionality', () => {
|
|||
|
||||
await test.step('Verify saved search SELECT is restored', async () => {
|
||||
const selectEditor = searchPage.getSELECTEditor();
|
||||
const selectContent = await selectEditor.textContent();
|
||||
|
||||
// Verifies the fix: SELECT restores to saved search's custom value
|
||||
expect(selectContent).toContain('lower(Body) as body_lower');
|
||||
expect(selectContent).toContain('Timestamp, Body, lower(Body)');
|
||||
await expect(selectEditor).toContainText('lower(Body) as body_lower');
|
||||
await expect(selectEditor).toContainText(
|
||||
'Timestamp, Body, lower(Body)',
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
@ -153,12 +153,10 @@ test.describe('Saved Search Functionality', () => {
|
|||
|
||||
await test.step('Verify SELECT changed to the new source default', async () => {
|
||||
const selectEditor = searchPage.getSELECTEditor();
|
||||
const selectContent = await selectEditor.textContent();
|
||||
|
||||
expect(selectContent).not.toContain(
|
||||
await expect(selectEditor).not.toContainText(
|
||||
'lower(ServiceName) as service_name',
|
||||
);
|
||||
expect(selectContent).toMatch(/Timestamp/i);
|
||||
await expect(selectEditor).toContainText('Timestamp');
|
||||
});
|
||||
|
||||
await test.step('Switch back to original source via dropdown', async () => {
|
||||
|
|
@ -170,9 +168,9 @@ test.describe('Saved Search Functionality', () => {
|
|||
|
||||
await test.step('Verify SELECT is search custom SELECT', async () => {
|
||||
const selectEditor = searchPage.getSELECTEditor();
|
||||
const selectContent = await selectEditor.textContent();
|
||||
|
||||
expect(selectContent).toContain('lower(ServiceName) as service_name');
|
||||
await expect(selectEditor).toContainText(
|
||||
'lower(ServiceName) as service_name',
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
@ -240,8 +238,7 @@ test.describe('Saved Search Functionality', () => {
|
|||
|
||||
// Verify ORDER BY is restored
|
||||
const orderByEditor = searchPage.getOrderByEditor();
|
||||
const orderByContent = await orderByEditor.textContent();
|
||||
expect(orderByContent).toContain('ServiceName ASC');
|
||||
await expect(orderByEditor).toContainText('ServiceName ASC');
|
||||
|
||||
// Verify search results are visible (search executed automatically)
|
||||
await searchPage.table.waitForRowsToPopulate();
|
||||
|
|
@ -295,10 +292,10 @@ test.describe('Saved Search Functionality', () => {
|
|||
|
||||
// Verify SELECT content
|
||||
const selectEditor = searchPage.getSELECTEditor();
|
||||
const selectContent = await selectEditor.textContent();
|
||||
|
||||
expect(selectContent).toContain('upper(ServiceName) as service_name');
|
||||
expect(selectContent).toContain('Timestamp, Body');
|
||||
await expect(selectEditor).toContainText(
|
||||
'upper(ServiceName) as service_name',
|
||||
);
|
||||
await expect(selectEditor).toContainText('Timestamp, Body');
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
@ -320,8 +317,7 @@ test.describe('Saved Search Functionality', () => {
|
|||
|
||||
await test.step('Verify default SELECT is loaded', async () => {
|
||||
const selectEditor = searchPage.getSELECTEditor();
|
||||
const selectContent = await selectEditor.textContent();
|
||||
expect(selectContent).toContain(
|
||||
await expect(selectEditor).toContainText(
|
||||
'Timestamp, ServiceName, SeverityText, Body',
|
||||
);
|
||||
});
|
||||
|
|
@ -338,8 +334,7 @@ test.describe('Saved Search Functionality', () => {
|
|||
|
||||
await test.step('Verify default SELECT is loaded', async () => {
|
||||
const selectEditor = searchPage.getSELECTEditor();
|
||||
const selectContent = await selectEditor.textContent();
|
||||
expect(selectContent).toContain(
|
||||
await expect(selectEditor).toContainText(
|
||||
'Timestamp, ServiceName, SeverityText, Body',
|
||||
);
|
||||
});
|
||||
|
|
@ -484,11 +479,16 @@ test.describe('Saved Search Functionality', () => {
|
|||
appliedFilterValue = picked;
|
||||
|
||||
// Apply the filter
|
||||
await searchPage.filters.applyFilter(appliedFilterValue);
|
||||
await searchPage.filters.applyFilter(
|
||||
'SeverityText',
|
||||
appliedFilterValue,
|
||||
);
|
||||
|
||||
// Verify filter is checked
|
||||
const filterInput =
|
||||
searchPage.filters.getFilterCheckboxInput(appliedFilterValue);
|
||||
const filterInput = searchPage.filters.getFilterCheckboxInput(
|
||||
'SeverityText',
|
||||
appliedFilterValue,
|
||||
);
|
||||
await expect(filterInput).toBeChecked();
|
||||
|
||||
// Submit search to apply filters
|
||||
|
|
@ -516,8 +516,10 @@ test.describe('Saved Search Functionality', () => {
|
|||
await searchPage.filters.openFilterGroup('SeverityText');
|
||||
|
||||
// Verify filter is not checked
|
||||
const filterInput =
|
||||
searchPage.filters.getFilterCheckboxInput(appliedFilterValue);
|
||||
const filterInput = searchPage.filters.getFilterCheckboxInput(
|
||||
'SeverityText',
|
||||
appliedFilterValue,
|
||||
);
|
||||
await expect(filterInput).not.toBeChecked();
|
||||
});
|
||||
|
||||
|
|
@ -532,8 +534,10 @@ test.describe('Saved Search Functionality', () => {
|
|||
await searchPage.filters.openFilterGroup('SeverityText');
|
||||
|
||||
// Verify filter is checked again
|
||||
const filterInput =
|
||||
searchPage.filters.getFilterCheckboxInput(appliedFilterValue);
|
||||
const filterInput = searchPage.filters.getFilterCheckboxInput(
|
||||
'SeverityText',
|
||||
appliedFilterValue,
|
||||
);
|
||||
await expect(filterInput).toBeChecked();
|
||||
});
|
||||
},
|
||||
|
|
@ -565,7 +569,7 @@ test.describe('Saved Search Functionality', () => {
|
|||
|
||||
await test.step('Create saved search with one filter', async () => {
|
||||
await searchPage.filters.openFilterGroup(firstFilterGroup);
|
||||
await searchPage.filters.applyFilter(firstFilter);
|
||||
await searchPage.filters.applyFilter(firstFilterGroup, firstFilter);
|
||||
await searchPage.submitButton.click();
|
||||
await searchPage.table.waitForRowsToPopulate(true);
|
||||
|
||||
|
|
@ -578,7 +582,7 @@ test.describe('Saved Search Functionality', () => {
|
|||
|
||||
await test.step('Update saved search with second filter', async () => {
|
||||
await searchPage.filters.openFilterGroup(secondFilterGroup);
|
||||
await searchPage.filters.applyFilter(secondFilter);
|
||||
await searchPage.filters.applyFilter(secondFilterGroup, secondFilter);
|
||||
await searchPage.submitButton.click();
|
||||
await searchPage.table.waitForRowsToPopulate(true);
|
||||
|
||||
|
|
@ -600,10 +604,16 @@ test.describe('Saved Search Functionality', () => {
|
|||
await searchPage.filters.openFilterGroup(firstFilterGroup);
|
||||
await searchPage.filters.openFilterGroup(secondFilterGroup);
|
||||
await expect(
|
||||
searchPage.filters.getFilterCheckboxInput(firstFilter),
|
||||
searchPage.filters.getFilterCheckboxInput(
|
||||
firstFilterGroup,
|
||||
firstFilter,
|
||||
),
|
||||
).toBeChecked();
|
||||
await expect(
|
||||
searchPage.filters.getFilterCheckboxInput(secondFilter),
|
||||
searchPage.filters.getFilterCheckboxInput(
|
||||
secondFilterGroup,
|
||||
secondFilter,
|
||||
),
|
||||
).toBeChecked();
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,21 +4,24 @@ import { expect, test } from '../../utils/base-test';
|
|||
test.describe('Search Filters', { tag: ['@search'] }, () => {
|
||||
let searchPage: SearchPage;
|
||||
// Using known seeded data - 'info' severity always exists in test data
|
||||
const TEST_FILTER_GROUP = 'SeverityText';
|
||||
const TEST_FILTER_VALUE = 'info';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
searchPage = new SearchPage(page);
|
||||
await searchPage.goto();
|
||||
await searchPage.filters.openFilterGroup('SeverityText');
|
||||
await searchPage.filters.openFilterGroup(TEST_FILTER_GROUP);
|
||||
});
|
||||
|
||||
test('Should apply filters', async () => {
|
||||
// Apply the filter using component method
|
||||
const filterInput =
|
||||
searchPage.filters.getFilterCheckboxInput(TEST_FILTER_VALUE);
|
||||
const filterInput = searchPage.filters.getFilterCheckboxInput(
|
||||
TEST_FILTER_GROUP,
|
||||
TEST_FILTER_VALUE,
|
||||
);
|
||||
await expect(filterInput).toBeVisible();
|
||||
|
||||
await searchPage.filters.applyFilter(TEST_FILTER_VALUE);
|
||||
await searchPage.filters.applyFilter(TEST_FILTER_GROUP, TEST_FILTER_VALUE);
|
||||
|
||||
// Verify filter is checked
|
||||
await expect(filterInput).toBeChecked();
|
||||
|
|
@ -29,47 +32,56 @@ test.describe('Search Filters', { tag: ['@search'] }, () => {
|
|||
|
||||
test('Should exclude filters', async () => {
|
||||
// Use filter component to exclude the filter
|
||||
await searchPage.filters.excludeFilter(TEST_FILTER_VALUE);
|
||||
await searchPage.filters.excludeFilter(
|
||||
TEST_FILTER_GROUP,
|
||||
TEST_FILTER_VALUE,
|
||||
);
|
||||
|
||||
// Verify filter shows as excluded using web-first assertion
|
||||
const isExcluded =
|
||||
await searchPage.filters.isFilterExcluded(TEST_FILTER_VALUE);
|
||||
const isExcluded = await searchPage.filters.isFilterExcluded(
|
||||
TEST_FILTER_GROUP,
|
||||
TEST_FILTER_VALUE,
|
||||
);
|
||||
expect(isExcluded).toBe(true);
|
||||
});
|
||||
|
||||
test('Should clear filters', async () => {
|
||||
await searchPage.filters.clearFilter(TEST_FILTER_VALUE);
|
||||
await searchPage.filters.clearFilter(TEST_FILTER_GROUP, TEST_FILTER_VALUE);
|
||||
|
||||
// Verify filter is no longer checked
|
||||
const filterInput =
|
||||
searchPage.filters.getFilterCheckboxInput(TEST_FILTER_VALUE);
|
||||
const filterInput = searchPage.filters.getFilterCheckboxInput(
|
||||
TEST_FILTER_GROUP,
|
||||
TEST_FILTER_VALUE,
|
||||
);
|
||||
await expect(filterInput).not.toBeChecked();
|
||||
});
|
||||
|
||||
test('Should search for and apply filters', async () => {
|
||||
const filterName = 'SeverityText';
|
||||
await searchPage.filters.openFilterGroup(filterName);
|
||||
await searchPage.filters.searchFilterValues(filterName, 'test');
|
||||
const searchInput = searchPage.filters.getFilterSearchInput(filterName);
|
||||
await searchPage.filters.openFilterGroup(TEST_FILTER_GROUP);
|
||||
await searchPage.filters.searchFilterValues(TEST_FILTER_GROUP, 'test');
|
||||
const searchInput =
|
||||
searchPage.filters.getFilterSearchInput(TEST_FILTER_GROUP);
|
||||
await expect(searchInput).toHaveValue('test');
|
||||
await searchPage.filters.clearFilterSearch(filterName);
|
||||
await searchPage.filters.clearFilterSearch(TEST_FILTER_GROUP);
|
||||
await expect(searchInput).toHaveValue('');
|
||||
});
|
||||
|
||||
test('Should pin filter and verify it persists after reload', async () => {
|
||||
await searchPage.filters.pinFilter(TEST_FILTER_VALUE);
|
||||
await searchPage.filters.pinFilter(TEST_FILTER_GROUP, TEST_FILTER_VALUE);
|
||||
|
||||
// Reload page and verify filter persists
|
||||
await searchPage.page.reload();
|
||||
|
||||
// Verify filter checkbox is still visible
|
||||
const filterCheckbox =
|
||||
searchPage.filters.getFilterCheckbox(TEST_FILTER_VALUE);
|
||||
const filterCheckbox = searchPage.filters.getFilterCheckbox(
|
||||
TEST_FILTER_GROUP,
|
||||
TEST_FILTER_VALUE,
|
||||
);
|
||||
await expect(filterCheckbox).toBeVisible();
|
||||
|
||||
//verify there is a pin icon
|
||||
const pinIcon = searchPage.page.getByTestId(
|
||||
`filter-pin-${TEST_FILTER_VALUE}-pinned`,
|
||||
`filter-checkbox-${TEST_FILTER_GROUP}-${TEST_FILTER_VALUE}-pin-pinned`,
|
||||
);
|
||||
await expect(pinIcon).toBeVisible();
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue