Skip to content

Commit

Permalink
fix(atomic): add query-summary E2E tests (#4102)
Browse files Browse the repository at this point in the history
https://coveord.atlassian.net/browse/KIT-3254

---------

Co-authored-by: GitHub Actions Bot <>
  • Loading branch information
y-lakhdar authored Jun 26, 2024
1 parent 49e9415 commit 6f73da7
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ AtomicCategoryFacet,
AtomicColorFacet,
AtomicCommerceFacets,
AtomicCommerceLoadMoreProducts,
AtomicCommerceQuerySummary,
AtomicCommerceSearchBox,
AtomicComponentError,
AtomicDidYouMean,
Expand Down Expand Up @@ -113,6 +114,7 @@ AtomicCategoryFacet,
AtomicColorFacet,
AtomicCommerceFacets,
AtomicCommerceLoadMoreProducts,
AtomicCommerceQuerySummary,
AtomicCommerceSearchBox,
AtomicComponentError,
AtomicDidYouMean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,27 @@ export class AtomicCommerceLoadMoreProducts {
export declare interface AtomicCommerceLoadMoreProducts extends Components.AtomicCommerceLoadMoreProducts {}


@ProxyCmp({
})
@Component({
selector: 'atomic-commerce-query-summary',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: [],
})
export class AtomicCommerceQuerySummary {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
}
}


export declare interface AtomicCommerceQuerySummary extends Components.AtomicCommerceQuerySummary {}


@ProxyCmp({
inputs: ['clearFilters', 'disableSearch', 'minimumQueryLength', 'numberOfQueries', 'redirectionUrl', 'suggestionDelay', 'suggestionTimeout']
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const AtomicCommerceNoProducts = /*@__PURE__*/createReactComponent<JSX.At
export const AtomicCommerceNumericFacet = /*@__PURE__*/createReactComponent<JSX.AtomicCommerceNumericFacet, HTMLAtomicCommerceNumericFacetElement>('atomic-commerce-numeric-facet');
export const AtomicCommercePager = /*@__PURE__*/createReactComponent<JSX.AtomicCommercePager, HTMLAtomicCommercePagerElement>('atomic-commerce-pager');
export const AtomicCommerceProductList = /*@__PURE__*/createReactComponent<JSX.AtomicCommerceProductList, HTMLAtomicCommerceProductListElement>('atomic-commerce-product-list');
export const AtomicCommerceQuerySummary = /*@__PURE__*/createReactComponent<JSX.AtomicCommerceQuerySummary, HTMLAtomicCommerceQuerySummaryElement>('atomic-commerce-query-summary');
export const AtomicCommerceRecommendationInterface = /*@__PURE__*/createReactComponent<JSX.AtomicCommerceRecommendationInterface, HTMLAtomicCommerceRecommendationInterfaceElement>('atomic-commerce-recommendation-interface');
export const AtomicCommerceRecommendationList = /*@__PURE__*/createReactComponent<JSX.AtomicCommerceRecommendationList, HTMLAtomicCommerceRecommendationListElement>('atomic-commerce-recommendation-list');
export const AtomicCommerceSearchBox = /*@__PURE__*/createReactComponent<JSX.AtomicCommerceSearchBox, HTMLAtomicCommerceSearchBoxElement>('atomic-commerce-search-box');
Expand Down
4 changes: 4 additions & 0 deletions packages/atomic/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ export namespace Components {
}
/**
* The `atomic-commerce-query-summary` component displays information about the current range of results and the request duration (e.g., "Results 1-10 of 123 in 0.47 seconds").
* @alpha
*/
interface AtomicCommerceQuerySummary {
}
Expand Down Expand Up @@ -3606,6 +3607,7 @@ declare global {
};
/**
* The `atomic-commerce-query-summary` component displays information about the current range of results and the request duration (e.g., "Results 1-10 of 123 in 0.47 seconds").
* @alpha
*/
interface HTMLAtomicCommerceQuerySummaryElement extends Components.AtomicCommerceQuerySummary, HTMLStencilElement {
}
Expand Down Expand Up @@ -5895,6 +5897,7 @@ declare namespace LocalJSX {
}
/**
* The `atomic-commerce-query-summary` component displays information about the current range of results and the request duration (e.g., "Results 1-10 of 123 in 0.47 seconds").
* @alpha
*/
interface AtomicCommerceQuerySummary {
}
Expand Down Expand Up @@ -8862,6 +8865,7 @@ declare module "@stencil/core" {
"atomic-commerce-query-error": LocalJSX.AtomicCommerceQueryError & JSXBase.HTMLAttributes<HTMLAtomicCommerceQueryErrorElement>;
/**
* The `atomic-commerce-query-summary` component displays information about the current range of results and the request duration (e.g., "Results 1-10 of 123 in 0.47 seconds").
* @alpha
*/
"atomic-commerce-query-summary": LocalJSX.AtomicCommerceQuerySummary & JSXBase.HTMLAttributes<HTMLAtomicCommerceQuerySummaryElement>;
"atomic-commerce-recommendation-interface": LocalJSX.AtomicCommerceRecommendationInterface & JSXBase.HTMLAttributes<HTMLAtomicCommerceRecommendationInterfaceElement>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import {wrapInCommerceInterface} from '@coveo/atomic/storybookUtils/commerce-interface-wrapper';
import {parameters} from '@coveo/atomic/storybookUtils/common-meta-parameters';
import {renderComponent} from '@coveo/atomic/storybookUtils/render-component';
import {CommerceEngineConfiguration} from '@coveo/headless/commerce';
import type {Meta, StoryObj as Story} from '@storybook/web-components';
import {html} from 'lit/static-html.js';

const {decorator, play} = wrapInCommerceInterface({
skipFirstSearch: true,
});

const noResultsEngineConfig: Partial<CommerceEngineConfiguration> = {
preprocessRequest: (r) => {
const parsed = JSON.parse(r.body as string);
// eslint-disable-next-line @cspell/spellchecker
parsed.query = 'qnjssoptjhyalwnmrbgtyslsd';
r.body = JSON.stringify(parsed);
return r;
},
};

const fixedNumberOfResults = (
perPage: number
): Partial<CommerceEngineConfiguration> => ({
preprocessRequest: (r) => {
const parsed = JSON.parse(r.body as string);
parsed.perPage = perPage;
r.body = JSON.stringify(parsed);
return r;
},
});

const {play: playNoresults} = wrapInCommerceInterface({
skipFirstSearch: false,
engineConfig: noResultsEngineConfig,
});

const {play: playFixedNumberOfResults} = wrapInCommerceInterface({
skipFirstSearch: false,
engineConfig: fixedNumberOfResults(27),
});

const meta: Meta = {
component: 'atomic-commerce-query-summary',
title: 'Atomic-Commerce/QuerySummary',
id: 'atomic-commerce-query-summary',
render: renderComponent,
decorators: [decorator],
parameters,
play,
};

export default meta;

export const Default: Story = {
name: 'atomic-query-summary',
};

export const NoResults: Story = {
name: 'No Results',
tags: ['test'],
decorators: [(story) => story()],
play: async (context) => {
await playNoresults(context);
},
};

export const FixedNumberOfResults: Story = {
name: 'Fixed Number of Results',
tags: ['test'],
decorators: [(story) => story()],
play: async (context) => {
await playFixedNumberOfResults(context);
},
};

export const WithSearchBox: Story = {
name: 'With a Search Box',
tags: ['test'],
decorators: [
(story) =>
html` <atomic-commerce-layout>
<atomic-layout-section section="search">
<atomic-commerce-search-box></atomic-commerce-search-box>
</atomic-layout-section>
<atomic-layout-section section="main">
<atomic-layout-section section="status">
${story()}
</atomic-layout-section>
</atomic-layout-section>
</atomic-commerce-layout>`,
],
play: async (context) => {
await play(context);
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@ import {CommerceBindings} from '../atomic-commerce-interface/atomic-commerce-int
*
* @part container - The container for the whole summary.
* @part results - The container for the results.
* @part duration - The container for the duration.
* @part highlight - The summary highlights.
* @part query - The summary highlighted query.
* @part placeholder - The query summary placeholder used while the search interface is initializing.
*
* @internal
* @alpha
*/
@Component({
tag: 'atomic-commerce-query-summary',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {test, expect} from './fixture';

test.describe('when search has not been executed', () => {
test.beforeEach(async ({querySummary}) => {
await querySummary.load();
});

test('should display a placeholder', async ({querySummary}) => {
await expect(querySummary.placeholder).not.toBeVisible();
});
});

test.describe('after searching for kayak', () => {
test.beforeEach(async ({searchBox, querySummary}) => {
await querySummary.load({}, 'with-search-box');
await searchBox.hydrated.waitFor();
await searchBox.searchInput.fill('kayak');
await searchBox.submitButton.click();
});

test('should not display duration by default', async ({querySummary}) => {
const textRegex = /^Results 1-[\d,]+ of [\d,]+ for kayak$/;
await expect(querySummary.text(textRegex)).toBeVisible();

Check failure on line 23 in packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts

View workflow job for this annotation

GitHub Actions / Run Playwright tests for Atomic

[webkit] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:21:7 › after searching for kayak › should not display duration by default

1) [webkit] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:21:7 › after searching for kayak › should not display duration by default Error: expect.toBeVisible: Error: strict mode violation: getByText(/^Results 1-[\d,]+ of [\d,]+ for kayak$/) resolved to 2 elements: 1) <div role="status" aria-live="polite" id="aria-live-x…>Results 1-48 of 79 for kayak</div> aka locator('#aria-live-xnufx-query-summary') 2) <div part="container" class="text-on-background">…</div> aka locator('#code-root').getByText('Results 1-48 of 79 for kayak') Call log: - expect.toBeVisible with timeout 20000ms - waiting for getByText(/^Results 1-[\d,]+ of [\d,]+ for kayak$/) 21 | test('should not display duration by default', async ({querySummary}) => { 22 | const textRegex = /^Results 1-[\d,]+ of [\d,]+ for kayak$/; > 23 | await expect(querySummary.text(textRegex)).toBeVisible(); | ^ 24 | }); 25 | }); 26 | at /home/runner/work/ui-kit/ui-kit/packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:23:48

Check failure on line 23 in packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts

View workflow job for this annotation

GitHub Actions / Run Playwright tests for Atomic

[webkit] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:21:7 › after searching for kayak › should not display duration by default

1) [webkit] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:21:7 › after searching for kayak › should not display duration by default Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: expect.toBeVisible: Error: strict mode violation: getByText(/^Results 1-[\d,]+ of [\d,]+ for kayak$/) resolved to 2 elements: 1) <div role="status" aria-live="polite" id="aria-live-z…>Results 1-48 of 79 for kayak</div> aka locator('#aria-live-zvlep-query-summary') 2) <div part="container" class="text-on-background">…</div> aka locator('#code-root').getByText('Results 1-48 of 79 for kayak') Call log: - expect.toBeVisible with timeout 20000ms - waiting for getByText(/^Results 1-[\d,]+ of [\d,]+ for kayak$/) 21 | test('should not display duration by default', async ({querySummary}) => { 22 | const textRegex = /^Results 1-[\d,]+ of [\d,]+ for kayak$/; > 23 | await expect(querySummary.text(textRegex)).toBeVisible(); | ^ 24 | }); 25 | }); 26 | at /home/runner/work/ui-kit/ui-kit/packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:23:48

Check failure on line 23 in packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts

View workflow job for this annotation

GitHub Actions / Run Playwright tests for Atomic

[webkit] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:21:7 › after searching for kayak › should not display duration by default

1) [webkit] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:21:7 › after searching for kayak › should not display duration by default Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: expect.toBeVisible: Error: strict mode violation: getByText(/^Results 1-[\d,]+ of [\d,]+ for kayak$/) resolved to 2 elements: 1) <div role="status" aria-live="polite" id="aria-live-c…>Results 1-48 of 79 for kayak</div> aka locator('#aria-live-c1w1u-query-summary') 2) <div part="container" class="text-on-background">…</div> aka locator('#code-root').getByText('Results 1-48 of 79 for kayak') Call log: - expect.toBeVisible with timeout 20000ms - waiting for getByText(/^Results 1-[\d,]+ of [\d,]+ for kayak$/) 21 | test('should not display duration by default', async ({querySummary}) => { 22 | const textRegex = /^Results 1-[\d,]+ of [\d,]+ for kayak$/; > 23 | await expect(querySummary.text(textRegex)).toBeVisible(); | ^ 24 | }); 25 | }); 26 | at /home/runner/work/ui-kit/ui-kit/packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:23:48

Check failure on line 23 in packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts

View workflow job for this annotation

GitHub Actions / Run Playwright tests for Atomic

[chromium] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:21:7 › after searching for kayak › should not display duration by default

3) [chromium] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:21:7 › after searching for kayak › should not display duration by default Error: expect.toBeVisible: Error: strict mode violation: getByText(/^Results 1-[\d,]+ of [\d,]+ for kayak$/) resolved to 2 elements: 1) <div role="status" aria-live="polite" id="aria-live-e…>Results 1-48 of 79 for kayak</div> aka locator('#aria-live-eqfqu-query-summary') 2) <div part="container" class="text-on-background">…</div> aka locator('#code-root').getByText('Results 1-48 of 79 for kayak') Call log: - expect.toBeVisible with timeout 20000ms - waiting for getByText(/^Results 1-[\d,]+ of [\d,]+ for kayak$/) 21 | test('should not display duration by default', async ({querySummary}) => { 22 | const textRegex = /^Results 1-[\d,]+ of [\d,]+ for kayak$/; > 23 | await expect(querySummary.text(textRegex)).toBeVisible(); | ^ 24 | }); 25 | }); 26 | at /home/runner/work/ui-kit/ui-kit/packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:23:48
});
});

