UNPKG

@better-scroll/core

Version:

Minimalistic core scrolling for BetterScroll, it is pure and tiny

440 lines (392 loc) 12.5 kB
import { Behavior } from '../Behavior' import createAnimater from '../../animater' import Translater from '../../translater' import { OptionsConstructor } from '../../Options' import ActionsHandler from '../../base/ActionsHandler' import Actions from '../Actions' jest.mock('../Behavior') jest.mock('../../animater') jest.mock('../../translater') jest.mock('../../Options') jest.mock('../../base/ActionsHandler') jest.mock('../Actions') import Scroller from '../Scroller' import { createDiv } from '../../__tests__/__utils__/layout' describe('Scroller Class tests', () => { let scroller: Scroller let wrapper: HTMLElement let content: HTMLElement beforeEach(() => { // redefine window.performance // because we will use window.performance.timing.navigationStart // in our file('src/util/lang.ts') Object.defineProperty(window, 'performance', { get() { return undefined }, }) wrapper = createDiv(100, 200, 0, 0) content = createDiv(100, 400, 0, 0) document.body.appendChild(content) wrapper.appendChild(content) let bscrollOptions = new OptionsConstructor() as any scroller = new Scroller(wrapper, content, bscrollOptions) }) it('should init hooks when call constructor function', () => { ;[ 'beforeStart', 'beforeMove', 'beforeScrollStart', 'scrollStart', 'scroll', 'beforeEnd', 'scrollEnd', 'resize', 'beforeRefresh', 'touchEnd', 'flick', 'scrollCancel', 'momentum', 'scrollTo', 'scrollToElement', 'minDistanceScroll', ].forEach((key) => { expect(scroller.hooks.eventTypes).toHaveProperty(key) }) }) describe('bindTranslater', () => { it('should bind beforeTranslate hook', () => { let transform: string[] = [] scroller.translater.hooks.trigger('beforeTranslate', transform) expect(transform).toContain(' translateZ(0)') }) it('should bind translate hook', () => { scroller.actions.getCurrentPos = jest.fn().mockImplementation(() => { return { x: 0, y: 0, } }) scroller.translater.hooks.trigger('translate', { x: 0, y: -20 }) expect(scroller.scrollBehaviorX.updatePosition).toBeCalled() expect(scroller.scrollBehaviorY.updatePosition).toBeCalled() expect(scroller.scrollBehaviorX.updatePosition).toHaveBeenCalledWith(0) expect(scroller.scrollBehaviorY.updatePosition).toHaveBeenCalledWith(-20) }) }) describe('bindAnimater', () => { it('should bind end hook ', () => { let pos = { x: 0, y: 20, } let scrollEndMockHandler = jest.fn() scroller.hooks.on('scrollEnd', scrollEndMockHandler) scroller.scrollBehaviorX.checkInBoundary = jest .fn() .mockImplementation(() => { return { position: 0, inBoundary: true, } }) scroller.scrollBehaviorY.checkInBoundary = jest .fn() .mockImplementation(() => { return { position: 0, inBoundary: true, } }) scroller.animater.hooks.trigger('end', pos) expect(scroller.animater.setPending).toHaveBeenCalledWith(false) expect(scrollEndMockHandler).toHaveBeenCalledWith({ x: 0, y: 20, }) }) it('should bubble hooks', () => { let scrollEndMockHandler = jest.fn() let scrollMockHandler = jest.fn() scroller.hooks.on('scrollEnd', scrollEndMockHandler) scroller.hooks.on('scroll', scrollMockHandler) scroller.animater.hooks.trigger('forceStop') scroller.animater.hooks.trigger('move') expect(scrollEndMockHandler).toBeCalled() expect(scrollMockHandler).toBeCalled() }) }) describe('bindActions', () => { it('bind end hook', () => { let touchEndMockHandler = jest.fn() let e = new Event('touch') as any Object.defineProperty(e, 'target', { get() { return scroller.wrapper }, }) // cancelable scroller end hook scroller.hooks.on(scroller.hooks.eventTypes.touchEnd, touchEndMockHandler) scroller.hooks.trigger(scroller.hooks.eventTypes.touchEnd, { x: 0, y: 0 }) scroller.hooks.on( scroller.hooks.eventTypes.end, jest.fn().mockImplementationOnce(() => true) ) const ret = scroller.actions.hooks.trigger( scroller.actions.hooks.eventTypes.end, e, { x: 0, y: 0 } ) expect(ret).toBe(true) expect(touchEndMockHandler).toBeCalled() expect(touchEndMockHandler).toHaveBeenCalledWith({ x: 0, y: 0, }) /* click operation */ // case 1 scroller.hooks.on( scroller.hooks.eventTypes.checkClick, jest.fn().mockImplementationOnce(() => true) ) scroller.actions.hooks.trigger(scroller.actions.hooks.eventTypes.end, e, { x: 0, y: 0, }) expect(scroller.animater.setForceStopped).toBeCalledWith(false) // case 2 dblclick const mockFn2 = jest.fn() scroller.options.dblclick = true scroller.lastClickTime = Date.now() scroller.wrapper.addEventListener('dblclick', mockFn2) scroller.actions.hooks.trigger(scroller.actions.hooks.eventTypes.end, e, { x: 0, y: 0, }) expect(mockFn2).toBeCalled() // case 3 tap const mockFn3 = jest.fn() scroller.options.tap = 'tap' scroller.wrapper.addEventListener('tap', mockFn3) scroller.actions.hooks.trigger(scroller.actions.hooks.eventTypes.end, e, { x: 0, y: 0, }) expect(mockFn3).toBeCalled() // case 4 click const mockFn4 = jest.fn() scroller.options.click = true scroller.wrapper.addEventListener('click', mockFn4) scroller.actions.hooks.trigger(scroller.actions.hooks.eventTypes.end, e, { x: 0, y: 0, }) expect(mockFn4).toBeCalled() // case 5 force stopped scroller.animater.forceStopped = true const ret2 = scroller.actions.hooks.trigger( scroller.actions.hooks.eventTypes.end, e, { x: 0, y: 0 } ) expect(ret2).toBe(true) }) it('bind scrollEnd hook', () => { let momentumMockHandler = jest.fn() let noop = (() => {}) as any scroller.hooks.on('momentum', momentumMockHandler) scroller.scrollBehaviorX.end = jest.fn().mockImplementation(() => { return { duration: 400, destination: 0, } }) scroller.scrollBehaviorY.end = jest.fn().mockImplementation(() => { return { duration: 400, destination: -20, } }) // flick const mockFn = jest.fn() scroller.hooks.events['flick'] = [noop, noop] scroller.hooks.on(scroller.hooks.eventTypes.flick, mockFn) scroller.actions.hooks.trigger( scroller.actions.hooks.eventTypes.scrollEnd, { x: 0, y: -20 }, 50 ) expect(mockFn).toBeCalled() // momentum scroller.hooks.events['flick'] = [] scroller.actions.hooks.trigger( scroller.actions.hooks.eventTypes.scrollEnd, { x: 0, y: -40 }, 50 ) expect(scroller.animater.setForceStopped).toBeCalledWith(false) // force stop from transition scroller.actions.contentMoved = false scroller.animater.forceStopped = true scroller.actions.hooks.trigger( scroller.actions.hooks.eventTypes.scrollEnd, { x: 0, y: -20 }, 50 ) expect(scroller.animater.setForceStopped).toBeCalledWith(false) const mockFn2 = jest.fn() scroller.actions.contentMoved = true scroller.hooks.on(scroller.hooks.eventTypes.scrollEnd, mockFn2) scroller.actions.hooks.trigger( scroller.actions.hooks.eventTypes.scrollEnd, { x: 0, y: -20 }, 50 ) expect(mockFn2).toBeCalledWith({ x: 0, y: -20, }) }) }) it('should invoke resize method when window is resized', () => { jest.useFakeTimers() const mockFn = jest.fn() scroller.hooks.on(scroller.hooks.eventTypes.resize, mockFn) const resizeEvent = document.createEvent('Event') resizeEvent.initEvent('resize', true, true) window.dispatchEvent(resizeEvent) jest.advanceTimersByTime(60) jest.clearAllTimers() expect(mockFn).toBeCalledTimes(1) // disable scroller scroller.actions.enabled = false resizeEvent.initEvent('resize', true, true) window.dispatchEvent(resizeEvent) expect(mockFn).toBeCalledTimes(1) }) it('should trigger scrollTo hook when invoking scrollTo method', () => { let scrollToMockHandler = jest.fn() scroller.hooks.on('scrollTo', scrollToMockHandler) scroller.actions.getCurrentPos = jest.fn().mockImplementation(() => { return { x: 0, y: 0, } }) scroller.scrollTo(0, -20, 800) expect(scrollToMockHandler).toBeCalledWith({ x: 0, y: -20, }) expect(scroller.animater.move).toBeCalledWith( { x: 0, y: 0, }, { x: 0, y: -20, }, 800, 'cubic-bezier(0.165, 0.84, 0.44, 1)' ) }) it('scrollToElement()', () => { let scrollToElementMockHandler = jest.fn() scroller.hooks.on( scroller.hooks.eventTypes.scrollToElement, scrollToElementMockHandler ) scroller.refresh(scroller.content) scroller.scrollBehaviorX.adjustPosition = jest.fn(() => { return 0 }) scroller.scrollBehaviorY.adjustPosition = jest.fn(() => { return 0 }) scroller.actions.getCurrentPos = jest.fn().mockImplementation(() => { return { x: 0, y: 0, } }) scroller.scrollToElement(content, 0, false, false) expect(scrollToElementMockHandler).toBeCalled() // to a specified position scroller.scrollToElement(content, 0, 0, 0) expect(scrollToElementMockHandler).toHaveBeenLastCalledWith(content, { left: 0, top: 0, }) const mockFn2 = jest.fn() scroller.hooks.on(scroller.hooks.eventTypes.scrollToElement, () => true) scroller.hooks.on(scroller.hooks.eventTypes.scrollToElement, mockFn2) scroller.scrollToElement(content, 0, 0, 0) expect(mockFn2).not.toBeCalled() }) it('scrollBy ', () => { const mockFn = jest.fn() scroller.hooks.on(scroller.hooks.eventTypes.scrollTo, mockFn) scroller.scrollBy(20, 20) expect(mockFn).toBeCalledWith({ x: 20, y: 20, }) }) it('enable() & disable()', () => { scroller.disable() expect(scroller.actions.enabled).toBe(false) scroller.enable() expect(scroller.actions.enabled).toBe(true) }) it('should update postions when invoking updatePositions method', () => { scroller.updatePositions({ x: 20, y: -20, }) expect(scroller.scrollBehaviorX.updatePosition).toHaveBeenCalledWith(20) expect(scroller.scrollBehaviorY.updatePosition).toHaveBeenCalledWith(-20) }) it('refresh()', () => { scroller.options.bindToTarget = true scroller.refresh(document.createElement('p')) expect(scroller.scrollBehaviorX.refresh).toBeCalled() expect(scroller.scrollBehaviorY.refresh).toBeCalled() expect(scroller.actions.refresh).toBeCalled() expect(scroller.actionsHandler.setContent).toBeCalled() }) it('destroy()', () => { scroller.destroy() const keys = [ 'actionsHandler', 'actions', 'animater', 'translater', 'scrollBehaviorX', 'scrollBehaviorY', ] keys.forEach((key) => { expect(scroller[key].destroy).toBeCalled() }) }) it('resetPosition() ', () => { const mockFn = jest.fn() scroller.hooks.on(scroller.hooks.eventTypes.scrollTo, mockFn) scroller.resetPosition() expect(mockFn).toBeCalledWith({ x: 0, y: 0, }) }) it('scrollTo()', () => { // minDistanceScroll const mockFn = jest.fn() scroller.hooks.on(scroller.hooks.eventTypes.minDistanceScroll, mockFn) scroller.scrollTo(0, 0.5, 300) }) it('should not toggle pointer-events when casting last position into integer in touchend handlers', () => { scroller.actions.ensuringInteger = true scroller.translater.hooks.trigger('translate', { x: 0, y: -20 }) expect(scroller.actions.ensuringInteger).toBe(false) }) })