build / task (16.x, npm, macOS-latest) (push) Canceled after 0s
build / task (16.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, npm, windows-latest) (push) Canceled after 0s
build / task (16.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (16.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (16.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (16.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (16.x, yarn, windows-latest) (push) Canceled after 0s
build / task (18.x, npm, macOS-latest) (push) Canceled after 0s
build / task (18.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, npm, windows-latest) (push) Canceled after 0s
build / task (18.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (18.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (18.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (18.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (18.x, yarn, windows-latest) (push) Canceled after 0s
build / task (20.x, npm, macOS-latest) (push) Canceled after 0s
build / task (20.x, npm, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, npm, windows-latest) (push) Canceled after 0s
build / task (20.x, pnpm, macOS-latest) (push) Canceled after 0s
build / task (20.x, pnpm, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, pnpm, windows-latest) (push) Canceled after 0s
build / task (20.x, yarn, macOS-latest) (push) Canceled after 0s
build / task (20.x, yarn, ubuntu-latest) (push) Canceled after 0s
build / task (20.x, yarn, windows-latest) (push) Canceled after 0s
88 lines
5.4 KiB
JavaScript
88 lines
5.4 KiB
JavaScript
const { test } = require('node:test')
|
|
const assert = require('node:assert/strict')
|
|
const fs = require('node:fs')
|
|
const path = require('node:path')
|
|
const vue = require('vue')
|
|
const { parse, compileScript, compileTemplate } = require('@vue/compiler-sfc')
|
|
const { compile, root, runtime } = require('./helpers/file-access-runtime.cjs')
|
|
|
|
function setup(relative, props, request, fileAccess = {}) {
|
|
const descriptor = parse(fs.readFileSync(path.join(root, relative), 'utf8')).descriptor
|
|
assert.deepEqual(compileTemplate({ source: descriptor.template.content, filename: relative, id: 'upload-transport' }).errors, [])
|
|
const errors = [], events = [], loaded = { exports: {} }
|
|
const vueHooks = { ...vue, onBeforeUnmount() {} }
|
|
const mocks = {
|
|
vue: vueHooks,
|
|
'/@/utils/request': { default: request },
|
|
'/@/utils/storage': { Session: { get: () => 'token', getTenant: () => '42' } },
|
|
'/@/hooks/message': { useMessage: () => ({ error: value => errors.push(value) }) },
|
|
'/@/utils/other': { default: { adaptationUrl: url => url, addUnit: value => value } },
|
|
'/@/utils/file-access': fileAccess,
|
|
'/@/utils/file-access-dom': { bindFileAccessDom() {} },
|
|
'vue-i18n': { useI18n: () => ({ t: key => key }) },
|
|
'@wangeditor/editor/dist/css/style.css': {},
|
|
'@wangeditor/editor-for-vue': { Toolbar: {}, Editor: {} },
|
|
}
|
|
const globals = { ref: vue.ref, computed: vue.computed, watch: vue.watch,
|
|
getCurrentInstance: () => ({ proxy: { baseURL: '/api' } }), onMounted() {}, nextTick: vue.nextTick }
|
|
new Function('module', 'exports', 'require', ...Object.keys(globals), compile(compileScript(descriptor, { id: 'upload-transport' }).content))(
|
|
loaded, loaded.exports, name => name in mocks ? mocks[name] : require(name), ...Object.values(globals))
|
|
const state = loaded.exports.default.setup(props, { expose() {}, emit: (...args) => events.push(args) })
|
|
return { state, errors, events }
|
|
}
|
|
|
|
test('generic uploads preserve multipart fields, delegate URL adaptation to the common transport, and finish a batch after one file fails', async () => {
|
|
const requests = []
|
|
const file = new File(['%PDF-1.4\n%%EOF'], 'report.PDF', { type: 'application/pdf' })
|
|
for (const type of ['default', 'simple']) {
|
|
const { state, errors, events } = setup('src/components/Upload/index.vue', {
|
|
type, modelValue: '', fileType: ['pdf'], fileSize: 100, data: {}, dir: 'reports', uploadFileUrl: '/admin/sys-file/upload',
|
|
}, async options => { requests.push(options); return { code: 0, data: { url: '/admin/sys-file/oss/file?fileName=report.pdf' } } })
|
|
assert.equal(state.handleBeforeUpload(file), true)
|
|
assert.equal(state.handleBeforeUpload(file), true)
|
|
const result = await state.handleHttpUpload({ action: '/api/admin/sys-file/upload', filename: 'file', file,
|
|
data: { dir: 'reports', groupId: 12, type: 'pdf' } })
|
|
const request = requests.at(-1)
|
|
assert.equal(request.url, '/admin/sys-file/upload')
|
|
assert.equal(request.baseURL, undefined)
|
|
assert.equal(request.data.get('file').name, 'report.PDF')
|
|
assert.equal(request.data.get('dir'), 'reports')
|
|
assert.equal(request.data.get('groupId'), '12')
|
|
assert.equal(request.data.get('type'), 'pdf')
|
|
state.handleUploadSuccess(result, { name: file.name, raw: file, size: file.size })
|
|
assert.equal(events.length, 0)
|
|
state.handleUploadError({ msg: '不支持上传此文件类型' })
|
|
assert.deepEqual(errors, ['不支持上传此文件类型'])
|
|
assert.equal(state.number.value, 0)
|
|
assert.equal(events.find(event => event[0] === 'update:modelValue')[1], '/admin/sys-file/oss/file?fileName=report.pdf')
|
|
}
|
|
})
|
|
|
|
test('rich text images and videos use the common transport, preserve raw saved references, and propagate upload failures', async () => {
|
|
const env = runtime(), requests = [], inserted = []
|
|
const raw = '/admin/sys-file/oss/file?fileName=photo.png'
|
|
let result = { code: 0, data: { url: raw } }
|
|
let transportError
|
|
try {
|
|
const { state, events } = setup('src/components/Editor/index.vue', {
|
|
getHtml: '', placeholder: '', height: '310', width: 'auto', uploadFileUrl: '/admin/sys-file/upload',
|
|
}, async options => { requests.push(options); if (transportError) throw transportError; return result }, env.api)
|
|
for (const name of ['uploadImage', 'uploadVideo']) {
|
|
const file = new File(['test content'], name === 'uploadImage' ? 'photo.png' : 'video.mp4')
|
|
await state.state.editorConfig.MENU_CONF[name].customUpload(file, value => inserted.push(value))
|
|
assert.equal(requests.at(-1).url, '/admin/sys-file/upload')
|
|
assert.equal(requests.at(-1).method, 'post')
|
|
assert.equal(requests.at(-1).data.get('file').name, file.name)
|
|
}
|
|
state.handleChange({ getHtml: () => '<img src="' + env.api.fileUrl(raw) + '">', getText: () => '' })
|
|
const saved = events.find(event => event[0] === 'update:getHtml')[1]
|
|
assert.doesNotMatch(saved, /[?&](?:sign|appId|kid|ts)=/)
|
|
assert.match(saved, /fileName=photo\.png/)
|
|
result = { code: 1, msg: '文件内容与后缀类型不符' }
|
|
await assert.rejects(state.uploadAttr.customUpload(new File(['bad'], 'bad.png'), value => inserted.push(value)), /文件内容与后缀类型不符/)
|
|
transportError = { msg: '上传身份校验失败' }
|
|
await assert.rejects(state.uploadAttr.customUpload(new File(['bad'], 'bad.png'), value => inserted.push(value)), /上传身份校验失败/)
|
|
assert.equal(inserted.length, 2)
|
|
} finally { env.close() }
|
|
})
|