根因:公开路径判定原先只认「带参数」形态(isH5ClaimPath 只认 /h5/<orderNo>), 裸前缀 https://<域名>/h5(后台 claimLinkBase / certLink 的基址)判定不命中, 于是落回主站 HashRouter 被后台路由接管成 https://<域名>/h5/#/admin/dashboard 后台登录页。 - utils/claim-path.ts:isClaimPath / isH5ClaimPath 改为命名空间级判定(含裸前缀与多段路径), 参数解析仍严格;新增 resolveH5ClaimOrderNoFromInput(纯订单号 / 整条链接 / 缺协议 / 带后台残留 hash 都能取出订单号,只有前缀时返回空串) - pages/claim/H5ClaimEntryPage.tsx:新增 /h5 领取入口页,买家输订单号或粘贴完整链接后 跳 /h5/<订单号>,走与分享链接同一套领取流程 - App.tsx:H5ClaimApplication 接管 /h5 整段前缀(/h5 入口页、/h5/:orderNo 领取页、 其它子路径回入口页);useClearStrayHash 抹掉历史残留的 #/admin/dashboard - tests/claim-path.test.ts:补齐裸前缀、相邻前缀 /h5x、粘贴链接解析等回归用例 验证:npm run check 全绿(prettier + eslint + tsc + 143 单测 + vite build)
119 lines
5.3 KiB
TypeScript
119 lines
5.3 KiB
TypeScript
import assert from 'node:assert/strict'
|
||
import test from 'node:test'
|
||
|
||
import {
|
||
isClaimPath,
|
||
isH5ClaimPath,
|
||
resolveClaimToken,
|
||
resolveH5ClaimOrderNo,
|
||
resolveH5ClaimOrderNoFromInput,
|
||
} from '../src/utils/claim-path.ts'
|
||
|
||
test('isClaimPath:/claim 命名空间整段命中(含只有前缀、多段路径)', () => {
|
||
assert.equal(isClaimPath('/claim/abc123'), true)
|
||
assert.equal(isClaimPath('/claim/abc123/'), true)
|
||
assert.equal(isClaimPath('/claim/abc123//'), true)
|
||
// 只有前缀 / 没有 token 也要命中:否则会被主站 HashRouter 接管跳到后台登录
|
||
assert.equal(isClaimPath('/claim'), true)
|
||
assert.equal(isClaimPath('/claim/'), true)
|
||
assert.equal(isClaimPath('/claim/a/b'), true)
|
||
})
|
||
|
||
test('isClaimPath:其它路径、非路径开头的串都不算', () => {
|
||
assert.equal(isClaimPath('/redeem'), false)
|
||
assert.equal(isClaimPath('/h5/123'), false)
|
||
assert.equal(isClaimPath('/admin/claim/abc'), false)
|
||
assert.equal(isClaimPath('/claimx/abc'), false)
|
||
assert.equal(isClaimPath('/'), false)
|
||
assert.equal(isClaimPath(''), false)
|
||
assert.equal(isClaimPath(null), false)
|
||
assert.equal(isClaimPath(undefined), false)
|
||
})
|
||
|
||
test('isClaimPath:打手页是 hash 路由,pathname 为 / 或 hash 串时不命中', () => {
|
||
// 打手链接形如 https://<域名>/#/claim/<token>:pathname 是 '/',hash 才是路由
|
||
assert.equal(isClaimPath('/'), false)
|
||
assert.equal(isClaimPath('#/claim/abc123'), false)
|
||
// 路径本身带 token 时,多出来的 hash 片段会被忽略
|
||
assert.equal(isClaimPath('/claim/abc123#/claim/other'), true)
|
||
})
|
||
|
||
test('resolveClaimToken:取出并解码 token 片段', () => {
|
||
assert.equal(resolveClaimToken('/claim/abc123'), 'abc123')
|
||
assert.equal(resolveClaimToken('/claim/abc123/?x=1'), 'abc123')
|
||
assert.equal(resolveClaimToken('/claim/a%2Fb'), 'a/b')
|
||
assert.equal(resolveClaimToken('/claim/%E4%B8%AD%E6%96%87'), '中文')
|
||
// 非法百分号编码退回原始片段,交给后端判定
|
||
assert.equal(resolveClaimToken('/claim/100%'), '100%')
|
||
// 只有前缀 / 多段路径取不到 token,由页面展示「链接无效」
|
||
assert.equal(resolveClaimToken('/claim'), '')
|
||
assert.equal(resolveClaimToken('/claim/a/b'), '')
|
||
assert.equal(resolveClaimToken('/redeem'), '')
|
||
})
|
||
|
||
test('isH5ClaimPath:/h5 命名空间整段命中(含只有前缀、多段路径)', () => {
|
||
assert.equal(isH5ClaimPath('/h5/2623001922675146'), true)
|
||
assert.equal(isH5ClaimPath('/h5/2623001922675146/'), true)
|
||
// 关键回归:`https://<域名>/h5`(后台配置的链接基址)必须命领取入口,不能落回后台路由
|
||
assert.equal(isH5ClaimPath('/h5'), true)
|
||
assert.equal(isH5ClaimPath('/h5/'), true)
|
||
assert.equal(isH5ClaimPath('/h5/a/b'), true)
|
||
assert.equal(isH5ClaimPath('/h5#/admin/dashboard'), true)
|
||
})
|
||
|
||
test('isH5ClaimPath:相邻前缀与其它路径都不算', () => {
|
||
assert.equal(isH5ClaimPath('/h5x'), false)
|
||
assert.equal(isH5ClaimPath('/h5x/1'), false)
|
||
assert.equal(isH5ClaimPath('/claim/123'), false)
|
||
assert.equal(isH5ClaimPath('/admin/h5/1'), false)
|
||
assert.equal(isH5ClaimPath('/'), false)
|
||
assert.equal(isH5ClaimPath(undefined), false)
|
||
})
|
||
|
||
test('resolveH5ClaimOrderNo:只认 /h5/<一段订单号>,前缀本身与多段路径返回空串', () => {
|
||
assert.equal(resolveH5ClaimOrderNo('/h5/2623001922675146'), '2623001922675146')
|
||
assert.equal(resolveH5ClaimOrderNo('/h5/2623001922675146?from=sms'), '2623001922675146')
|
||
assert.equal(resolveH5ClaimOrderNo('/h5/ORDER-001/'), 'ORDER-001')
|
||
assert.equal(resolveH5ClaimOrderNo('/h5/%E4%B8%AD%E6%96%87'), '中文')
|
||
assert.equal(resolveH5ClaimOrderNo('/h5'), '')
|
||
assert.equal(resolveH5ClaimOrderNo('/h5/'), '')
|
||
assert.equal(resolveH5ClaimOrderNo('/h5/a/b'), '')
|
||
assert.equal(resolveH5ClaimOrderNo('/claim/123'), '')
|
||
})
|
||
|
||
test('resolveH5ClaimOrderNoFromInput:纯订单号原样返回', () => {
|
||
assert.equal(resolveH5ClaimOrderNoFromInput('2628601934580683'), '2628601934580683')
|
||
assert.equal(resolveH5ClaimOrderNoFromInput(' 2628601934580683 '), '2628601934580683')
|
||
assert.equal(resolveH5ClaimOrderNoFromInput('ORDER-001'), 'ORDER-001')
|
||
assert.equal(resolveH5ClaimOrderNoFromInput(''), '')
|
||
assert.equal(resolveH5ClaimOrderNoFromInput(null), '')
|
||
})
|
||
|
||
test('resolveH5ClaimOrderNoFromInput:粘贴整条链接时取路径里的订单号', () => {
|
||
assert.equal(
|
||
resolveH5ClaimOrderNoFromInput('https://ks.khhao.com/h5/2628601934580683'),
|
||
'2628601934580683',
|
||
)
|
||
// 缺协议 / 带查询串 / 带后台残留 hash 都要能取出订单号
|
||
assert.equal(
|
||
resolveH5ClaimOrderNoFromInput('ks.khhao.com/h5/2628601934580683'),
|
||
'2628601934580683',
|
||
)
|
||
assert.equal(
|
||
resolveH5ClaimOrderNoFromInput('https://ks.khhao.com/h5/2628601934580683?from=sms'),
|
||
'2628601934580683',
|
||
)
|
||
assert.equal(
|
||
resolveH5ClaimOrderNoFromInput('https://ks.khhao.com/h5/2628601934580683#/admin/dashboard'),
|
||
'2628601934580683',
|
||
)
|
||
})
|
||
|
||
test('resolveH5ClaimOrderNoFromInput:链接里没有订单号时返回空串(不把整条 URL 当订单号)', () => {
|
||
assert.equal(resolveH5ClaimOrderNoFromInput('https://ks.khhao.com/h5'), '')
|
||
assert.equal(resolveH5ClaimOrderNoFromInput('https://ks.khhao.com/h5/'), '')
|
||
assert.equal(resolveH5ClaimOrderNoFromInput('https://ks.khhao.com/h5#/admin/dashboard'), '')
|
||
// 不是 /h5 领取链接的地址同样取不到订单号
|
||
assert.equal(resolveH5ClaimOrderNoFromInput('https://ks.khhao.com/claim/abc123'), '')
|
||
})
|