86 lines
4.6 KiB
JavaScript
86 lines
4.6 KiB
JavaScript
const test = require('node:test')
|
|
const assert = require('node:assert/strict')
|
|
const fs = require('node:fs')
|
|
const path = require('node:path')
|
|
const vm = require('node:vm')
|
|
const ts = require('typescript')
|
|
const profile = { nickname: '测试', gender: '男', birthday: '1998-06-18', birthdayCalendar: 'solar', birthHour: '辰时', province: '北京市', city: '北京市', district: '朝阳区', remainingEdits: 5 }
|
|
|
|
function render(env, sheet = false) {
|
|
const exports = {}, updates = [], media = []
|
|
let index = 0
|
|
const source = fs.readFileSync(path.join(__dirname, '../src/pages/profile-edit/index.tsx'), 'utf8')
|
|
const parsed = ts.createSourceFile('profile-edit.tsx', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX)
|
|
const stateNames = []
|
|
const collectStateNames = node => {
|
|
if (ts.isVariableDeclaration(node) && ts.isArrayBindingPattern(node.name)
|
|
&& node.initializer && ts.isCallExpression(node.initializer) && node.initializer.expression.getText(parsed) === 'useState') {
|
|
stateNames.push(node.name.elements[0].name.getText(parsed))
|
|
}
|
|
ts.forEachChild(node, collectStateNames)
|
|
}
|
|
collectStateNames(parsed)
|
|
const avatarSheetIndex = stateNames.indexOf('avatarSheetOpen')
|
|
assert.notEqual(avatarSheetIndex, -1)
|
|
vm.runInNewContext(ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS,
|
|
target: ts.ScriptTarget.ES2019, jsx: ts.JsxEmit.ReactJSX } }).outputText, {
|
|
exports, process: { env: { TARO_ENV: env } }, require: id => {
|
|
if (id === 'react/jsx-runtime') return require(id)
|
|
if (id === 'react') return { useEffect: () => {}, useRef: initial => ({ current: initial }), useState: initial => {
|
|
const current = index++
|
|
let value = typeof initial === 'function' ? initial() : initial
|
|
if (value === null || (value && typeof value === 'object' && 'nickname' in value)) value = profile
|
|
if (initial === true) value = false
|
|
if (current === avatarSheetIndex) value = sheet
|
|
return [value, next => updates.push([current, next])]
|
|
} }
|
|
if (id === '@tarojs/taro') return { useRouter: () => ({ params: {} }), default: {
|
|
getWindowInfo: () => ({ statusBarHeight: 44, safeArea: { top: 44 } }),
|
|
chooseMedia: async options => { media.push(options); return { tempFiles: [{ tempFilePath: 'temp:album', size: 10 }] } }, showToast: () => {}
|
|
} }
|
|
if (id === '@tarojs/components') return { View: 'View', Text: 'Text', Button: 'Button', Input: 'Input', Picker: 'Picker' }
|
|
if (id === 'china-area-data') return { default: {} }
|
|
if (id.endsWith('/NavBar')) return { NavBar: 'NavBar' }
|
|
if (id.endsWith('/UserAvatar')) return { UserAvatar: 'UserAvatar' }
|
|
if (id.endsWith('/BirthdayPicker')) return { BirthdayPicker: 'BirthdayPicker' }
|
|
if (id.endsWith('/frontendPreview')) return { isFrontendPreview: () => false }
|
|
if (id.endsWith('/avatarDemo')) return { isAvatarDemo: () => false }
|
|
return {}
|
|
}
|
|
})
|
|
const collect = node => {
|
|
if (!node || typeof node !== 'object') return []
|
|
if (Array.isArray(node)) return node.flatMap(collect)
|
|
return [node, ...collect(node.props?.children)]
|
|
}
|
|
return { nodes: collect(exports.default()), updates, media,
|
|
avatarSheetUpdates: () => updates.filter(([index]) => index === avatarSheetIndex).map(([, value]) => value) }
|
|
}
|
|
|
|
test('微信端头像本身直接绑定官方chooseAvatar,不再弹自定义菜单', () => {
|
|
const h = render('weapp', true)
|
|
const button = h.nodes.find(node => node.props?.className === 'avatar-button')
|
|
assert.equal(button.props.openType, 'chooseAvatar')
|
|
assert.equal(button.props.onClick, undefined)
|
|
assert.equal(typeof button.props.onChooseAvatar, 'function')
|
|
assert.ok(!h.nodes.some(node => node.props?.className === 'avatar-mask'))
|
|
button.props.onChooseAvatar({ detail: { avatarUrl: 'temp:wechat-avatar' } })
|
|
assert.equal(h.media.length, 0)
|
|
assert.ok(h.updates.some(([, value]) => value === 'temp:wechat-avatar'))
|
|
assert.ok(h.avatarSheetUpdates().includes(false))
|
|
})
|
|
|
|
test('H5保留相册拍照菜单,不伪造微信官方头像选择能力', async () => {
|
|
const h = render('h5')
|
|
const button = h.nodes.find(node => node.props?.className === 'avatar-button')
|
|
assert.equal(button.props.openType, undefined)
|
|
assert.equal(button.props.onChooseAvatar, undefined)
|
|
button.props.onClick()
|
|
assert.ok(h.avatarSheetUpdates().includes(true))
|
|
const opened = render('h5', true)
|
|
const album = opened.nodes.find(node => node.props?.children === '从相册选择')
|
|
await album.props.onClick()
|
|
assert.equal(opened.media.length, 1)
|
|
assert.equal(opened.media[0].sourceType[0], 'album')
|
|
})
|