test.describe('when search yields no results', () => {
test.beforeEach(async ({querySummary}) => {
await querySummary.load({}, 'no-results');
});

test('should not display anything', async ({querySummary}) => {
await expect(querySummary.hydrated).toBeEmpty();
});
});

test.describe('when search yields 27 results', () => {
test.beforeEach(async ({querySummary}) => {
await querySummary.load({}, 'fixed-number-of-results');
});

test('screen readers should read out', async ({querySummary}) => {
const textRegex = /Results 1-27 of [\d,]+/;
await expect(querySummary.ariaLive(textRegex)).toBeVisible();
});
});

test.describe('when a query yield a single result', () => {
test.beforeEach(async ({querySummary, searchBox}) => {
await querySummary.load({}, 'with-search-box');
await searchBox.hydrated.waitFor();
await searchBox.searchInput.fill('@ec_product_id=SP03730_00007');
await searchBox.submitButton.click();
});

test('should display message', async ({querySummary}) => {
const textRegex = /^Result 1 of 1 for @ec_product_id=SP03730_00007$/;
await expect(querySummary.text(textRegex)).toBeVisible();

Check failure on line 58 in packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts

View workflow job for this annotation

GitHub Actions / Run Playwright tests for Atomic

[webkit] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:56:7 › when a query yield a single result › should display message

2) [webkit] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:56:7 › when a query yield a single result › should display message Error: expect.toBeVisible: Error: strict mode violation: getByText(/^Result 1 of 1 for @ec_product_id=SP03730_00007$/) resolved to 2 elements: 1) <div role="status" aria-live="polite" id="aria-live-1…>Result 1 of 1 for @ec_product_id=SP03730_00007</div> aka locator('#aria-live-1scrm-query-summary') 2) <div part="container" class="text-on-background">…</div> aka locator('#code-root').getByText('Result 1 of 1 for @') Call log: - expect.toBeVisible with timeout 20000ms - waiting for getByText(/^Result 1 of 1 for @ec_product_id=SP03730_00007$/) 56 | test('should display message', async ({querySummary}) => { 57 | const textRegex = /^Result 1 of 1 for @ec_product_id=SP03730_00007$/; > 58 | await expect(querySummary.text(textRegex)).toBeVisible(); | ^ 59 | }); 60 | 61 | test('screen readers should read out', async ({querySummary}) => { at /home/runner/work/ui-kit/ui-kit/packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:58:48

Check failure on line 58 in packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts

View workflow job for this annotation

GitHub Actions / Run Playwright tests for Atomic

[webkit] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:56:7 › when a query yield a single result › should display message

2) [webkit] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:56:7 › when a query yield a single result › should display message Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: expect.toBeVisible: Error: strict mode violation: getByText(/^Result 1 of 1 for @ec_product_id=SP03730_00007$/) resolved to 2 elements: 1) <div role="status" aria-live="polite" id="aria-live-g…>Result 1 of 1 for @ec_product_id=SP03730_00007</div> aka locator('#aria-live-gkk50-query-summary') 2) <div part="container" class="text-on-background">…</div> aka locator('#code-root').getByText('Result 1 of 1 for @') Call log: - expect.toBeVisible with timeout 20000ms - waiting for getByText(/^Result 1 of 1 for @ec_product_id=SP03730_00007$/) 56 | test('should display message', async ({querySummary}) => { 57 | const textRegex = /^Result 1 of 1 for @ec_product_id=SP03730_00007$/; > 58 | await expect(querySummary.text(textRegex)).toBeVisible(); | ^ 59 | }); 60 | 61 | test('screen readers should read out', async ({querySummary}) => { at /home/runner/work/ui-kit/ui-kit/packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:58:48

