UNPKG

@carbon/ibm-products-web-components

Version:

Carbon for IBM Products Web Components

330 lines (322 loc) 18.2 kB
/** * Copyright IBM Corp. 2024 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { fixture, oneEvent, html } from '@open-wc/testing'; import './interstitial-screen.js'; import './interstitial-screen-header.js'; import './interstitial-screen-body.js'; import './interstitial-screen-body-item.js'; import CDSInterstitialScreenFooter from './interstitial-screen-footer.js'; import { interstitialDetailsSignal } from './interstitial-screen-context.js'; /** * Copyright IBM Corp. 2025 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ const templateSingleStep = (args = {}) => { return html ` <c4p-interstitial-screen ?fullscreen=${args.fullscreen} ?open=${args.open}> <c4p-interstitial-screen-header header-title="Use case-specific title" header-subtitle="Use case-specific sub title" closeIconDescription="Close" ></c4p-interstitial-screen-header> <c4p-interstitial-screen-body> <c4p-interstitial-screen-body-item id="${1}"> <div role="complementary" aria-label="Use case-specific heading" class="c4p--interstitial-screen-view" > <section class="bodyText">body text</section> </div></c4p-interstitial-screen-body-item > </c4p-interstitial-screen-body> <c4p-interstitial-screen-footer></c4p-interstitial-screen-footer> </c4p-interstitial-screen> `; }; const templateMultiStep = (args = {}) => { return html ` <c4p-interstitial-screen ?fullscreen=${args.fullscreen} ?open="true"> <c4p-interstitial-screen-header header-title="Use case-specific title" header-subtitle="Use case-specific sub title " ></c4p-interstitial-screen-header> <c4p-interstitial-screen-body> <c4p-interstitial-screen-body-item id="${1}" stepTitle="step 1"> <div role="complementary" aria-label="Use case-specific heading" class="c4p--interstitial-screen-view" > <section> <h1>Use case-specific heading 1</h1> </section> </div></c4p-interstitial-screen-body-item > <c4p-interstitial-screen-body-item id="${2}" stepTitle="step 2"> <div role="complementary" aria-label="Use case-specific heading" class="c4p--interstitial-screen-view" > <section> <h1>Use case-specific heading 2</h1> </section> </div></c4p-interstitial-screen-body-item > <c4p-interstitial-screen-body-item id="${3}" stepTitle="step 3"> <div role="complementary" aria-label="Use case-specific heading" class="c4p--interstitial-screen-view" > <section> <h1>Use case-specific heading 3</h1> </section> </div></c4p-interstitial-screen-body-item > </c4p-interstitial-screen-body> <c4p-interstitial-screen-footer></c4p-interstitial-screen-footer> </c4p-interstitial-screen> `; }; const prefix = 'c4p'; describe('c4p-interstitial-screen', function () { it('should render single step modal variant', async () => { var _a, _b, _c, _d, _e, _f, _g, _h; const el = await fixture(templateSingleStep({ fullscreen: false, open: true })); // Header const header = el === null || el === void 0 ? void 0 : el.querySelector(`${prefix}-interstitial-screen-header`); const headerShadow = header.shadowRoot; const headerTitle = (_b = (_a = headerShadow.querySelector('h1')) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim(); const headerSubTitle = (_d = (_c = headerShadow .querySelector('h2')) === null || _c === void 0 ? void 0 : _c.textContent) === null || _d === void 0 ? void 0 : _d.trim(); expect(headerTitle).to.equal('Use case-specific title'); expect(headerSubTitle).to.equal('Use case-specific sub title'); // Body const body = el === null || el === void 0 ? void 0 : el.querySelector(`${prefix}-interstitial-screen-body`); const bodyItem = body === null || body === void 0 ? void 0 : body.querySelector(`${prefix}-interstitial-screen-body-item`); const contentText = (_f = (_e = bodyItem === null || bodyItem === void 0 ? void 0 : bodyItem.querySelector('.bodyText')) === null || _e === void 0 ? void 0 : _e.textContent) === null || _f === void 0 ? void 0 : _f.trim(); expect(contentText).to.equal('body text'); // Footer const footer = el === null || el === void 0 ? void 0 : el.querySelector(`${prefix}-interstitial-screen-footer`); const footerShadow = footer.shadowRoot; const startButtonText = (_h = (_g = footerShadow .querySelector('cds-custom-button')) === null || _g === void 0 ? void 0 : _g.textContent) === null || _h === void 0 ? void 0 : _h.trim(); expect(startButtonText).to.equal('Get Started'); }); it('responds to hasCloseIcon and renders closeIconDescription', async () => { var _a, _b, _c; const el = (await fixture(templateSingleStep({ open: true, fullscreen: false }))); expect(el === null || el === void 0 ? void 0 : el.open).toBeTruthy(); const header = el === null || el === void 0 ? void 0 : el.querySelector(`${prefix}-interstitial-screen-header`); expect(header === null || header === void 0 ? void 0 : header.closeIconDescription).toBe('Close'); const headerEle = (_a = header.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('cds-custom-modal-header'); expect(headerEle).to.exist; const closeButton = (_c = (_b = headerEle === null || headerEle === void 0 ? void 0 : headerEle.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('slot')) === null || _c === void 0 ? void 0 : _c.assignedNodes({ flatten: true }).find((node) => node.nodeName.toLowerCase() === 'cds-custom-modal-close-button'); expect(closeButton).to.exist; // Capture both events const beforeClosePromise = oneEvent(el, 'c4p-interstitial-beingclosed'); const closePromise = oneEvent(el, 'c4p-interstitial-closed'); closeButton === null || closeButton === void 0 ? void 0 : closeButton.click(); await el.updateComplete; // Wait for the first event const beforeCloseEvent = await beforeClosePromise; expect(beforeCloseEvent.type).to.equal('c4p-interstitial-beingclosed'); expect(beforeCloseEvent.defaultPrevented).to.be.false; // Wait for the second event const closeEvent = await closePromise; expect(closeEvent.type).to.equal('c4p-interstitial-closed'); expect(el === null || el === void 0 ? void 0 : el.open).to.be.false; }); it('should render multi step modal variant', async () => { var _a, _b, _c, _d, _e, _f, _g, _h; const el = await fixture(templateMultiStep({ fullscreen: false})); //verify c4p-interstitial-opened is dispatched const eventPromise = oneEvent(el, 'c4p-interstitial-opened'); el.open = true; await el.updateComplete; const event = await eventPromise; expect(event).to.exist; expect(event.type).to.equal('c4p-interstitial-opened'); // Header const header = el === null || el === void 0 ? void 0 : el.querySelector(`${prefix}-interstitial-screen-header`); const headerShadow = header.shadowRoot; const headerTitle = (_b = (_a = headerShadow.querySelector('h1')) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim(); const headerSubTitle = (_d = (_c = headerShadow .querySelector('h2')) === null || _c === void 0 ? void 0 : _c.textContent) === null || _d === void 0 ? void 0 : _d.trim(); expect(headerTitle).to.equal('Use case-specific title'); expect(headerSubTitle).to.equal('Use case-specific sub title'); //progress indicator const progressIndicatorStepsLength = (_e = headerShadow === null || headerShadow === void 0 ? void 0 : headerShadow.querySelector(`cds-custom-progress-indicator`)) === null || _e === void 0 ? void 0 : _e.children.length; expect(progressIndicatorStepsLength).to.be.equal(3); //check step 1 is active const step1 = (_f = headerShadow === null || headerShadow === void 0 ? void 0 : headerShadow.querySelector(`cds-custom-progress-indicator`)) === null || _f === void 0 ? void 0 : _f.children[0]; expect(step1 === null || step1 === void 0 ? void 0 : step1.getAttribute('label')).to.equal('step 1'); expect(step1 === null || step1 === void 0 ? void 0 : step1.getAttribute('state')).to.equal('current'); // check content is of step 1 const body = el === null || el === void 0 ? void 0 : el.querySelector(`${prefix}-interstitial-screen-body`); const bodyItem = body === null || body === void 0 ? void 0 : body.querySelector(`${prefix}-interstitial-screen-body-item`); const contentText = (_h = (_g = bodyItem === null || bodyItem === void 0 ? void 0 : bodyItem.querySelector('h1')) === null || _g === void 0 ? void 0 : _g.textContent) === null || _h === void 0 ? void 0 : _h.trim(); expect(contentText).to.equal('Use case-specific heading 1'); }); it('step navigation using footer action buttons', async () => { var _a, _b, _c, _d, _e, _f, _g; const el = await fixture(templateMultiStep({ fullscreen: false})); // Header const header = el.querySelector(`${prefix}-interstitial-screen-header`); const headerShadow = header.shadowRoot; // Step 1 active let stepIndicator = headerShadow.querySelector(`cds-custom-progress-indicator`); const step1 = stepIndicator === null || stepIndicator === void 0 ? void 0 : stepIndicator.children[0]; expect(step1 === null || step1 === void 0 ? void 0 : step1.getAttribute('label')).to.equal('step 1'); expect(step1 === null || step1 === void 0 ? void 0 : step1.getAttribute('state')).to.equal('current'); // Step 1 content check const body = el.querySelector(`${prefix}-interstitial-screen-body`); const bodyItem = body.querySelector(`${prefix}-interstitial-screen-body-item`); expect((_b = (_a = bodyItem === null || bodyItem === void 0 ? void 0 : bodyItem.querySelector('h1')) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim()).to.equal('Use case-specific heading 1'); // Footer const footer = el.querySelector(`${prefix}-interstitial-screen-footer`); const footerShadow = footer.shadowRoot; const nextButton = footerShadow.querySelector('.c4p--interstitial-screen--next-btn'); // Go to Step 2 nextButton.click(); (_c = body .querySelectorAll(`${prefix}-interstitial-screen-body-item`)[1]) === null || _c === void 0 ? void 0 : _c.dispatchEvent(new Event('transitionend', { bubbles: true })); await el.updateComplete; await Promise.all([footer.updateComplete, header.updateComplete]); stepIndicator = headerShadow.querySelector(`cds-custom-progress-indicator`); const step2 = stepIndicator === null || stepIndicator === void 0 ? void 0 : stepIndicator.children[1]; expect(step2 === null || step2 === void 0 ? void 0 : step2.getAttribute('label')).to.equal('step 2'); expect(step2 === null || step2 === void 0 ? void 0 : step2.getAttribute('state')).to.equal('current'); // Go to Step 3 nextButton.click(); (_d = body .querySelectorAll(`${prefix}-interstitial-screen-body-item`)[2]) === null || _d === void 0 ? void 0 : _d.dispatchEvent(new Event('transitionend', { bubbles: true })); await el.updateComplete; await Promise.all([footer.updateComplete, header.updateComplete]); stepIndicator = headerShadow.querySelector(`cds-custom-progress-indicator`); const step3 = stepIndicator === null || stepIndicator === void 0 ? void 0 : stepIndicator.children[2]; expect(step3 === null || step3 === void 0 ? void 0 : step3.getAttribute('label')).to.equal('step 3'); expect(step3 === null || step3 === void 0 ? void 0 : step3.getAttribute('state')).to.equal('current'); // Back to Step 2 const backButton = footerShadow.querySelector('.c4p--interstitial-screen--prev-btn'); expect(backButton).to.exist; backButton.click(); (_e = body .querySelectorAll(`${prefix}-interstitial-screen-body-item`)[1]) === null || _e === void 0 ? void 0 : _e.dispatchEvent(new Event('transitionend', { bubbles: true })); await el.updateComplete; await Promise.all([footer.updateComplete, header.updateComplete]); stepIndicator = headerShadow.querySelector(`cds-custom-progress-indicator`); expect((_f = stepIndicator === null || stepIndicator === void 0 ? void 0 : stepIndicator.children[1]) === null || _f === void 0 ? void 0 : _f.getAttribute('state')).to.equal('current'); // Forward to Step 3 again nextButton.click(); (_g = body .querySelectorAll(`${prefix}-interstitial-screen-body-item`)[2]) === null || _g === void 0 ? void 0 : _g.dispatchEvent(new Event('transitionend', { bubbles: true })); await el.updateComplete; await Promise.all([footer.updateComplete, header.updateComplete]); const startButton = footerShadow.querySelector('.c4p--interstitial-screen--start-btn'); expect(startButton).to.exist; // Close component startButton.click(); await el.updateComplete; expect(el.open).to.be.false; }); it('should render single step fullscreen variant', async () => { const el = await fixture(templateSingleStep({ fullscreen: true, open: true })); expect(el === null || el === void 0 ? void 0 : el.isFullScreen).to.be.true; const style = getComputedStyle(el); expect(style.position).to.equal('fixed'); // Header const header = el === null || el === void 0 ? void 0 : el.querySelector(`${prefix}-interstitial-screen-header`); expect(header).to.exist; // Body const body = el === null || el === void 0 ? void 0 : el.querySelector(`${prefix}-interstitial-screen-body`); expect(body).to.exist; const footer = el === null || el === void 0 ? void 0 : el.querySelector(`${prefix}-interstitial-screen-footer`); expect(footer).to.exist; }); describe('handleAction', () => { let component; const mockCarouselAPI = { next: vi.fn(), prev: vi.fn(), }; const mockDetails = { currentStep: 1, stepDetails: [ { stepTitle: 'Step 1', id: 1 }, { stepTitle: 'Step 2', id: 2 }, { stepTitle: 'Step 3', id: 3 }, ], carouselAPI: mockCarouselAPI, isFullScreen: false, disableActions: { next: false, back: false, cancel: false, }, }; beforeEach(() => { component = new CDSInterstitialScreenFooter(); component.asyncAction = true; interstitialDetailsSignal.get = vi.fn(() => mockDetails); mockCarouselAPI.next.mockClear(); mockCarouselAPI.prev.mockClear(); }); it('should dispatch "c4p-on-action" and proceed when allowed', async () => { const dispatchSpy = vi .spyOn(component, 'dispatchEvent') .mockImplementation((event) => { expect(event.type).toBe('c4p-on-action'); event.detail.proceed(true); return true; }); await component['handleAction']('next'); expect(dispatchSpy).toHaveBeenCalled(); expect(mockCarouselAPI.next).toHaveBeenCalled(); expect(component.loadingAction).toBe(''); }); it('should cancel if proceed(false) is called', async () => { vi.spyOn(component, 'dispatchEvent').mockImplementation((event) => { expect(event.type).toBe('c4p-on-action'); event.detail.proceed(false); return true; }); await component['handleAction']('next'); expect(mockCarouselAPI.next).not.toHaveBeenCalled(); expect(component.loadingAction).toBe(''); }); it('should cancel if event is canceled (dispatchEvent returns false)', async () => { vi.spyOn(component, 'dispatchEvent').mockReturnValue(false); await component['handleAction']('next'); expect(mockCarouselAPI.next).not.toHaveBeenCalled(); }); it('should call prev if actionType is "back"', async () => { vi.spyOn(component, 'dispatchEvent').mockImplementation((event) => { event.detail.proceed(true); return true; }); await component['handleAction']('back'); expect(mockCarouselAPI.prev).toHaveBeenCalled(); }); it('should call _handleUserInitiatedClose if actionType is not "next" or "back"', async () => { component._handleUserInitiatedClose = vi.fn(); vi.spyOn(component, 'dispatchEvent').mockImplementation((event) => { event.detail.proceed(true); return true; }); await component['handleAction']('skip'); expect(component._handleUserInitiatedClose).toHaveBeenCalledWith('skip'); }); }); }); //# sourceMappingURL=interstitial-screen.test.js.map