import { mount } from '@cypress/vue' ; import 'reflect-metadata' ; import Vue from 'vue' ; import StaggeredFadeAndSlide from '../../src/components/animations/staggered-fade-and-slide.vue' ; import { mockedAdapter } from '../../src/adapter/mocked-adapter' ; import { BaseXBus } from '../../src/plugins/x-bus' ; import { XPlugin } from '../../src/plugins/x-plugin' ; import { UrlParams } from '../../src/types/url-params' ; import MainScrollItem from '../../src/x-modules/scroll/components/main-scroll-item.vue' ; import MainScroll from '../../src/x-modules/scroll/components/main-scroll.vue' ; import { scrollXModule } from '../../src/x-modules/scroll/x-module' ; /**
 * Renders a  MainScroll component with the provided options.
 *
 *  param options returns An API to test the  MainScroll component.
  function renderMainScroll ( { itemsCount = 10 , itemHeight = '50px' , threshold , margin , useWindow , windowScrollingElement , template = `
      <MainScroll v-bind="{ threshold, margin, useWindow }">
        <div class="container" ${ ! useWindow ? `data-test="scroll"` : '' }>
          <MainScrollItem
            v-for="item in items"
            class="item"
            tag="article"
            :item="item"
            :data-test="item.id">
            {{ item.id }}
          </MainScrollItem>
        </div>
      </MainScroll>
  ` } = { } ) { const userScrolledToElementSpy = cy . spy ( ) ; XPlugin . resetInstance ( ) ; document . body . dataset . test = document . documentElement . dataset . test = '' ; if ( windowScrollingElement === 'body' ) { document . body . dataset . test = 'scroll' ; } else if ( windowScrollingElement === 'html' ) { document . documentElement . dataset . test = 'scroll' ; } let pendingScroll ; cy . viewport ( 1920 , 200 ) ; mount ( { components : { MainScroll , MainScrollItem , StaggeredFadeAndSlide } , template , data ( ) { return { items : Array . from ( { length : itemsCount } , ( _ , index ) => ( { id : `item-${ index }` } ) ) , threshold , margin , useWindow } ; } , beforeCreate ( ) { XPlugin . bus . on ( 'UserScrolledToElement' ) . subscribe ( userScrolledToElementSpy ) ; if ( pendingScroll ) { XPlugin . bus . emit ( 'ParamsLoadedFromUrl' , < UrlParams > { scroll : `item-${ pendingScroll }` } ) ; } } } , { vue : Vue . extend ( { } ) , plugins : [ [ new XPlugin ( new BaseXBus ( ) ) , { adapter : mockedAdapter , initialXModules : [ scrollXModule ] } ] ] , style : `
        ${ windowScrollingElement === 'body' ? 'html { overflow: hidden; }' : '' }

        html,
        body,
        #__cy_root {
          margin: 0;
          height: 100%;
          max-height: 100%;
        }

        [data-test='scroll'] {
          overflow: auto;
          height: 100%;
        }

        .item {
          height: ${ itemHeight };
        }` } ) ; return { scrollToItem ( index ) { cy . getByDataTest ( `item-${ index }` ) . then ( $0 => $0 . get ( 0 ) . scrollIntoView ( ) ) ; } , scrollBy ( y ) { return cy . getByDataTest ( 'scroll' ) . then ( $0 => { $0 . get ( 0 ) . scrollBy ( 0 , y ) ; } ) ; } , restoreScrollToItem ( index ) { pendingScroll = index ; } , getItem ( index ) { return cy . getByDataTest ( `item-${ index }` ) ; } , getScrollingElement ( ) { return cy . getByDataTest ( 'scroll' ) ; } , userScrolledToElementSpy : ( ) => cy . wrap ( userScrolledToElementSpy ) } ; } describe ( 'testing MainScroll component' , ( ) => { const cases = [ { description : 'when using the html element' , useWindow : true , windowScrollingElement : 'html' } , { description : 'when using the body element' , useWindow : true , windowScrollingElement : 'body' } , { description : 'when using a custom scrolling element' } ] ; cases . forEach ( ( { description , ... defaultParameters } ) => { describe ( description , ( ) => { it ( 'emits the first visible element' , ( ) => { const { scrollToItem , userScrolledToElementSpy } = renderMainScroll ( defaultParameters ) ; userScrolledToElementSpy ( ) . should ( 'have.been.calledOnce' ) . should ( 'have.been.calledWith' , '' ) ; scrollToItem ( 5 ) ; userScrolledToElementSpy ( ) . should ( 'have.been.calledWith' , 'item-5' ) ; } ) ; it ( 'ignores the first element of scroll' , ( ) => { const { scrollToItem , userScrolledToElementSpy } = renderMainScroll ( defaultParameters ) ; scrollToItem ( 1 ) ; userScrolledToElementSpy ( ) . should ( 'have.been.calledWith' , 'item-1' ) ; userScrolledToElementSpy ( ) . should ( 'not.have.been.calledWith' , 'item-0' ) ; scrollToItem ( 0 ) ; userScrolledToElementSpy ( ) . should ( 'not.have.been.calledWith' , 'item-0' ) ; } ) ; it ( 'restores the scroll' , ( ) => { const { restoreScrollToItem , getItem } = renderMainScroll ( defaultParameters ) ; restoreScrollToItem ( 5 ) ; cy . log ( 'Item 5 should be the first one.' ) ; getItem ( 5 ) . should ( $item5 => { expect ( $item5 . get ( 0 ) . getBoundingClientRect ( ) . top ) . to . be . eq ( 0 ) ; } ) ; cy . log ( "Item 4 shouldn't be visible" ) ; getItem ( 4 ) . should ( $item4 => { const item4Bounds = $item4 . get ( 0 ) . getBoundingClientRect ( ) ; const item4Bottom = item4Bounds . top + item4Bounds . height ; expect ( item4Bottom ) . to . be . eq ( 0 ) ; } ) ; } ) ; it ( 'restores the scroll with transitions enabled' , ( ) => { const { restoreScrollToItem , getItem } = renderMainScroll ( { ... defaultParameters , template : `
            <MainScroll v-bind="{ threshold, margin, useWindow }">
              <div data-test="main-scroll">
                <StaggeredFadeAndSlide>
                  <MainScrollItem
                    v-for="item in items"
                    class="item"
                    tag="article"
                    :item="item"
                    :data-test="item.id"
                    :key="item.id">
                    {{ item.id }}
                  </MainScrollItem>
                </StaggeredFadeAndSlide>
              </div>
            </MainScroll>
          ` } ) ; restoreScrollToItem ( 5 ) ; cy . log ( 'Item 5 should be the first one.' ) ; getItem ( 5 ) . should ( 'be.visible' ) . should ( $item5 => { expect ( $item5 . get ( 0 ) . getBoundingClientRect ( ) . top ) . to . be . eq ( 0 ) ; } ) ; cy . log ( "Item 4 shouldn't be visible" ) ; getItem ( 4 ) . should ( $item4 => { const item4Bounds = $item4 . get ( 0 ) . getBoundingClientRect ( ) ; const item4Bottom = item4Bounds . top + item4Bounds . height ; expect ( item4Bottom ) . to . be . eq ( 0 ) ; } ) ; } ) ; it ( 'allows configuring when to consider an element visible' , ( ) => { const { scrollToItem , userScrolledToElementSpy , scrollBy } = renderMainScroll ( { ... defaultParameters , threshold : 1 } ) ; scrollToItem ( 5 ) ; userScrolledToElementSpy ( ) . should ( 'have.been.calledWith' , 'item-5' ) ; userScrolledToElementSpy ( ) . should ( 'not.have.been.calledWith' , 'item-6' ) ; scrollBy ( 1 ) ; userScrolledToElementSpy ( ) . should ( 'have.been.calledWith' , 'item-6' ) ; } ) ; it ( 'allows configuring the bounds of the intersection' , ( ) => { const { scrollToItem , userScrolledToElementSpy , scrollBy } = renderMainScroll ( { ... defaultParameters , margin : '-25px 0px 0px 0px' , threshold : 0.5 , itemHeight : '50px' } ) ; scrollToItem ( 5 ) ; userScrolledToElementSpy ( ) . should ( 'have.been.calledWith' , 'item-5' ) ; userScrolledToElementSpy ( ) . should ( 'not.have.been.calledWith' , 'item-6' ) ; scrollBy ( 1 ) ; userScrolledToElementSpy ( ) . should ( 'have.been.calledWith' , 'item-6' ) ; } ) ; } ) ; } ) ; } ) ; /**
 * Options to render the  MainScroll component with.
  interface RenderMainScrollOptions { /** Number of elements to render. */ itemsCount ? : number ; /** Bounds to adjust the size of the rect that the items should intersect with to be considered
     * visible. */ margin ? : string ; /** The percentage of an element that should be visible to consider it the first one. */ threshold ? : number ; /** The height of each one of the items. */ itemHeight ? : string ; /** The template to render.*/ template ? : string ; /** Enables listening to the global scroll instead of the wrapping element scroll. */ useWindow ? : boolean ; /** When `useWindow=true`, the element that should be scrollable. */ windowScrollingElement ? : 'body' | 'html' ; } /**
 * Test API for the  MainScroll component.
  interface RenderMainScrollAPI { param index returns getItem : ( index : number ) => Cypress . Chainable < JQuery > ; param index restoreScrollToItem : ( index : number ) => void ; param index scrollToItem : ( index : number ) => void ; param y returns scrollBy : ( y : number ) => Cypress . Chainable < JQuery > ; returns getScrollingElement : ( ) => Cypress . Chainable < JQuery > ; /** Spy of  ScrollXEvents . UserScrolledToElement .  userScrolledToElementSpy : ( ) => Cypress . Chainable < Cypress . SinonSpyAgent < any > > ; }