Release 2.21.3: multi-file media uploads
This commit is contained in:
1 parent
24a566a1a4
commit
75b01d49a9
8 files changed
+138
-81
No files matched your search
+39
-29
@@ -504,11 +504,11 @@ test('crop loading toggles the frame overlay and accessible busy state', () => {
|
||||
assert.match(source, /addEventListener\('error', function \(\) \{\s+setLoading\(false\);/);
|
||||
});
|
||||
|
||||
test('library upload selection and drop submit the original File and reject invalid files', () => {
|
||||
test('library upload selection and drop accept valid batches and skip invalid files', () => {
|
||||
const source = fs.readFileSync(path.join(__dirname, '../src/web/public/js/settings/media-library.js'), 'utf8');
|
||||
const handlers = {};
|
||||
let submissions = 0;
|
||||
const input = { accept: 'image/png, image/gif, image/svg+xml', dataset: { imageMaxBytes: '1000' }, files: [], addEventListener(name, handler) { handlers[name] = handler; } };
|
||||
const input = { accept: 'image/png, image/gif, image/svg+xml', dataset: { imageMaxBytes: '1000' }, files: [], value: '', addEventListener(name, handler) { handlers[name] = handler; } };
|
||||
const zone = { classList: { add() {}, remove() {} }, addEventListener(name, handler) { handlers[name] = handler; } };
|
||||
const context = vm.createContext({
|
||||
uploadInput: input, uploadZone: zone, uploadName: {},
|
||||
@@ -516,20 +516,21 @@ test('library upload selection and drop submit the original File and reject inva
|
||||
window: { PulseUpload: { showMessage() {}, formatUploadLimit: String } },
|
||||
DataTransfer: class { constructor() { this.files = []; this.items = { add: file => this.files.push(file) }; } }
|
||||
});
|
||||
vm.runInContext(source.slice(source.indexOf('function setUploadFile('), source.indexOf('if (uploadForm) {')), context);
|
||||
for (const type of ['image/png', 'image/gif', 'image/svg+xml']) {
|
||||
const file = { name: 'original', type, size: 42 };
|
||||
input.files = [file];
|
||||
handlers.change();
|
||||
assert.equal(input.files[0], file);
|
||||
handlers.drop({ preventDefault() {}, dataTransfer: { files: [file] } });
|
||||
assert.equal(input.files[0], file);
|
||||
}
|
||||
assert.equal(submissions, 6);
|
||||
input.files = [{ type: 'image/png', size: 1001 }];
|
||||
vm.runInContext(source.slice(source.indexOf('function getUploadFileError('), source.indexOf('if (uploadForm) {')), context);
|
||||
const first = { name: 'first.png', type: 'image/png', size: 42 };
|
||||
const second = { name: 'second.gif', type: 'image/gif', size: 42 };
|
||||
const tooLarge = { name: 'large.png', type: 'image/png', size: 1001 };
|
||||
const unsupported = { name: 'document.txt', type: 'text/plain', size: 1 };
|
||||
input.files = [first, second, tooLarge];
|
||||
handlers.change();
|
||||
handlers.drop({ preventDefault() {}, dataTransfer: { files: [{ type: 'text/plain', size: 1 }] } });
|
||||
assert.equal(submissions, 6);
|
||||
assert.deepEqual(Array.from(input.files), [first, second]);
|
||||
assert.equal(submissions, 1);
|
||||
handlers.drop({ preventDefault() {}, dataTransfer: { files: [first, second] } });
|
||||
assert.deepEqual(Array.from(input.files), [first, second]);
|
||||
assert.equal(submissions, 2);
|
||||
input.files = [unsupported];
|
||||
handlers.change();
|
||||
assert.equal(submissions, 2);
|
||||
const slideForm = fs.readFileSync(path.join(__dirname, '../src/web/views/signage/slides/form.hbs'), 'utf8');
|
||||
assert.doesNotMatch(slideForm, /slide-image-cropper|cropper\.min/);
|
||||
});
|
||||
@@ -826,7 +827,7 @@ test('media library upload and delete routes audit successful asset changes', as
|
||||
}
|
||||
});
|
||||
|
||||
test('media library drop-zone upload reports transfer progress, processing, errors and success', async () => {
|
||||
test('media library uploads a batch sequentially and reports independent failures', async () => {
|
||||
const source = fs.readFileSync(path.join(__dirname, '../src/web/public/js/settings/media-library.js'), 'utf8');
|
||||
const handlers = {};
|
||||
const requests = [];
|
||||
@@ -836,7 +837,7 @@ test('media library drop-zone upload reports transfer progress, processing, erro
|
||||
const label = { textContent: '' };
|
||||
const bar = { setAttribute(name, value) { attributes[name] = value; }, removeAttribute(name) { delete attributes[name]; } };
|
||||
const fill = { style: {}, classList: { toggle() {} }, textContent: '' };
|
||||
const input = { disabled: false, files: [{ name: 'original.png' }], value: 'original.png' };
|
||||
const input = { disabled: false, files: [{ name: 'first.png' }, { name: 'second.png' }, { name: 'third.png' }], value: 'selected' };
|
||||
const closeButton = { disabled: false };
|
||||
let refreshed = 0;
|
||||
let hidden = 0;
|
||||
@@ -851,7 +852,7 @@ test('media library drop-zone upload reports transfer progress, processing, erro
|
||||
uploadModal: { querySelectorAll() { return [closeButton]; }, addEventListener(name, handler) { handlers[name] = handler; } },
|
||||
window: { PulseUpload: { showMessage(message) { messages.push(message); } }, showToast() {} },
|
||||
hideModal() { hidden += 1; }, async refreshGallery() { refreshed += 1; },
|
||||
FormData: class { constructor() { assert.equal(input.disabled, false); } },
|
||||
FormData: class { constructor() { assert.equal(input.disabled, true); this.files = []; } append(name, file) { this.files.push({ name, file }); } },
|
||||
XMLHttpRequest: class {
|
||||
constructor() { this.upload = {}; requests.push(this); }
|
||||
open(method, url) { this.method = method; this.url = url; }
|
||||
@@ -868,12 +869,14 @@ test('media library drop-zone upload reports transfer progress, processing, erro
|
||||
assert.equal(input.disabled, true);
|
||||
assert.equal(closeButton.disabled, true);
|
||||
assert.equal(attributes['aria-busy'], 'true');
|
||||
assert.equal(requests.length, 1);
|
||||
assert.equal(requests[0].data.files[0].file.name, 'first.png');
|
||||
requests[0].upload.onprogress({ lengthComputable: true, loaded: 250, total: 1000 });
|
||||
assert.equal(fill.style.width, '25%');
|
||||
assert.equal(label.textContent, 'Uploading 25%');
|
||||
assert.equal(label.textContent, 'Uploading 1 of 3: first.png (25%)');
|
||||
assert.equal(attributes['aria-valuenow'], '25');
|
||||
requests[0].upload.onload();
|
||||
assert.equal(label.textContent, 'Processing...');
|
||||
assert.equal(label.textContent, 'Processing 1 of 3: first.png');
|
||||
assert.equal(fill.style.width, '100%');
|
||||
assert.equal(attributes['aria-valuenow'], undefined);
|
||||
submit();
|
||||
@@ -884,24 +887,31 @@ test('media library drop-zone upload reports transfer progress, processing, erro
|
||||
requests[0].status = 400;
|
||||
requests[0].responseText = JSON.stringify({ error: 'Upload rejected.' });
|
||||
await requests[0].onload();
|
||||
assert.deepEqual(messages, ['Upload rejected.']);
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
assert.equal(requests.length, 2);
|
||||
assert.equal(requests[1].data.files[0].file.name, 'second.png');
|
||||
requests[1].status = 200;
|
||||
requests[1].responseText = JSON.stringify({ success: true });
|
||||
await requests[1].onload();
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
assert.equal(requests.length, 3);
|
||||
assert.equal(requests[2].data.files[0].file.name, 'third.png');
|
||||
requests[2].ontimeout();
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
assert.deepEqual(messages, ['1 uploaded. first.png: Upload rejected. third.png: Upload timed out.']);
|
||||
assert.equal(input.disabled, false);
|
||||
assert.equal(closeButton.disabled, false);
|
||||
assert.equal(progress.hidden, true);
|
||||
submit();
|
||||
requests[1].ontimeout();
|
||||
assert.equal(input.disabled, false);
|
||||
assert.match(messages[1], /timed out/);
|
||||
submit();
|
||||
requests[2].status = 200;
|
||||
requests[2].responseText = JSON.stringify({ success: true });
|
||||
await requests[2].onload();
|
||||
assert.equal(hidden, 1);
|
||||
assert.equal(refreshed, 1);
|
||||
assert.equal(input.value, '');
|
||||
assert.equal(progress.hidden, true);
|
||||
assert.equal(attributes['aria-busy'], 'false');
|
||||
assert.deepEqual(requests.map(request => request.method), ['POST', 'POST', 'POST']);
|
||||
assert.ok(requests.every(request => request.url === '/media-library'));
|
||||
assert.equal(requests[1].withCredentials, true);
|
||||
const view = fs.readFileSync(path.join(__dirname, '../src/web/views/settings/media-library/list.hbs'), 'utf8');
|
||||
assert.match(view, /data-media-library-upload[^>]*multiple/);
|
||||
assert.match(view, /data-media-library-upload-progress/);
|
||||
assert.match(view, /role="progressbar" aria-label="Media upload progress"/);
|
||||
assert.match(view, /class="progress rounded-pill slide-image-region-upload-zone-progress-bar"/);
|
||||
|
||||
Reference in new issue
Block a user