Check failure on line 58 in packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts

View workflow job for this annotation

GitHub Actions / Run Playwright tests for Atomic

[webkit] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:56:7 › when a query yield a single result › should display message

2) [webkit] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:56:7 › when a query yield a single result › should display message Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: expect.toBeVisible: Error: strict mode violation: getByText(/^Result 1 of 1 for @ec_product_id=SP03730_00007$/) resolved to 2 elements: 1) <div role="status" aria-live="polite" id="aria-live-8…>Result 1 of 1 for @ec_product_id=SP03730_00007</div> aka locator('#aria-live-8xvum-query-summary') 2) <div part="container" class="text-on-background">…</div> aka locator('#code-root').getByText('Result 1 of 1 for @') Call log: - expect.toBeVisible with timeout 20000ms - waiting for getByText(/^Result 1 of 1 for @ec_product_id=SP03730_00007$/) 56 | test('should display message', async ({querySummary}) => { 57 | const textRegex = /^Result 1 of 1 for @ec_product_id=SP03730_00007$/; > 58 | await expect(querySummary.text(textRegex)).toBeVisible(); | ^ 59 | }); 60 | 61 | test('screen readers should read out', async ({querySummary}) => { at /home/runner/work/ui-kit/ui-kit/packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:58:48
});

