Adds uppy upload functionality behind a enable_experimental_composer_uploader site setting (default false, and hidden). When enabled this site setting will make the composer-editor-uppy component be used within composer.hbs, which in turn points to a ComposerUploadUppy mixin which overrides the relevant functions from ComposerUpload. This uppy uploader has parity with all the features of jQuery file uploader in the original composer-editor, including: progress tracking error handling number of files validation pasting files dragging and dropping files updating upload placeholders upload markdown resolvers processing actions (the only one we have so far is the media optimization worker by falco, this works) cancelling uploads For now all uploads still go via the /uploads.json endpoint, direct S3 support will be added later. Also included in this PR are some changes to the media optimization service, to support uppy's different file data structures, and also to make the promise tracking and resolving more robust. Currently it uses the file name to track promises, we can switch to something more unique later if needed. Does not include custom upload handlers, that will come in a later PR, it is a tricky problem to handle. Also, this new functionality will not be used in encrypted PMs because encrypted PM uploads rely on custom upload handlers.
194 lines
5.8 KiB
JavaScript
194 lines
5.8 KiB
JavaScript
import UppyChecksum from "discourse/lib/uppy-checksum-plugin";
|
|
import { module, test } from "qunit";
|
|
import sinon from "sinon";
|
|
|
|
class FakeUppy {
|
|
constructor() {
|
|
this.preprocessors = [];
|
|
this.emitted = [];
|
|
this.files = {
|
|
"uppy-test/file/vv2/xvejg5w/blah/png-1d-1d-2v-1d-1e-image/jpeg-9043429-1624921727764": {
|
|
meta: {},
|
|
data: createFile("test1.png"),
|
|
},
|
|
"uppy-test/file/blah1/ads37x2/blah1/png-1d-1d-2v-1d-1e-image/jpeg-99999-1837921727764": {
|
|
meta: {},
|
|
data: createFile("test2.png"),
|
|
},
|
|
};
|
|
}
|
|
|
|
addPreProcessor(fn) {
|
|
this.preprocessors.push(fn);
|
|
}
|
|
|
|
getFile(id) {
|
|
return this.files[id];
|
|
}
|
|
|
|
emit(event, file, data) {
|
|
this.emitted.push({ event, file, data });
|
|
}
|
|
|
|
setFileMeta(fileId, meta) {
|
|
this.files[fileId].meta = meta;
|
|
}
|
|
}
|
|
|
|
module("Unit | Utility | UppyChecksum Plugin", function () {
|
|
test("sets the options passed in", function (assert) {
|
|
const capabilities = {};
|
|
const fakeUppy = new FakeUppy();
|
|
const plugin = new UppyChecksum(fakeUppy, {
|
|
id: "test-uppy",
|
|
capabilities,
|
|
});
|
|
assert.equal(plugin.id, "test-uppy");
|
|
assert.equal(plugin.capabilities, capabilities);
|
|
});
|
|
|
|
test("it does nothing if not running in a secure context", function (assert) {
|
|
const capabilities = {};
|
|
const fakeUppy = new FakeUppy();
|
|
const plugin = new UppyChecksum(fakeUppy, {
|
|
id: "test-uppy",
|
|
capabilities,
|
|
});
|
|
plugin.install();
|
|
const done = assert.async();
|
|
|
|
sinon.stub(plugin, "_secureContext").returns(false);
|
|
|
|
const fileId =
|
|
"uppy-test/file/vv2/xvejg5w/blah/png-1d-1d-2v-1d-1e-image/jpeg-9043429-1624921727764";
|
|
plugin.uppy.preprocessors[0]([fileId]).then(() => {
|
|
assert.equal(
|
|
plugin.uppy.emitted.length,
|
|
0,
|
|
"no events were fired by the checksum plugin because it returned early"
|
|
);
|
|
done();
|
|
});
|
|
});
|
|
|
|
test("it does nothing if the crypto object + cipher is not available", function (assert) {
|
|
const capabilities = {};
|
|
const fakeUppy = new FakeUppy();
|
|
const plugin = new UppyChecksum(fakeUppy, {
|
|
id: "test-uppy",
|
|
capabilities,
|
|
});
|
|
plugin.install();
|
|
const done = assert.async();
|
|
|
|
sinon.stub(plugin, "_hasCryptoCipher").returns(false);
|
|
|
|
const fileId =
|
|
"uppy-test/file/vv2/xvejg5w/blah/png-1d-1d-2v-1d-1e-image/jpeg-9043429-1624921727764";
|
|
plugin.uppy.preprocessors[0]([fileId]).then(() => {
|
|
assert.equal(
|
|
plugin.uppy.emitted.length,
|
|
0,
|
|
"no events were fired by the checksum plugin because it returned early"
|
|
);
|
|
done();
|
|
});
|
|
});
|
|
|
|
test("it does nothing if the browser is IE11", function (assert) {
|
|
const capabilities = { isIE11: true };
|
|
const fakeUppy = new FakeUppy();
|
|
const plugin = new UppyChecksum(fakeUppy, {
|
|
id: "test-uppy",
|
|
capabilities,
|
|
});
|
|
plugin.install();
|
|
const done = assert.async();
|
|
|
|
const fileId =
|
|
"uppy-test/file/vv2/xvejg5w/blah/png-1d-1d-2v-1d-1e-image/jpeg-9043429-1624921727764";
|
|
plugin.uppy.preprocessors[0]([fileId]).then(() => {
|
|
assert.equal(
|
|
plugin.uppy.emitted.length,
|
|
0,
|
|
"no events were fired by the checksum plugin because it returned early"
|
|
);
|
|
done();
|
|
});
|
|
});
|
|
|
|
test("it gets a sha1 hash of each file and adds it to the file meta", function (assert) {
|
|
const capabilities = {};
|
|
const fakeUppy = new FakeUppy();
|
|
const plugin = new UppyChecksum(fakeUppy, {
|
|
id: "test-uppy",
|
|
capabilities,
|
|
});
|
|
plugin.install();
|
|
const done = assert.async();
|
|
|
|
const fileIds = [
|
|
"uppy-test/file/vv2/xvejg5w/blah/png-1d-1d-2v-1d-1e-image/jpeg-9043429-1624921727764",
|
|
"uppy-test/file/blah1/ads37x2/blah1/png-1d-1d-2v-1d-1e-image/jpeg-99999-1837921727764",
|
|
];
|
|
plugin.uppy.preprocessors[0](fileIds).then(() => {
|
|
assert.equal(plugin.uppy.emitted[0].event, "preprocess-progress");
|
|
assert.equal(plugin.uppy.emitted[1].event, "preprocess-progress");
|
|
assert.equal(plugin.uppy.emitted[2].event, "preprocess-complete");
|
|
assert.equal(plugin.uppy.emitted[3].event, "preprocess-complete");
|
|
|
|
// these checksums are the actual SHA1 hashes of the test file names
|
|
assert.equal(
|
|
plugin.uppy.getFile(fileIds[0]).meta.sha1_checksum,
|
|
"d9bafe64b034b655db018ad0226c6865300ada31"
|
|
);
|
|
assert.equal(
|
|
plugin.uppy.getFile(fileIds[1]).meta.sha1_checksum,
|
|
"cb10341e3efeab45f0bc309a1c497edca4c5a744"
|
|
);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
test("it does nothing if the window.crypto.subtle.digest function throws an error / rejects", function (assert) {
|
|
const capabilities = {};
|
|
const fakeUppy = new FakeUppy();
|
|
const plugin = new UppyChecksum(fakeUppy, {
|
|
id: "test-uppy",
|
|
capabilities,
|
|
});
|
|
plugin.install();
|
|
const done = assert.async();
|
|
|
|
const fileIds = [
|
|
"uppy-test/file/vv2/xvejg5w/blah/png-1d-1d-2v-1d-1e-image/jpeg-9043429-1624921727764",
|
|
"uppy-test/file/blah1/ads37x2/blah1/png-1d-1d-2v-1d-1e-image/jpeg-99999-1837921727764",
|
|
];
|
|
|
|
sinon
|
|
.stub(window.crypto.subtle, "digest")
|
|
.rejects({ message: "Algorithm: Unrecognized name" });
|
|
|
|
plugin.uppy.preprocessors[0](fileIds).then(() => {
|
|
assert.equal(plugin.uppy.emitted[0].event, "preprocess-progress");
|
|
assert.equal(plugin.uppy.emitted[1].event, "preprocess-progress");
|
|
assert.equal(plugin.uppy.emitted[2].event, "preprocess-complete");
|
|
assert.equal(plugin.uppy.emitted[3].event, "preprocess-complete");
|
|
|
|
assert.deepEqual(plugin.uppy.getFile(fileIds[0]).meta, {});
|
|
assert.deepEqual(plugin.uppy.getFile(fileIds[1]).meta, {});
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
function createFile(name, type = "image/png") {
|
|
const file = new Blob([name], {
|
|
type,
|
|
});
|
|
file.name = name;
|
|
return file;
|
|
}
|