Skip to content

Commit

Permalink
fix(atomic): clear recent searches on keyboard enter keypress (#4072)
Browse files Browse the repository at this point in the history
https://coveord.atlassian.net/browse/KIT-3234

---------

Co-authored-by: ylakhdar <ylakhdar@coveo.com>
  • Loading branch information
fbeaudoincoveo and y-lakhdar authored Jun 20, 2024
1 parent 6623e03 commit 9ab7ba5
Show file tree
Hide file tree
Showing 9 changed files with 393 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -372,14 +372,18 @@ export class AtomicCommerceSearchBox
}

private onSubmit() {
if (this.suggestionManager.isRightPanelInFocus()) {
this.suggestionManager.clickOnActiveElement();
this.isExpanded = false;
if (
this.suggestionManager.isRightPanelInFocus() ||
this.suggestionManager.activeDescendantElement?.part.contains(
'recent-query-title-item'
)
) {
this.suggestionManager.onSubmit();
return;
}

this.isExpanded = false;
this.searchBox.submit();
this.suggestionManager.onSubmit();
}

private onKeyDown(e: KeyboardEvent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,36 @@ test.describe('with instant results & query suggestions', () => {
await searchBox.load({suggestionTimeout: 5000}, 'rich-search-box');
});

test.describe('with recent queries', () => {
test.beforeEach(async ({searchBox}) => {
await searchBox.searchInput.waitFor({state: 'visible'});
await searchBox.searchInput.click();
await searchBox.searchInput.fill('kayak');
await searchBox.searchInput.press('Enter');
await searchBox.clearButton.waitFor({state: 'visible'});
await searchBox.searchInput.fill('');
});

test('should display recent queries', async ({searchBox}) => {
await expect(searchBox.recentQueries().first()).toBeVisible();
});

test('should clear recent queries when clicking the clear button', async ({
searchBox,
}) => {
await searchBox.clearRecentQueriesButton.click();
await expect(searchBox.recentQueries().first()).not.toBeVisible();
});

test('should clear recent queries when pressing enter while the clear button is focused', async ({
searchBox,
}) => {
await searchBox.clearRecentQueriesButton.hover();
await searchBox.searchInput.press('Enter');
await expect(searchBox.recentQueries().first()).not.toBeVisible();
});
});

test.describe('after clicking the searchbox input', () => {
test.beforeEach(async ({searchBox}) => {
await searchBox.searchInput.click();
Expand All @@ -74,6 +104,25 @@ test.describe('with instant results & query suggestions', () => {
const accessibilityResults = await makeAxeBuilder().analyze();
expect(accessibilityResults.violations).toEqual([]);
});

test('should display in the search box what has been submitted', async ({
searchBox,
}) => {
await searchBox.searchInput.fill('Rec');
await searchBox.searchInput.press('Enter');
await expect(searchBox.searchInput).toHaveValue('Rec');
});

test.describe('after focusing on suggestion with the mouse', () => {
test('should submit what is in the search box regardless of the mouse position', async ({
searchBox,
}) => {
await searchBox.searchInput.fill('Rec');
await searchBox.searchSuggestions({listSide: 'Left'}).first().hover();
await searchBox.searchInput.press('Enter');
await expect(searchBox.searchInput).toHaveValue('Rec');
});
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ export const test = base.extend<MyFixtures & AxeFixture>({
await use(new LoadMoreProductsPageObject(page));
},
});

export {expect} from '@playwright/test';
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,34 @@ export class SearchBoxPageObject extends BasePageObject<'atomic-commerce-search-
return this.page.getByRole('combobox', {name: 'Search'});
}

get clearRecentQueriesButton() {
return this.page.getByLabel('Clear recent searches.');
}

get clearButton() {
return this.page.getByLabel('Clear', {exact: true});
}

searchSuggestions({
index,
total,
listSide,
}: {index?: number; total?: number; listSide?: 'Left' | 'Right'} = {}) {
return this.page.getByLabel(
new RegExp(
`suggested query\\. ${index ?? '\\d'} of ${total ?? '\\d'}\\.${this.listSideAffix(listSide)}`
`suggested query\\.(?: Button\\.)? ${index ?? '\\d'} of ${total ?? '\\d'}\\.${this.listSideAffix(listSide)}`
)
);
}

recentQueries({
index,
total,
listSide,
}: {index?: number; total?: number; listSide?: 'Left' | 'Right'} = {}) {
return this.page.getByLabel(
new RegExp(
`recent query\\.(?: Button\\.)? ${index ?? '\\d'} of ${total ?? '\\d'}\\.${this.listSideAffix(listSide)}`
)
);
}
Expand All @@ -33,7 +53,7 @@ export class SearchBoxPageObject extends BasePageObject<'atomic-commerce-search-
}: {index?: number; total?: number; listSide?: 'Left' | 'Right'} = {}) {
return this.page.getByLabel(
new RegExp(
`instant result\\. ${index ?? '\\d'} of ${total ?? '\\d'}\\.${this.listSideAffix(listSide)}`
`instant result\\.(?: Button\\.)? ${index ?? '\\d'} of ${total ?? '\\d'}\\.${this.listSideAffix(listSide)}`
)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class SuggestionManager<SearchBoxController> {
}

public onSubmit() {
this.updateActiveDescendant();
this.clickOnActiveElement();
this.clearSuggestions();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,14 +403,18 @@ export class AtomicSearchBox implements InitializableComponent<Bindings> {
}

private onSubmit() {
if (this.suggestionManager.isRightPanelInFocus()) {
this.suggestionManager.clickOnActiveElement();
this.isExpanded = false;
if (
this.suggestionManager.isRightPanelInFocus() ||
this.suggestionManager.activeDescendantElement?.part.contains(
'recent-query-title-item'
)
) {
this.suggestionManager.onSubmit();
return;
}

this.isExpanded = false;
this.searchBox.submit();
this.suggestionManager.onSubmit();
}

private onKeyDown(e: KeyboardEvent) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import {test, expect} from './fixture';

test.describe('default', () => {
test.beforeEach(async ({searchBox}) => {
await searchBox.load({suggestionTimeout: 5000});
});

test('should have an enabled search button', async ({searchBox}) => {
await expect(searchBox.submitButton).toBeEnabled();
});

test('should be A11y compliant', async ({searchBox, makeAxeBuilder}) => {
await searchBox.hydrated.waitFor();
const accessibilityResults = await makeAxeBuilder().analyze();
expect(accessibilityResults.violations).toEqual([]);
});

test.describe('after clicking the searchbox input', () => {
test.beforeEach(async ({searchBox}) => {
await searchBox.searchInput.waitFor({state: 'visible'});
await searchBox.searchInput.click();
});

test('should display suggested queries', async ({searchBox}) => {
await expect(searchBox.searchSuggestions().first()).toBeVisible();
});

test('should be A11y compliant', async ({searchBox, makeAxeBuilder}) => {
await searchBox.hydrated.waitFor();
const accessibilityResults = await makeAxeBuilder().analyze();
expect(accessibilityResults.violations).toEqual([]);
});

test.describe('after clicking the submit button', () => {
test.beforeEach(async ({searchBox}) => {
await searchBox
.searchSuggestions()
.first()
.waitFor({state: 'visible', timeout: 10e3});
await searchBox.submitButton.click();
});

test('should collapse the suggested queries', async ({searchBox}) => {
await expect(searchBox.searchSuggestions().first()).not.toBeVisible();
});
});
});
});

test.describe('with instant results & query suggestions', () => {
test.beforeEach(async ({searchBox}) => {
await searchBox.load({suggestionTimeout: 5000}, 'rich-search-box');
});

test.describe('with recent queries', () => {
test.beforeEach(async ({searchBox}) => {
await searchBox.searchInput.waitFor({state: 'visible'});
await searchBox.searchInput.click();
await searchBox.searchInput.fill('kayak');
await searchBox.searchInput.press('Enter');
await searchBox.clearButton.waitFor({state: 'visible'});
await searchBox.searchInput.fill('');
});

test('should display recent queries', async ({searchBox}) => {
await expect(searchBox.recentQueries().first()).toBeVisible();
});

test('should clear recent queries when clicking the clear button', async ({
searchBox,
}) => {
await searchBox.clearRecentQueriesButton.click();
await expect(searchBox.recentQueries().first()).not.toBeVisible();
});

test('should clear recent queries when pressing enter while the clear button is focused', async ({
searchBox,
}) => {
await searchBox.clearRecentQueriesButton.hover();
await searchBox.searchInput.press('Enter');
await expect(searchBox.recentQueries().first()).not.toBeVisible();
});
});

test.describe('after clicking the searchbox input', () => {
test.beforeEach(async ({searchBox}) => {
await searchBox.searchInput.click();
});

test('should display suggested queries', async ({searchBox}) => {
await expect(
searchBox.searchSuggestions({listSide: 'Left'}).first()
).toBeVisible();
});

test('should display instant results', async ({searchBox}) => {
await expect(
searchBox.instantResult({listSide: 'Right'}).first()
).toBeVisible();
});

test('should be A11y compliant', async ({searchBox, makeAxeBuilder}) => {
await searchBox.hydrated.waitFor();
const accessibilityResults = await makeAxeBuilder().analyze();
expect(accessibilityResults.violations).toEqual([]);
});

test('should display in the search box what has been submitted', async ({
searchBox,
}) => {
await searchBox.searchInput.fill('Rec');
await searchBox.searchInput.press('Enter');
await expect(searchBox.searchInput).toHaveValue('Rec');
});

test.describe('after focusing on suggestion with the mouse', () => {
test('should submit what is in the search box regardless of the mouse position', async ({
searchBox,
}) => {
await searchBox.searchInput.fill('Rec');
await searchBox.searchSuggestions({listSide: 'Left'}).first().hover();
await searchBox.searchInput.press('Enter');
await expect(searchBox.searchInput).toHaveValue('Rec');
});
});
});
});

test.describe('with disable-search=true and minimum-query-length=1', () => {
test.beforeEach(async ({searchBox}) => {
await searchBox.load({
suggestionTimeout: 5000,
disableSearch: true,
minimumQueryLength: 1,
});
});

const testCases = () => {
test('the submit button is disabled', async ({searchBox}) => {
await expect(searchBox.submitButton).toBeDisabled();
});

test('there are no search suggestions', async ({searchBox}) => {
await expect(searchBox.searchSuggestions().first()).not.toBeVisible();
});

test('should be A11y compliant', async ({searchBox, makeAxeBuilder}) => {
await searchBox.hydrated.waitFor();
const accessibilityResults = await makeAxeBuilder().analyze();
expect(accessibilityResults.violations).toEqual([]);
});
};

testCases();

test.describe('after clicking the searchbox input', () => {
test.beforeEach(async ({searchBox}) => {
await expect(searchBox.searchInput).toBeEnabled();
});

testCases();
});

test.describe('after typing a query above the threshold', () => {
test.beforeEach(async ({page}) => {
await page.getByPlaceholder('Search').fill('kayak');
});

testCases();
});
});

test.describe('with minimum-query-length=4', () => {
test.beforeEach(async ({searchBox}) => {
await searchBox.load({minimumQueryLength: 4, suggestionTimeout: 5000});
});

const testCases = () => {
test('the submit button is disabled', async ({searchBox}) => {
await expect(searchBox.submitButton).toBeDisabled();
});

test('there are no search suggestions', async ({searchBox}) => {
await expect(searchBox.searchSuggestions().first()).not.toBeVisible();
});

test('should be A11y compliant', async ({searchBox, makeAxeBuilder}) => {
await searchBox.hydrated.waitFor();
const accessibilityResults = await makeAxeBuilder().analyze();
expect(accessibilityResults.violations).toEqual([]);
});
};

testCases();

test.describe('after clicking the searchbox input', () => {
test.beforeEach(async ({searchBox}) => {
await expect(searchBox.searchInput).toBeEnabled();
});

testCases();
});

test.describe('after typing a query below the threshold', () => {
test.beforeEach(async ({page}) => {
await page.getByPlaceholder('Search').fill('kay');
});

testCases();
});

test.describe('after typing a query above the threshold', () => {
test.beforeEach(async ({page}) => {
await page.getByPlaceholder('Search').fill('kayak');
});

test('the submit button is disabled', async ({searchBox}) => {
await expect(searchBox.submitButton).toBeEnabled();
});

test('there are no search suggestions', async ({searchBox}) => {
await expect(searchBox.searchSuggestions().first()).toBeVisible();
});
});
});
Loading

0 comments on commit 9ab7ba5

Please sign in to comment.