test('screen readers should read out', async ({querySummary}) => {
const textRegex = /^Result 1 of 1 for @ec_product_id=SP03730_00007$/;
await expect(querySummary.ariaLive(textRegex)).toBeVisible();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {test as base} from '@playwright/test';
import {
AxeFixture,
makeAxeBuilder,
} from '../../../../../playwright-utils/base-fixture';
import {SearchBoxPageObject as SearchBox} from '../../atomic-commerce-search-box/e2e/page-object';
import {QuerySummaryPageObject as QuerySummary} from './page-object';

type MyFixtures = {
searchBox: SearchBox;
querySummary: QuerySummary;
};

export const test = base.extend<MyFixtures & AxeFixture>({
makeAxeBuilder,
searchBox: async ({page}, use) => {
await use(new SearchBox(page));
},
querySummary: async ({page}, use) => {
await use(new QuerySummary(page));
},
});
export {expect} from '@playwright/test';
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type {Page} from '@playwright/test';
import {BasePageObject} from '../../../../../playwright-utils/base-page-object';

export class QuerySummaryPageObject extends BasePageObject<'atomic-commerce-query-summary'> {
constructor(page: Page) {
super(page, 'atomic-commerce-query-summary');
}

get placeholder() {
return this.page.locator('[part="placeholder"]');
}

ariaLive(textRegex: RegExp) {
return this.page.getByRole('status').filter({hasText: textRegex});
}

text(summaryRegex: RegExp) {
return this.page.getByText(summaryRegex);
}
}

0 comments on commit 6f73da7

Please sign in to comment.