Skip to content

Commit

Permalink
test(atomic): start tests on atomic-commerce-facets (#4071)
Browse files Browse the repository at this point in the history
Mostly: Add new `base-page-object` to inherit from for each tests, which
should simplify/streamline the "load this story for this component with
these arguments in the URL" repetitive pattern.


https://coveord.atlassian.net/browse/KIT-3256
  • Loading branch information
olamothe authored Jun 18, 2024
1 parent d7214c7 commit 8af33c1
Show file tree
Hide file tree
Showing 18 changed files with 306 additions and 77 deletions.
29 changes: 29 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ AtomicAutomaticFacetGenerator,
AtomicBreadbox,
AtomicCategoryFacet,
AtomicColorFacet,
AtomicCommerceFacets,
AtomicCommerceLoadMoreProducts,
AtomicCommerceSearchBox,
AtomicComponentError,
Expand Down Expand Up @@ -110,6 +111,7 @@ AtomicAutomaticFacetGenerator,
AtomicBreadbox,
AtomicCategoryFacet,
AtomicColorFacet,
AtomicCommerceFacets,
AtomicCommerceLoadMoreProducts,
AtomicCommerceSearchBox,
AtomicComponentError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,28 @@ export class AtomicColorFacet {
export declare interface AtomicColorFacet extends Components.AtomicColorFacet {}


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


export declare interface AtomicCommerceFacets extends Components.AtomicCommerceFacets {}


@ProxyCmp({
})
@Component({
Expand Down
1 change: 1 addition & 0 deletions packages/atomic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
"@storybook/core-server": "8.1.5",
"@storybook/jest": "^0.2.3",
"@storybook/manager-api": "8.1.5",
"@storybook/router": "8.1.6",
"@storybook/test": "8.1.5",
"@storybook/test-runner": "0.18.2",
"@storybook/testing-library": "^0.2.2",
Expand Down
42 changes: 42 additions & 0 deletions packages/atomic/playwright-utils/base-page-object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type {Page} from '@playwright/test';
import {buildArgsParam} from '@storybook/router';
import {JSX} from '../dist/types/components';

export class BasePageObject<
TagName extends keyof JSX.IntrinsicElements,
Component = JSX.IntrinsicElements[TagName],
> {
constructor(
public page: Page,
public tag: TagName
) {}

get hydrated() {
return this.page.locator(`${this.tag}[class*="hydrated"]`);
}

get urlRoot() {
return 'http://localhost:4400/iframe.html';
}

async load(args?: Component, story: string = 'default') {
if (args) {
await this.page.goto(
`${this.urlRoot}?id=${this.tag}--${story}&args=${buildArgsParam(undefined, this.camelToKebab(args))}`
);
} else {
await this.page.goto(`${this.urlRoot}?id=${this.tag}--${story}`);
}
}

private camelToKebab(args: Component) {
const toKebab: Record<string, unknown> = {};
Object.entries(args as Record<string, unknown>).forEach(([key, value]) => {
toKebab[
`attributes-${key.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase()}`
] = value;
});

return toKebab;
}
}
4 changes: 4 additions & 0 deletions packages/atomic/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ export namespace Components {
/**
* The `atomic-commerce-facets` component automatically renders commerce facets based on the Commerce API response.
* Unlike regular facets, which require explicit definition and request in the query, the `atomic-commerce-facets` component dynamically generates facets.
* @alpha
*/
interface AtomicCommerceFacets {
/**
Expand Down Expand Up @@ -3503,6 +3504,7 @@ declare global {
/**
* The `atomic-commerce-facets` component automatically renders commerce facets based on the Commerce API response.
* Unlike regular facets, which require explicit definition and request in the query, the `atomic-commerce-facets` component dynamically generates facets.
* @alpha
*/
interface HTMLAtomicCommerceFacetsElement extends Components.AtomicCommerceFacets, HTMLStencilElement {
}
Expand Down Expand Up @@ -5726,6 +5728,7 @@ declare namespace LocalJSX {
/**
* The `atomic-commerce-facets` component automatically renders commerce facets based on the Commerce API response.
* Unlike regular facets, which require explicit definition and request in the query, the `atomic-commerce-facets` component dynamically generates facets.
* @alpha
*/
interface AtomicCommerceFacets {
/**
Expand Down Expand Up @@ -8793,6 +8796,7 @@ declare module "@stencil/core" {
/**
* The `atomic-commerce-facets` component automatically renders commerce facets based on the Commerce API response.
* Unlike regular facets, which require explicit definition and request in the query, the `atomic-commerce-facets` component dynamically generates facets.
* @alpha
*/
"atomic-commerce-facets": LocalJSX.AtomicCommerceFacets & JSXBase.HTMLAttributes<HTMLAtomicCommerceFacetsElement>;
"atomic-commerce-interface": LocalJSX.AtomicCommerceInterface & JSXBase.HTMLAttributes<HTMLAtomicCommerceInterfaceElement>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ export class AtomicCommerceInterface
private summary!: SearchSummary | ListingSummary;
private context!: Context;
private unsubscribeUrlManager: Unsubscribe = () => {};
private unsubscribeSearchStatus: Unsubscribe = () => {};
private unsubscribeSummary: Unsubscribe = () => {};
private initialized = false;
private store: AtomicCommerceStore;
Expand Down Expand Up @@ -226,7 +225,6 @@ export class AtomicCommerceInterface

public disconnectedCallback() {
this.unsubscribeUrlManager();
this.unsubscribeSearchStatus();
this.unsubscribeSummary();
window.removeEventListener('hashchange', this.onHashChange);
}
Expand Down Expand Up @@ -373,15 +371,6 @@ export class AtomicCommerceInterface
this.type === 'product-listing'
? buildProductListing(this.engine!)
: buildSearch(this.engine!);

this.unsubscribeSearchStatus = this.searchOrListing.subscribe(() => {
if (
!this.searchOrListing.state.isLoading &&
this.store.hasLoadingFlag(FirstSearchExecutedFlag)
) {
this.store.unsetLoadingFlag(FirstSearchExecutedFlag);
}
});
}

private initSummary() {
Expand All @@ -406,6 +395,10 @@ export class AtomicCommerceInterface
firstSearchExecutedSelector,
firstSearchExecuted
);

if (firstSearchExecuted) {
this.store.unsetLoadingFlag(FirstSearchExecutedFlag);
}
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type {Page} from '@playwright/test';
import {BasePageObject} from '../../../../../playwright-utils/base-page-object';

export class AtomicCommerceLoadMoreProductsLocators {
private page: Page;
export class LoadMoreProductsPageObject extends BasePageObject<'atomic-commerce-load-more-products'> {
constructor(page: Page) {
this.page = page;
super(page, 'atomic-commerce-load-more-products');
}

summary({index, total}: {index?: number; total?: number} = {}) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import {test, expect} from './fixture';

test.describe('default', () => {
test.beforeEach(async ({page}) => {
await page.goto(
'http://localhost:4400/iframe.html?id=atomic-commerce-search-box--default&viewMode=story&args=attributes-suggestion-timeout:5000'
);
test.beforeEach(async ({searchBox}) => {
await searchBox.load({suggestionTimeout: 5000});
});

test('should have an enabled search button', async ({searchBox}) => {
Expand Down Expand Up @@ -50,10 +48,8 @@ test.describe('default', () => {
});

test.describe('with instant results & query suggestions', () => {
test.beforeEach(async ({page}) => {
await page.goto(
'http://localhost:4400/iframe.html?id=atomic-commerce-search-box--rich-search-box&viewMode=story&args=attributes-suggestion-timeout:5000'
);
test.beforeEach(async ({searchBox}) => {
await searchBox.load({suggestionTimeout: 5000}, 'rich-search-box');
});

test.describe('after clicking the searchbox input', () => {
Expand Down Expand Up @@ -82,10 +78,12 @@ test.describe('with instant results & query suggestions', () => {
});

test.describe('with disable-search=true and minimum-query-length=1', () => {
test.beforeEach(async ({page}) => {
await page.goto(
'http://localhost:4400/iframe.html?id=atomic-commerce-search-box--default&viewMode=story&args=attributes-disable-search:!true;attributes-minimum-query-length:1;attributes-suggestion-timeout:5000'
);
test.beforeEach(async ({searchBox}) => {
await searchBox.load({
suggestionTimeout: 5000,
disableSearch: true,
minimumQueryLength: 1,
});
});

const testCases = () => {
Expand Down Expand Up @@ -124,10 +122,8 @@ test.describe('with disable-search=true and minimum-query-length=1', () => {
});

test.describe('with minimum-query-length=4', () => {
test.beforeEach(async ({page}) => {
await page.goto(
'http://localhost:4400/iframe.html?id=atomic-commerce-search-box--default&viewMode=story&args=attributes-minimum-query-length:4;attributes-suggestion-timeout:5000'
);
test.beforeEach(async ({searchBox}) => {
await searchBox.load({minimumQueryLength: 4, suggestionTimeout: 5000});
});

const testCases = () => {
Expand Down Expand Up @@ -180,9 +176,10 @@ test.describe('with minimum-query-length=4', () => {
});

test.describe('with a facet & clear-filters set to true', () => {
test.beforeEach(async ({page}) => {
await page.goto(
'http://localhost:4400/iframe.html?id=atomic-commerce-search-box--in-page&args=attributes-clear-filters:!true;attributes-suggestion-timeout:5000'
test.beforeEach(async ({searchBox}) => {
await searchBox.load(
{clearFilters: true, suggestionTimeout: 5000},
'in-page'
);
});

Expand All @@ -198,9 +195,10 @@ test.describe('with a facet & clear-filters set to true', () => {
});

test.describe('with a facet & clear-filters set to false', () => {
test.beforeEach(async ({page}) => {
await page.goto(
'http://localhost:4400/iframe.html?id=atomic-commerce-search-box--in-page&args=attributes-clear-filters:!false;attributes-suggestion-timeout:5000'
test.beforeEach(async ({searchBox}) => {
await searchBox.load(
{clearFilters: false, suggestionTimeout: 5000},
'in-page'
);
});

Expand All @@ -214,21 +212,3 @@ test.describe('with a facet & clear-filters set to false', () => {
await expect(facets.clearFilters()).toBeVisible();
});
});

test.describe('with enable-query-syntax=true', () => {
test.beforeEach(async ({page}) => {
await page.goto(
'http://localhost:4400/iframe.html?id=atomic-commerce-search-box--in-page&viewMode=story&args=attributes-enable-query-syntax:!true;attributes-suggestion-timeout:5000'
);
});

test('should use query syntax', async ({loadMore, searchBox, page}) => {
await loadMore.loadMoreButton.waitFor({state: 'visible'});
await searchBox.searchInput
// eslint-disable-next-line @cspell/spellchecker
.fill('@urihash=bzo5fpM1vf8Xñds1');
await searchBox.submitButton.click();
await expect(loadMore.summary({total: 1})).toBeVisible();
await expect(page.getByText('WiLife Life Jacket WiLife')).toBeVisible();
});
});
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
import {test as base} from '@playwright/test';
import {
AxeFixture,
makeAxeBuilder,
} from '@coveo/atomic/playwrightUtils/base-fixture';
import {test as base} from '@playwright/test';
import {AtomicCommerceLoadMoreProductsLocators as LoadMore} from '../../atomic-commerce-load-more-products/e2e/page-object';
import {AtomicCommerceFacetsLocators as Facets} from '../../facets/atomic-commerce-facets/e2e/page-object';
import {AtomicCommerceSearchBoxLocators as SearchBox} from './page-object';
} from '../../../../../playwright-utils/base-fixture';
import {LoadMoreProductsPageObject} from '../../atomic-commerce-load-more-products/e2e/page-object';
import {FacetsPageObject} from '../../facets/atomic-commerce-facets/e2e/page-object';
import {SearchBoxPageObject} from './page-object';

type MyFixtures = {
searchBox: SearchBox;
facets: Facets;
loadMore: LoadMore;
searchBox: SearchBoxPageObject;
facets: FacetsPageObject;
loadMore: LoadMoreProductsPageObject;
};

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

export class AtomicCommerceSearchBoxLocators {
private page: Page;
export class SearchBoxPageObject extends BasePageObject<'atomic-commerce-search-box'> {
constructor(page: Page) {
this.page = page;
super(page, 'atomic-commerce-search-box');
}

get submitButton() {
Expand Down Expand Up @@ -38,10 +38,6 @@ export class AtomicCommerceSearchBoxLocators {
);
}

get hydrated() {
return this.page.locator('atomic-commerce-search-box[class*="hydrated"]');
}

private listSideAffix(listSide?: 'Left' | 'Right') {
return listSide ? ` In ${listSide} list\\.` : '';
}
Expand Down
Loading

0 comments on commit 8af33c1

Please sign in to comment.