Просмотр исходного кода

fix: ignore stale activity category responses

Developer 1 неделя назад
Родитель
Сommit
a4829dfbf8
2 измененных файлов с 110 добавлено и 1 удалено
  1. 6 1
      pages/home.vue
  2. 104 0
      tests/home-category-filter-race.test.js

+ 6 - 1
pages/home.vue

@@ -129,6 +129,7 @@
 	const gridIndex = ref(0)
 	const isOver = ref(false)
 	const activityList = ref([])
+	const listRequestVersion = ref(0)
 	const isTypeGridSticky = ref(false)
 	const typeGridOffsetTop = ref(0)
 	const typeGridHeight = ref(0)
@@ -180,6 +181,7 @@
 	}
 	
 	const initList = () => {
+		listRequestVersion.value++;
 		queryParams.value.page = 1;
 		isOver.value = false;
 		activityList.value = [];
@@ -239,7 +241,10 @@
 	}
 	
 	const getActivityList = () => {
-		proxy.$api.get('/core/activity/page',queryParams.value).then(({data:res})=>{
+		const requestVersion = listRequestVersion.value
+		const requestParams = { ...queryParams.value }
+		proxy.$api.get('/core/activity/page',requestParams).then(({data:res})=>{
+			if(requestVersion!==listRequestVersion.value) return
 			if(res.code!==0) return proxy.$showToast(res.msg)
 			// 只对本次新增项做格式化,避免 loadMore 时旧项(已格式化成 "yyyy.MM.dd hh:mm")
 			// 被再次 new Date() 解析为 Invalid Date 导致显示 NaN

+ 104 - 0
tests/home-category-filter-race.test.js

@@ -0,0 +1,104 @@
+import test from 'node:test'
+import assert from 'node:assert/strict'
+import fs from 'node:fs'
+import path from 'node:path'
+import vm from 'node:vm'
+
+const homeFile = path.resolve('pages/home.vue')
+const flushPromises = () => new Promise(resolve => setImmediate(resolve))
+
+function deferred () {
+  let resolve
+  const promise = new Promise(done => { resolve = done })
+  return { promise, resolve }
+}
+
+function mountHomePage () {
+  const source = fs.readFileSync(homeFile, 'utf8')
+  const script = source.match(/<script setup[^>]*>([\s\S]*?)<\/script>/)[1]
+    .replace(/^\s*import\s+.*$/gm, '') +
+    '\nglobalThis.__home = { changeType, activityList, queryParams }'
+
+  const lifecycle = {}
+  const activityRequests = []
+  const api = {
+    get (url, params) {
+      if (url === '/core/advertisement/manage/page') {
+        return Promise.resolve({ data: { code: 0, data: { list: [] } } })
+      }
+      if (url === '/core/activity/category/list') {
+        return Promise.resolve({
+          data: {
+            code: 0,
+            data: [{ id: 'category-1', categoryName: '分类一', iconUrl: 'icon.png' }]
+          }
+        })
+      }
+      if (url === '/core/activity/page') {
+        const request = deferred()
+        activityRequests.push({ params: { ...params }, ...request })
+        return request.promise
+      }
+      throw new Error(`unexpected request: ${url}`)
+    },
+    post () {
+      return Promise.resolve({ data: { code: 0 } })
+    }
+  }
+  const selectorQuery = {
+    in () { return this },
+    select () { return this },
+    boundingClientRect () { return this },
+    exec (callback) { callback([]) }
+  }
+  const context = {
+    ref: value => ({ value }),
+    computed: getter => ({ get value () { return getter() } }),
+    getCurrentInstance: () => ({ proxy: { $api: api, $showToast: () => {} } }),
+    onMounted: callback => { lifecycle.mounted = callback },
+    nextTick: callback => callback(),
+    watch: () => {},
+    useUserStore: () => ({ token: '' }),
+    onLoad: () => {},
+    onShareAppMessage: () => {},
+    onShareTimeline: () => {},
+    onPullDownRefresh: () => {},
+    uni: {
+      getStorageSync: () => '',
+      createSelectorQuery: () => selectorQuery,
+      stopPullDownRefresh: () => {}
+    },
+    Date,
+    console
+  }
+  vm.runInNewContext(script, context)
+  lifecycle.mounted()
+
+  return { home: context.__home, activityRequests }
+}
+
+test('点击分类后忽略晚到的全部活动响应', async () => {
+  const { home, activityRequests } = mountHomePage()
+  await flushPromises()
+
+  assert.equal(activityRequests.length, 1)
+  assert.equal(activityRequests[0].params.categoryId, '')
+
+  home.changeType({ id: 'category-1' })
+  assert.equal(activityRequests.length, 2)
+  assert.equal(activityRequests[1].params.categoryId, 'category-1')
+
+  activityRequests[1].resolve({
+    data: { code: 0, data: { list: [{ id: 'filtered-activity' }] } }
+  })
+  await flushPromises()
+  assert.deepEqual(Array.from(home.activityList.value, item => item.id), ['filtered-activity'])
+
+  activityRequests[0].resolve({
+    data: { code: 0, data: { list: [{ id: 'stale-all-activity' }] } }
+  })
+  await flushPromises()
+
+  assert.deepEqual(Array.from(home.activityList.value, item => item.id), ['filtered-activity'])
+  assert.equal(home.queryParams.value.categoryId, 'category-1')
+})