diff --git a/packages/examples/vue/components/pulldown/sina-weibo.vue b/packages/examples/vue/components/pulldown/sina-weibo.vue
new file mode 100644
index 00000000..82af6912
--- /dev/null
+++ b/packages/examples/vue/components/pulldown/sina-weibo.vue
@@ -0,0 +1,157 @@
+
+
+
+
+
+
+
diff --git a/packages/examples/vue/pages/pulldown-entry.vue b/packages/examples/vue/pages/pulldown-entry.vue
index 0c522015..91a85487 100644
--- a/packages/examples/vue/pages/pulldown-entry.vue
+++ b/packages/examples/vue/pages/pulldown-entry.vue
@@ -1,16 +1,24 @@
-
+
+
+ -
+ Default
+
+ -
+ Sina-Weibo(v2.4.0)
+
+
+
+
+
+
-
-
-
\ No newline at end of file
diff --git a/packages/examples/vue/router/index.js b/packages/examples/vue/router/index.js
index cd1a25af..56115d0b 100644
--- a/packages/examples/vue/router/index.js
+++ b/packages/examples/vue/router/index.js
@@ -68,6 +68,9 @@ import ComposeSlideNested from 'vue-example/components/compose/slide-nested'
import IndicatorsMinimap from 'vue-example/components/indicators/minimap'
import IndicatorsParallaxScroll from 'vue-example/components/indicators/parallax-scroll'
+
+import PulldownDefault from 'vue-example/components/pulldown/default'
+import PulldownSinaWeibo from 'vue-example/components/pulldown/sina-weibo'
Vue.use(Router)
export default new Router({
@@ -199,6 +202,16 @@ export default new Router({
{
path: '/pulldown',
component: PullDownEntry,
+ children: [
+ {
+ path: 'default',
+ component: PulldownDefault
+ },
+ {
+ path: 'sina',
+ component: PulldownSinaWeibo
+ }
+ ]
},
{
path: '/scrollbar',
diff --git a/packages/pull-down/src/__tests__/index.spec.ts b/packages/pull-down/src/__tests__/index.spec.ts
index eb478fb9..dfebec1c 100644
--- a/packages/pull-down/src/__tests__/index.spec.ts
+++ b/packages/pull-down/src/__tests__/index.spec.ts
@@ -100,6 +100,7 @@ describe('pull down tests', () => {
const mockFn = jest.fn()
scroll.on(scroll.eventTypes.pullingDown, mockFn)
+ scroll.trigger(scroll.eventTypes.scrollStart)
// simulate pullUp action
scroll.y = -100
scroll.scroller.hooks.trigger(scroll.scroller.hooks.eventTypes.end)
@@ -112,11 +113,31 @@ describe('pull down tests', () => {
expect(mockFn).toHaveBeenCalledTimes(1)
})
+ it('should checkLocationOfThresholdBoundary', () => {
+ const enterThresholdFn = jest.fn()
+ const leaveThresholdFn = jest.fn()
+ scroll.on(scroll.eventTypes.enterThreshold, enterThresholdFn)
+ scroll.on(scroll.eventTypes.leaveThreshold, leaveThresholdFn)
+ scroll.trigger(scroll.eventTypes.scrollStart)
+
+ // enter threshold boundary
+ scroll.y = 20
+ scroll.trigger(scroll.eventTypes.scroll)
+
+ // leave threshold boundary
+ scroll.y = 100
+ scroll.trigger(scroll.eventTypes.scroll)
+
+ expect(enterThresholdFn).toHaveBeenCalledTimes(1)
+ expect(leaveThresholdFn).toHaveBeenCalledTimes(1)
+ })
+
it('should trigger pullingDown once', () => {
const mockFn = jest.fn()
scroll.on(scroll.eventTypes.pullingDown, mockFn)
// when
scroll.y = 100
+ scroll.trigger(scroll.eventTypes.scrollStart)
scroll.scroller.hooks.trigger('end')
scroll.scroller.hooks.trigger('end')
// then
@@ -136,10 +157,10 @@ describe('pull down tests', () => {
})
it('should work well when call finishPullDown()', () => {
- pullDown.pulling = true
+ pullDown.pulling = 2
pullDown.finishPullDown()
- expect(pullDown.pulling).toBe(false)
+ expect(pullDown.pulling).toBe(0)
expect(scroll.scroller.scrollBehaviorY.computeBoundary).toBeCalled()
expect(scroll.resetPosition).toBeCalled()
})
@@ -196,7 +217,7 @@ describe('pull down tests', () => {
it('should call finishPullDown when content DOM changed', () => {
// simulate pullDown action
- pullDown.pulling = true
+ pullDown.pulling = 2
scroll.hooks.trigger(scroll.hooks.eventTypes.contentChanged)
expect(scroll.scroller.scrollBehaviorY.computeBoundary).toBeCalled()
diff --git a/packages/pull-down/src/index.ts b/packages/pull-down/src/index.ts
index 318dd1d1..dc8f1351 100644
--- a/packages/pull-down/src/index.ts
+++ b/packages/pull-down/src/index.ts
@@ -5,6 +5,22 @@ import propertiesConfig from './propertiesConfig'
export type PullDownRefreshOptions = Partial | true
+// pulldownRefresh phase will go through:
+// DEFAULT -> MOVING -> FETCHING
+// or
+// DEFAULT -> MOVING
+const enum PullDownPhase {
+ DEFAULT,
+ MOVING,
+ FETCHING,
+}
+
+const enum ThresholdBoundary {
+ DEFAULT,
+ INSIDE,
+ OUTSIDE,
+}
+
export interface PullDownRefreshConfig {
threshold: number
stop: number
@@ -26,12 +42,15 @@ interface PluginAPI {
autoPullDownRefresh(): void
}
-const PULL_DOWN_HOOKS_NAME = 'pullingDown'
+const PULLING_DOWN_EVENT = 'pullingDown'
+const ENTER_THRESHOLD_EVENT = 'enterThreshold'
+const LEAVE_THRESHOLD_EVENT = 'leaveThreshold'
export default class PullDown implements PluginAPI {
static pluginName = 'pullDownRefresh'
private hooksFn: Array<[EventEmitter, string, Function]>
- pulling: boolean = false
+ pulling: PullDownPhase = PullDownPhase.DEFAULT
+ thresholdBoundary: ThresholdBoundary = ThresholdBoundary.DEFAULT
watching: boolean
options: PullDownRefreshConfig
cachedOriginanMinScrollY: number
@@ -41,6 +60,14 @@ export default class PullDown implements PluginAPI {
this.init()
}
+ private setPulling(status: PullDownPhase) {
+ this.pulling = status
+ }
+
+ private setThresholdBoundary(boundary: ThresholdBoundary) {
+ this.thresholdBoundary = boundary
+ }
+
private init() {
this.handleBScroll()
@@ -52,7 +79,11 @@ export default class PullDown implements PluginAPI {
}
private handleBScroll() {
- this.scroll.registerType([PULL_DOWN_HOOKS_NAME])
+ this.scroll.registerType([
+ PULLING_DOWN_EVENT,
+ ENTER_THRESHOLD_EVENT,
+ LEAVE_THRESHOLD_EVENT,
+ ])
this.scroll.proxy(propertiesConfig)
}
@@ -66,8 +97,7 @@ export default class PullDown implements PluginAPI {
stop: 40,
}
this.options = extend(defaultOptions, userOptions)
- // plugin relies on scrollTo api
- // set it to Realtime make bs dispatch scroll、scrollEnd hooks
+
this.scroll.options.probeType = Probe.Realtime
}
@@ -137,12 +167,62 @@ export default class PullDown implements PluginAPI {
scroller.hooks.eventTypes.end,
this.checkPullDown
)
+
+ this.registerHooks(
+ this.scroll,
+ this.scroll.eventTypes.scrollStart,
+ this.resetStateBeforeScrollStart
+ )
+
+ this.registerHooks(
+ this.scroll,
+ this.scroll.eventTypes.scroll,
+ this.checkLocationOfThresholdBoundary
+ )
+ }
+
+ private resetStateBeforeScrollStart() {
+ // current fetching pulldownRefresh has ended
+ if (!this.isFetchingStatus()) {
+ this.setPulling(PullDownPhase.MOVING)
+ this.setThresholdBoundary(ThresholdBoundary.DEFAULT)
+ }
+ }
+
+ private checkLocationOfThresholdBoundary() {
+ // pulldownRefresh is in the phase of Moving
+ if (this.pulling === PullDownPhase.MOVING) {
+ const scroll = this.scroll
+ // enter threshold boundary
+ const enteredThresholdBoundary =
+ this.thresholdBoundary !== ThresholdBoundary.INSIDE &&
+ this.locateInsideThresholdBoundary()
+ // leave threshold boundary
+ const leftThresholdBoundary =
+ this.thresholdBoundary !== ThresholdBoundary.OUTSIDE &&
+ !this.locateInsideThresholdBoundary()
+ if (enteredThresholdBoundary) {
+ this.setThresholdBoundary(ThresholdBoundary.INSIDE)
+ scroll.trigger(ENTER_THRESHOLD_EVENT)
+ }
+ if (leftThresholdBoundary) {
+ this.setThresholdBoundary(ThresholdBoundary.OUTSIDE)
+ scroll.trigger(LEAVE_THRESHOLD_EVENT)
+ }
+ }
+ }
+
+ private locateInsideThresholdBoundary() {
+ return this.scroll.y <= this.options.threshold
}
private unwatch() {
- const scroller = this.scroll.scroller
+ const scroll = this.scroll
+ const scroller = scroll.scroller
this.watching = false
scroller.hooks.off(scroller.hooks.eventTypes.end, this.checkPullDown)
+ scroll.off(scroll.eventTypes.scrollStart, this.resetStateBeforeScrollStart)
+ scroll.off(scroll.eventTypes.scroll, this.checkLocationOfThresholdBoundary)
}
private checkPullDown() {
@@ -152,12 +232,12 @@ export default class PullDown implements PluginAPI {
return false
}
- if (!this.pulling) {
+ if (this.pulling === PullDownPhase.MOVING) {
this.modifyBehaviorYBoundary(stop)
- this.pulling = true
+ this.setPulling(PullDownPhase.FETCHING)
- this.scroll.trigger(PULL_DOWN_HOOKS_NAME)
+ this.scroll.trigger(PULLING_DOWN_EVENT)
}
this.scroll.scrollTo(
@@ -167,7 +247,11 @@ export default class PullDown implements PluginAPI {
ease.bounce
)
- return this.pulling
+ return this.isFetchingStatus()
+ }
+
+ private isFetchingStatus() {
+ return this.pulling === PullDownPhase.FETCHING
}
private modifyBehaviorYBoundary(stopDistance: number) {
@@ -180,12 +264,12 @@ export default class PullDown implements PluginAPI {
}
finishPullDown() {
- if (this.pulling === true) {
+ if (this.isFetchingStatus()) {
const scrollBehaviorY = this.scroll.scroller.scrollBehaviorY
// restore minScrollY since the hang animation has ended
this.currentMinScrollY = this.cachedOriginanMinScrollY
scrollBehaviorY.computeBoundary()
- this.pulling = false
+ this.setPulling(PullDownPhase.DEFAULT)
this.scroll.resetPosition(this.scroll.options.bounceTime, ease.bounce)
}
}
@@ -205,15 +289,17 @@ export default class PullDown implements PluginAPI {
autoPullDownRefresh() {
const { threshold, stop } = this.options
- if (this.pulling || !this.watching) {
+ if (this.isFetchingStatus() || !this.watching) {
return
}
- this.pulling = true
this.modifyBehaviorYBoundary(stop)
+ this.scroll.trigger(this.scroll.eventTypes.scrollStart)
this.scroll.scrollTo(this.scroll.x, threshold)
- this.scroll.trigger(PULL_DOWN_HOOKS_NAME)
+
+ this.setPulling(PullDownPhase.FETCHING)
+ this.scroll.trigger(PULLING_DOWN_EVENT)
this.scroll.scrollTo(
this.scroll.x,
stop,
diff --git a/packages/scroll-bar/src/__tests__/event-handler.spec.ts b/packages/scroll-bar/src/__tests__/event-handler.spec.ts
index b9ff30bd..cd73b6c0 100644
--- a/packages/scroll-bar/src/__tests__/event-handler.spec.ts
+++ b/packages/scroll-bar/src/__tests__/event-handler.spec.ts
@@ -27,6 +27,8 @@ describe('scroll-bar indicator tests', () => {
wrapper,
direction: IndicatorDirection.Vertical,
fade: true,
+ fadeInTime: 250,
+ fadeOutTime: 500,
interactive: false,
minSize: 8,
isCustom: false,
diff --git a/packages/scroll-bar/src/__tests__/indicator.spec.ts b/packages/scroll-bar/src/__tests__/indicator.spec.ts
index c860b334..dad6ba50 100644
--- a/packages/scroll-bar/src/__tests__/indicator.spec.ts
+++ b/packages/scroll-bar/src/__tests__/indicator.spec.ts
@@ -26,6 +26,8 @@ describe('scroll-bar indicator tests', () => {
wrapper,
direction: IndicatorDirection.Vertical,
fade: true,
+ fadeInTime: 250,
+ fadeOutTime: 500,
interactive: false,
minSize: 8,
isCustom: false,
diff --git a/packages/scroll-bar/src/index.ts b/packages/scroll-bar/src/index.ts
index 4db7eb6f..aa97aaac 100644
--- a/packages/scroll-bar/src/index.ts
+++ b/packages/scroll-bar/src/index.ts
@@ -10,6 +10,8 @@ export type ScrollbarOptions = Partial | true
export interface ScrollbarConfig {
fade: boolean
+ fadeInTime: number
+ fadeOutTime: number
interactive: boolean
customElements: HTMLElement[]
minSize: number
@@ -50,6 +52,8 @@ export default class ScrollBar {
const defaultOptions: ScrollbarConfig = {
fade: true,
+ fadeInTime: 250,
+ fadeOutTime: 500,
interactive: false,
customElements: [],
minSize: 8,
diff --git a/packages/scroll-bar/src/indicator.ts b/packages/scroll-bar/src/indicator.ts
index ad82f7f3..e8995f31 100644
--- a/packages/scroll-bar/src/indicator.ts
+++ b/packages/scroll-bar/src/indicator.ts
@@ -28,6 +28,8 @@ export interface IndicatorOptions {
wrapper: HTMLElement
direction: IndicatorDirection
fade: boolean
+ fadeInTime: number
+ fadeOutTime: number
interactive: boolean
minSize: number
isCustom: boolean
@@ -247,7 +249,8 @@ export default class Indicator {
}
fade(visible?: boolean) {
- const time = visible ? 250 : 500
+ const { fadeInTime, fadeOutTime } = this.options
+ const time = visible ? fadeInTime : fadeOutTime
const wrapper = this.wrapper
wrapper.style[style.transitionDuration as any] = time + 'ms'
wrapper.style.opacity = visible ? '1' : '0'
diff --git a/packages/vuepress-docs/docs/en-US/plugins/pulldown.md b/packages/vuepress-docs/docs/en-US/plugins/pulldown.md
index 41b6f599..7863cf91 100644
--- a/packages/vuepress-docs/docs/en-US/plugins/pulldown.md
+++ b/packages/vuepress-docs/docs/en-US/plugins/pulldown.md
@@ -35,7 +35,9 @@ new BScroll('.bs-wrapper', {
## Demo
-
+- **Basic Usage**
+
+
<<< @/examples/vue/components/pulldown/default.vue?template
@@ -48,6 +50,45 @@ new BScroll('.bs-wrapper', {
+- **Sina-Weibo(v2.4.0)**
+
+
+
+ <<< @/examples/vue/components/pulldown/sina-weibo.vue?template
+
+
+ <<< @/examples/vue/components/pulldown/sina-weibo.vue?script
+
+
+ <<< @/examples/vue/components/pulldown/sina-weibo.vue?style
+
+
+
+
+In order to match the effects of App, in version v2.4.0, pulldown has made some changes and is compatible with previous versions. During a pulldown procedure, there are three internal circulation states, and the states are irreversible. They are as follows:
+
+1. **default**
+
+ The initial state.
+
+2. **moving**
+
+ Moving state, this state represents that the user's finger is manipulating BetterScroll, and the finger is keeping in touch. In this state, BetterScroll will dispatch two events.
+
+ - **enterThreshold**
+
+ Dispatched when BetterScroll scrolls **into** the pulldown threshold area. Inside this event, you can do the logic of texts initialization, such as prompting the user to "pull down to refresh"
+
+ - **leaveThreshold**
+
+ Dispatched when BetterScroll scrolls **out of** the pulldown threshold area. You can prompt the user to "Release finger"
+
+3. **fetching**
+
+ Once the finger went away, the pullingDown event is triggered to execute the logic of fetching data
+
+The state change can only be `default -> moving -> fetching` or `default -> moving`. The latter means that at the moment the user's finger is released, the conditions for triggering the pullingDown event are not met.
+
## pullDownRefresh Options
### threshold
@@ -141,8 +182,18 @@ bs.autoPullDownRefresh()
### `pullingDown`
- **Arguments**: None
- - **Trigger**:A `pullingDown` event is fired when the top pull-down distance is greater than the `threshold` value after touchend.
+ - **Trigger**: A `pullingDown` event is fired when the top pull-down distance is greater than the `threshold` value after touchend.
::: danger Note
After the pull-down refresh action is detected, the consumption opportunity of the `pullingDown` hook is only once, so you need to call `finishPullDown()` to tell BetterScroll to provide the next consumption opportunity of the `pullingDown` event.
-:::
\ No newline at end of file
+:::
+
+### `enterThreshold`
+
+ - **Arguments**: None
+ - **Trigger**: when pulldown is in the **moving** state and **enters** threshold area.
+
+### `leaveThreshold`
+
+ - **Arguments**: None
+ - **Trigger**: when pulldown is in the **moving** state and **leaves** threshold area.
\ No newline at end of file
diff --git a/packages/vuepress-docs/docs/en-US/plugins/scroll-bar.md b/packages/vuepress-docs/docs/en-US/plugins/scroll-bar.md
index 07a43e3a..6b514246 100644
--- a/packages/vuepress-docs/docs/en-US/plugins/scroll-bar.md
+++ b/packages/vuepress-docs/docs/en-US/plugins/scroll-bar.md
@@ -192,7 +192,21 @@ new BScroll('.bs-wrapper', {
- **Type**: `number`
- **Default**: `300`
- the scroll time after the scrollbar track is clicked,.
+ the scroll time after the scrollbar track is clicked.
+
+### fadeInTime
+
+ - **Type**: `number`
+ - **Default**: `250`
+
+ The duration of the animation when the scrollbar fades in.
+
+### fadeOutTime
+
+ - **Type**: `number`
+ - **Default**: `500`
+
+ The duration of the animation when the scrollbar fades out.
:::tip
When `scrollbar` is configured as `true`, the plugin uses the default plugin option.
diff --git a/packages/vuepress-docs/docs/zh-CN/plugins/pulldown.md b/packages/vuepress-docs/docs/zh-CN/plugins/pulldown.md
index 74661889..1296d464 100644
--- a/packages/vuepress-docs/docs/zh-CN/plugins/pulldown.md
+++ b/packages/vuepress-docs/docs/zh-CN/plugins/pulldown.md
@@ -35,7 +35,9 @@ new BScroll('.bs-wrapper', {
## 示例
-
+- **基础使用**
+
+
<<< @/examples/vue/components/pulldown/default.vue?template
@@ -48,6 +50,47 @@ new BScroll('.bs-wrapper', {
+- **仿新浪微博(v2.4.0)**
+
+
+
+ <<< @/examples/vue/components/pulldown/sina-weibo.vue?template
+
+
+ <<< @/examples/vue/components/pulldown/sina-weibo.vue?script
+
+
+ <<< @/examples/vue/components/pulldown/sina-weibo.vue?style
+
+
+
+
+为了拉齐客户端的交互效果,在 v2.4.0 版本,pulldown 内部进行了功能的改造并且兼容以前的版本,在一次 pulldown 的操作过程中,内部存在三个流转的状态,并且状态是不可逆的。分别如下:
+
+1. **default**
+
+ 初始状态。
+
+2. **moving**
+
+ 移动状态,这个状态代表用户的手指正在操控 BetterScroll,手指未移开,在这种状态下,BetterScroll 会派发两个事件。
+
+ - **enterThreshold**
+
+ 当 BetterScroll 滚动到 pulldown 的 threshold 阈值区域**之内**的时候派发,在这个事件内部,你可以做文案初始化的逻辑,比如提示用户“下拉刷新”
+
+ - **leaveThreshold**
+
+ 当 BetterScroll 滚动到 pulldown 的 threshold 阈值区域**之外**的时候派发。你可以提示用户“手指释放刷新”
+
+3. **fetching**
+
+ 手指移开的瞬间,触发 pullingDown 事件,执行获取数据的逻辑
+
+状态的变换只可能是 `default -> moving -> fetching` 或者是 `default -> moving`,后者代表用户的手指在释放的瞬间,没有满足触发 pullingDown 事件的条件。
+
+
+
## pullDownRefresh 选项对象
### threshold
@@ -150,3 +193,13 @@ openPullDown 方法应该配合 closePullDown 一起使用,因为在 pulldown
::: danger 危险
监测到下拉刷新的动作之后,`pullingDown` 钩子的消费机会只有一次,因此你需要调用 `finishPullDown()` 来告诉 BetterScroll 来提供下一次 `pullingDown` 钩子的消费机会。
:::
+
+### `enterThreshold`
+
+- **参数**:无
+- **触发时机**:当 pulldown 正处于 moving 状态,并且**进入** threshold 区域的瞬间。
+
+### `leaveThreshold`
+
+- **参数**:无
+- **触发时机**:当 pulldown 正处于 moving 状态,并且**离开** threshold 区域的瞬间。
\ No newline at end of file
diff --git a/packages/vuepress-docs/docs/zh-CN/plugins/scroll-bar.md b/packages/vuepress-docs/docs/zh-CN/plugins/scroll-bar.md
index 862458bb..09f3d45a 100644
--- a/packages/vuepress-docs/docs/zh-CN/plugins/scroll-bar.md
+++ b/packages/vuepress-docs/docs/zh-CN/plugins/scroll-bar.md
@@ -185,12 +185,20 @@ yarn add @better-scroll/scroll-bar
滚动条轨道被点击之后,滚动距离的计算方式,默认与浏览器的表现形式一样,可以配置为 `'clickedPoint'`,代表滚动条滚动至点击的位置。
-### scrollbarTrackOffsetTime
+### fadeInTime
- **类型**:`number`
- - **默认值**:`300`
+ - **默认值**:`250`
+
+ 滚动条渐显的动画时长。
+
+### fadeOutTime
+
+ - **类型**:`number`
+ - **默认值**:`500`
+
+ 滚动条渐隐的动画时长。
- 滚动条轨道被点击之后,滚动位移的时间
:::tip 提示
当 scrollbar 配置为 true 的时候,插件内部使用的是默认的插件选项对象。
diff --git a/tests/e2e/pulldown/default.e2e.ts b/tests/e2e/pulldown/default.e2e.ts
index d3ec83e1..b817fb73 100644
--- a/tests/e2e/pulldown/default.e2e.ts
+++ b/tests/e2e/pulldown/default.e2e.ts
@@ -9,7 +9,7 @@ describe('Pulldown', () => {
beforeEach(async () => {
// disable cache
await page.setCacheEnabled(false)
- await page.goto('http://0.0.0.0:8932/#/pulldown/')
+ await page.goto('http://0.0.0.0:8932/#/pulldown/default')
})
it('should render DOM correctly', async () => {
@@ -36,7 +36,7 @@ describe('Pulldown', () => {
})
// wait for requesting data
- await page.waitFor(5000)
+ await page.waitFor(3000)
const itemsCounts = await page.$$eval(
'.pulldown-list-item',
(element) => element.length
diff --git a/tests/e2e/pulldown/sina.e2e.ts b/tests/e2e/pulldown/sina.e2e.ts
new file mode 100644
index 00000000..40a221d9
--- /dev/null
+++ b/tests/e2e/pulldown/sina.e2e.ts
@@ -0,0 +1,112 @@
+import { Page } from 'puppeteer'
+import extendTouch from '../../util/extendTouch'
+
+jest.setTimeout(10000000)
+
+const chunk = (array, size) => {
+ let index = 0
+ let resIndex = 0
+ const length = array.length
+ const result = new Array(Math.ceil(length / size))
+
+ while (index < length) {
+ result[resIndex++] = array.slice(index, (index += size))
+ }
+ return result
+}
+
+describe('Pulldown-sina-weibo', () => {
+ let page = (global as any).page as Page
+ extendTouch(page)
+ beforeEach(async () => {
+ // disable cache
+ await page.setCacheEnabled(false)
+ await page.goto('http://0.0.0.0:8932/#/pulldown/sina')
+ })
+
+ it('should render DOM correctly', async () => {
+ await page.waitFor(300)
+
+ const itemsCounts = await page.$$eval(
+ '.pulldown-list-item',
+ (element) => element.length
+ )
+
+ await expect(itemsCounts).toBeGreaterThanOrEqual(20)
+ })
+
+ it('should go through correct phase', async () => {
+ await page.waitFor(300)
+
+ await page.dispatchTouch({
+ type: 'touchStart',
+ touchPoints: [
+ {
+ x: 200,
+ y: 40,
+ },
+ ],
+ })
+ const touchMovePoints = (() => {
+ const start = 70
+ const step = 5
+ const end = 550
+ const x = 200
+ let ret = []
+ for (let i = start; i <= end; i += step) {
+ ret.push({
+ x,
+ y: i,
+ })
+ }
+ // chrome only allow 16 items in touchPoints array
+ return chunk(ret, 16)
+ })()
+ // touchmove
+ for (const touchPoint of touchMovePoints) {
+ await page.dispatchTouch({
+ type: 'touchMove',
+ touchPoints: touchPoint,
+ })
+ }
+
+ const textContent = await page.$$eval(
+ '.pulldown-wrapper',
+ (element) => element[0].textContent
+ )
+ expect(textContent).toContain('Release')
+
+ await page.dispatchTouch({
+ type: 'touchEnd',
+ touchPoints: [
+ {
+ x: 200,
+ y: 560,
+ },
+ ],
+ })
+
+ await page.waitFor(300)
+ // loading
+ const textContent2 = await page.$$eval(
+ '.pulldown-wrapper',
+ (element) => element[0].textContent
+ )
+ expect(textContent2).toContain('Loading...')
+
+ await page.waitFor(3000)
+
+ // refresh succeed
+ const textContent3 = await page.$$eval(
+ '.pulldown-wrapper',
+ (element) => element[0].textContent
+ )
+ expect(textContent3).toContain('Refresh succeed')
+ const itemsCounts = await page.$$eval(
+ '.pulldown-list-item',
+ (element) => element.length
+ )
+ // has loaded
+ expect(itemsCounts).toBeGreaterThanOrEqual(40)
+ })
+})
diff --git a/tests/util/extendTouch.ts b/tests/util/extendTouch.ts
index ee3cc1cf..7bc730a1 100644
--- a/tests/util/extendTouch.ts
+++ b/tests/util/extendTouch.ts
@@ -23,18 +23,44 @@ interface ScrollParams {
repeatDelayMs?: number
}
+interface TouchPoint {
+ x: number
+ y: number
+ radiusX?: number
+ radiusY?: number
+ rotationAngle?: number
+ force?: number
+ tangentialPressure?: number
+ tiltX?: number
+ tiltY?: number
+ twist?: number
+ id?: number
+}
+
+interface TouchesParams {
+ type: 'touchStart' | 'touchEnd' | 'touchMove' | 'touchCancel'
+ touchPoints: TouchPoint[]
+ modifiers?: number // Alt=1, Ctrl=2, Meta/Command=4, Shift=8
+ timestamp?: number
+}
+
const PINCH_NAME = 'Input.synthesizePinchGesture'
const SCROLL_NAME = 'Input.synthesizeScrollGesture'
+const TOUCHES_NAME = 'Input.dispatchTouchEvent'
declare module 'puppeteer' {
interface Touchscreen {
_client: {
- send: (name: string, params: PinchParams | ScrollParams) => Promise
+ send: (
+ name: T,
+ params: PinchParams | ScrollParams | TouchesParams
+ ) => Promise
}
}
interface Page {
dispatchPinch: (pinchParams: PinchParams) => Promise
dispatchScroll: (scrollParams: ScrollParams) => Promise
+ dispatchTouch: (touchesParams: TouchesParams) => Promise
touchsceen: Touchscreen
}
}
@@ -43,10 +69,13 @@ declare module 'puppeteer' {
// since puppeteer is connected to chromium with chromeDevTools
// https://chromedevtools.github.io/devtools-protocol/tot/Input#method-dispatchTouchEvent
export default (page: Page) => {
- page.dispatchPinch = async pinchParams => {
+ page.dispatchPinch = async (pinchParams) => {
await page.touchscreen._client.send(PINCH_NAME, pinchParams)
}
- page.dispatchScroll = async scrollParams => {
+ page.dispatchScroll = async (scrollParams) => {
await page.touchscreen._client.send(SCROLL_NAME, scrollParams)
}
+ page.dispatchTouch = async (touchesParams) => {
+ await page.touchscreen._client.send(TOUCHES_NAME, touchesParams)
+ }
}