diff --git a/package-lock.json b/package-lock.json index 617d860da0b..6eec2d90ecf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57445,6 +57445,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", @@ -58552,6 +58553,34 @@ "win32" ] }, + "packages/atomic/node_modules/@storybook/client-logger": { + "version": "8.1.6", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-8.1.6.tgz", + "integrity": "sha512-QfSoUxS1rmrBzO7o99og9g+Gkm7sTmU5ZOpTkjszjlRqfV6/77eUnUOzUikej4LqPLmlJV5fqGuvoP0aNVksDw==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "packages/atomic/node_modules/@storybook/router": { + "version": "8.1.6", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-8.1.6.tgz", + "integrity": "sha512-tvuhB2uXHEKK640Epm1SqVzPhQ9lXYfF7FX6FleJgVYEvZpJpNTD4RojedQoLI6SUUSXNy1Vs2QV26VM0XIPHQ==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "8.1.6", + "memoizerific": "^1.11.3", + "qs": "^6.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, "packages/atomic/node_modules/@types/node": { "version": "18.19.33", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz", diff --git a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts index 152c04d402f..497f267f3ab 100644 --- a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts +++ b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts @@ -11,6 +11,7 @@ AtomicAutomaticFacetGenerator, AtomicBreadbox, AtomicCategoryFacet, AtomicColorFacet, +AtomicCommerceFacets, AtomicCommerceLoadMoreProducts, AtomicCommerceSearchBox, AtomicComponentError, @@ -110,6 +111,7 @@ AtomicAutomaticFacetGenerator, AtomicBreadbox, AtomicCategoryFacet, AtomicColorFacet, +AtomicCommerceFacets, AtomicCommerceLoadMoreProducts, AtomicCommerceSearchBox, AtomicComponentError, diff --git a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts index 661919c87e8..21f348cd36f 100644 --- a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts +++ b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts @@ -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: '', + // 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({ diff --git a/packages/atomic/package.json b/packages/atomic/package.json index f1c1c9dec4d..66b1ad352b7 100644 --- a/packages/atomic/package.json +++ b/packages/atomic/package.json @@ -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", diff --git a/packages/atomic/playwrightUtils/base-fixture.ts b/packages/atomic/playwright-utils/base-fixture.ts similarity index 100% rename from packages/atomic/playwrightUtils/base-fixture.ts rename to packages/atomic/playwright-utils/base-fixture.ts diff --git a/packages/atomic/playwright-utils/base-page-object.ts b/packages/atomic/playwright-utils/base-page-object.ts new file mode 100644 index 00000000000..17663a8af7d --- /dev/null +++ b/packages/atomic/playwright-utils/base-page-object.ts @@ -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 = {}; + Object.entries(args as Record).forEach(([key, value]) => { + toKebab[ + `attributes-${key.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase()}` + ] = value; + }); + + return toKebab; + } +} diff --git a/packages/atomic/src/components.d.ts b/packages/atomic/src/components.d.ts index 5e5ee24d848..5116f92a6df 100644 --- a/packages/atomic/src/components.d.ts +++ b/packages/atomic/src/components.d.ts @@ -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 { /** @@ -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 { } @@ -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 { /** @@ -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; "atomic-commerce-interface": LocalJSX.AtomicCommerceInterface & JSXBase.HTMLAttributes; diff --git a/packages/atomic/src/components/commerce/atomic-commerce-interface/atomic-commerce-interface.tsx b/packages/atomic/src/components/commerce/atomic-commerce-interface/atomic-commerce-interface.tsx index 0da28c7368e..afacd4e1b23 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-interface/atomic-commerce-interface.tsx +++ b/packages/atomic/src/components/commerce/atomic-commerce-interface/atomic-commerce-interface.tsx @@ -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; @@ -226,7 +225,6 @@ export class AtomicCommerceInterface public disconnectedCallback() { this.unsubscribeUrlManager(); - this.unsubscribeSearchStatus(); this.unsubscribeSummary(); window.removeEventListener('hashchange', this.onHashChange); } @@ -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() { @@ -406,6 +395,10 @@ export class AtomicCommerceInterface firstSearchExecutedSelector, firstSearchExecuted ); + + if (firstSearchExecuted) { + this.store.unsetLoadingFlag(FirstSearchExecutedFlag); + } }); } diff --git a/packages/atomic/src/components/commerce/atomic-commerce-load-more-products/e2e/page-object.ts b/packages/atomic/src/components/commerce/atomic-commerce-load-more-products/e2e/page-object.ts index b00512014d4..21fe40afe45 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-load-more-products/e2e/page-object.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-load-more-products/e2e/page-object.ts @@ -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} = {}) { diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/atomic-commerce-search-box.e2e.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/atomic-commerce-search-box.e2e.ts index f7ad243c2e3..3e34756ba45 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/atomic-commerce-search-box.e2e.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/atomic-commerce-search-box.e2e.ts @@ -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}) => { @@ -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', () => { @@ -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 = () => { @@ -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 = () => { @@ -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' ); }); @@ -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' ); }); @@ -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(); - }); -}); diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/fixture.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/fixture.ts index feb9519eb06..aa62c3a1ef4 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/fixture.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/fixture.ts @@ -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({ 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'; diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/page-object.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/page-object.ts index dc868c75652..21e15b6fd77 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/page-object.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/page-object.ts @@ -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() { @@ -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\\.` : ''; } diff --git a/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/atomic-commerce-facets.new.stories.tsx b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/atomic-commerce-facets.new.stories.tsx new file mode 100644 index 00000000000..0a3773c1a34 --- /dev/null +++ b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/atomic-commerce-facets.new.stories.tsx @@ -0,0 +1,72 @@ +import { + playExecuteFirstSearch, + 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 type {Meta, StoryObj as Story} from '@storybook/web-components'; +import {html} from 'lit/static-html.js'; + +const {decorator, play} = wrapInCommerceInterface({skipFirstSearch: true}); + +const meta: Meta = { + component: 'atomic-commerce-facets', + title: 'Atomic-Commerce/Facets', + id: 'atomic-commerce-facets', + render: renderComponent, + decorators: [decorator], + parameters, + play, +}; + +export default meta; + +export const Default: Story = { + name: 'atomic-commerce-facets', + play: async (context) => { + await play(context); + await playExecuteFirstSearch(context); + }, +}; + +export const InPage: Story = { + name: 'In a page', + decorators: [ + (story) => + html` + + + ${story()} + + + + + + + + + + + + + + + + `, + ], + play: async (context) => { + await play(context); + await playExecuteFirstSearch(context); + }, +}; + +export const LoadingState: Story = { + name: 'Loading state', + play: async (context) => { + await play(context); + }, +}; diff --git a/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/atomic-commerce-facets.tsx b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/atomic-commerce-facets.tsx index ebe5aca1e85..dfd22b4e06f 100644 --- a/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/atomic-commerce-facets.tsx +++ b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/atomic-commerce-facets.tsx @@ -26,7 +26,7 @@ import {CommerceBindings as Bindings} from '../../atomic-commerce-interface/atom * 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. * - * @internal + * @alpha */ @Component({ tag: 'atomic-commerce-facets', diff --git a/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/atomic-commerce-facets.e2e.ts b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/atomic-commerce-facets.e2e.ts new file mode 100644 index 00000000000..c4719114b91 --- /dev/null +++ b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/atomic-commerce-facets.e2e.ts @@ -0,0 +1,43 @@ +import {test, expect} from './fixture'; + +test.describe('default', async () => { + test('should be A11y compliant', async ({facets, makeAxeBuilder}) => { + await facets.load(); + await facets.hydrated.waitFor(); + const accessibilityResults = await makeAxeBuilder().analyze(); + + expect(accessibilityResults.violations).toEqual([]); + }); + + test('should display facets', async ({facets}) => { + await facets.load(); + await expect(facets.standardFacets.first()).toBeVisible(); + await expect(facets.numericFacets.first()).toBeVisible(); + await expect(facets.categoryFacets.first()).toBeVisible(); + }); + + test('should collapse facets when set to 1', async ({facets}) => { + await facets.load({ + collapseFacetsAfter: 1, + }); + await expect(facets.expandedFacets).toHaveCount(1); + await expect(facets.collapsedFacets).toHaveCount(4); + }); + + test('should disable collapse facets when set to -1', async ({facets}) => { + await facets.load({ + collapseFacetsAfter: -1, + }); + await expect(facets.collapsedFacets).toHaveCount(0); + await expect(facets.expandedFacets).toHaveCount(5); + }); +}); + +test.describe('loading state', async () => { + test('should display placeholder equal to the collapse facet after property', async ({ + facets, + }) => { + await facets.load({collapseFacetsAfter: 5}, 'loading-state'); + await expect(facets.placeholders).toHaveCount(5); + }); +}); diff --git a/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/fixture.ts b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/fixture.ts new file mode 100644 index 00000000000..52a2273ebc5 --- /dev/null +++ b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/fixture.ts @@ -0,0 +1,18 @@ +import {test as base} from '@playwright/test'; +import { + AxeFixture, + makeAxeBuilder, +} from '../../../../../../playwright-utils/base-fixture'; +import {FacetsPageObject} from './page-object'; + +interface TestFixture { + facets: FacetsPageObject; +} + +export const test = base.extend({ + makeAxeBuilder, + facets: async ({page}, use) => { + await use(new FacetsPageObject(page)); + }, +}); +export {expect} from '@playwright/test'; diff --git a/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/page-object.ts b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/page-object.ts index 27cabbd831b..6c345bb30e2 100644 --- a/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/page-object.ts +++ b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/page-object.ts @@ -1,15 +1,41 @@ import type {Page} from '@playwright/test'; +import {BasePageObject} from '../../../../../../playwright-utils/base-page-object'; -export class AtomicCommerceFacetsLocators { - private page: Page; +export class FacetsPageObject extends BasePageObject<'atomic-commerce-facets'> { constructor(page: Page) { - this.page = page; + super(page, 'atomic-commerce-facets'); + } + get standardFacets() { + return this.page + .getByText('Brand') + .or(this.page.getByText('Color')) + .or(this.page.getByText('Size')); + } + + get numericFacets() { + return this.page.getByText('Price'); + } + + get categoryFacets() { + return this.page.getByText('Category'); + } + + get collapsedFacets() { + return this.page.getByRole('button', {expanded: false}); + } + + get expandedFacets() { + return this.page.getByRole('button', {expanded: true}); } get inclusionFilters() { return this.page.getByLabel(/Inclusion filter/); } + get placeholders() { + return this.page.locator('[part="placeholder"]'); + } + clearFilters(numberOfFilters?: number) { return this.page.getByLabel( new RegExp(`Clear ${numberOfFilters ?? '\\d'} filter for`) diff --git a/packages/atomic/tsconfig.json b/packages/atomic/tsconfig.json index bf0a1ed5230..88a30e3ee65 100644 --- a/packages/atomic/tsconfig.json +++ b/packages/atomic/tsconfig.json @@ -26,6 +26,7 @@ "src/external-builds", "**/*.stories.tsx", "**/*.stories.ts", - "**/*.stories.js" + "**/*.stories.js", + "**/e2e/**/*" ] }