UNPKG

tus-js-client-olalonde

Version:

A pure JavaScript client for the tus resumable upload protocol

629 lines (512 loc) 18.6 kB
/* global FakeBlob tus */ var isBrowser = typeof window !== "undefined"; var isNode = !isBrowser; describe("tus", function () { describe("#Upload", function () { beforeEach(function () { jasmine.Ajax.install(); // Clear localStorage before every test to prevent stored URLs to // interfere with our setup. if (isBrowser) { localStorage.clear(); } }); afterEach(function () { jasmine.Ajax.uninstall(); }); it("should throw if no error handler is available", function () { var upload = new tus.Upload(null); expect(upload.start.bind(upload)).toThrowError("tus: no file or stream to upload provided"); }); it("should upload a file", function (done) { var file = new FakeBlob("hello world".split("")); var options = { endpoint: "http://tus.io/uploads", headers: { Custom: "blargh" }, metadata: { foo: "hello", bar: "world", nonlatin: "słońce" }, withCredentials: true, onProgress: function () {}, fingerprint: function () {} }; spyOn(options, "fingerprint").and.returnValue("fingerprinted"); spyOn(options, "onProgress"); var upload = new tus.Upload(file, options); upload.start(); expect(options.fingerprint).toHaveBeenCalledWith(file); var req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/uploads"); expect(req.method).toBe("POST"); expect(req.requestHeaders.Custom).toBe("blargh"); expect(req.requestHeaders["Tus-Resumable"]).toBe("1.0.0"); expect(req.requestHeaders["Upload-Length"]).toBe(11); if (isBrowser) expect(req.withCredentials).toBe(true); if (isNode || (isBrowser && "btoa" in window)) { expect(req.requestHeaders["Upload-Metadata"]).toBe("foo aGVsbG8=,bar d29ybGQ=,nonlatin c8WCb8WEY2U="); } req.respondWith({ status: 201, responseHeaders: { Location: "http://tus.io/uploads/blargh" } }); expect(upload.url).toBe("http://tus.io/uploads/blargh"); req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/uploads/blargh"); expect(req.method).toBe("PATCH"); expect(req.requestHeaders.Custom).toBe("blargh"); expect(req.requestHeaders["Tus-Resumable"]).toBe("1.0.0"); expect(req.requestHeaders["Upload-Offset"]).toBe(0); expect(req.contentType()).toBe("application/offset+octet-stream"); expect(req.params.size).toBe(11); if (isBrowser) expect(req.withCredentials).toBe(true); req.respondWith({ status: 204, responseHeaders: { "Upload-Offset": 11 } }); expect(options.onProgress).toHaveBeenCalledWith(11, 11); done(); }); it("should create an upload if resuming fails", function (done) { var file = new FakeBlob("hello world".split("")); var options = { endpoint: "http://tus.io/uploads", uploadUrl: "http://tus.io/uploads/resuming" }; var upload = new tus.Upload(file, options); upload.start(); var req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/uploads/resuming"); expect(req.method).toBe("HEAD"); expect(req.requestHeaders["Tus-Resumable"]).toBe("1.0.0"); req.respondWith({ status: 404 }); expect(upload.url).toBe(null); req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/uploads"); expect(req.method).toBe("POST"); expect(req.requestHeaders["Tus-Resumable"]).toBe("1.0.0"); expect(req.requestHeaders["Upload-Length"]).toBe(11); done(); }); it("should resolve relative URLs", function () { var file = new FakeBlob("hello world".split("")); var options = { endpoint: "http://master.tus.io:1080/files/" }; var upload = new tus.Upload(file, options); upload.start(); var req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://master.tus.io:1080/files/"); expect(req.method).toBe("POST"); req.respondWith({ status: 201, responseHeaders: { "Location": "//localhost/uploads/foo" } }); expect(upload.url).toBe("http://localhost/uploads/foo"); req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://localhost/uploads/foo"); expect(req.method).toBe("PATCH"); req.respondWith({ status: 204, responseHeaders: { "Upload-Offset": 11 } }); }); it("should upload a file in chunks", function (done) { var file = new FakeBlob("hello world".split("")); var options = { endpoint: "http://tus.io/uploads", chunkSize: 7, onProgress: function () {}, onChunkComplete: function () {}, fingerprint: function () {} }; spyOn(options, "fingerprint").and.returnValue("fingerprinted"); spyOn(options, "onProgress"); spyOn(options, "onChunkComplete"); var upload = new tus.Upload(file, options); upload.start(); expect(options.fingerprint).toHaveBeenCalledWith(file); var req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/uploads"); expect(req.method).toBe("POST"); expect(req.requestHeaders["Tus-Resumable"]).toBe("1.0.0"); expect(req.requestHeaders["Upload-Length"]).toBe(11); req.respondWith({ status: 201, responseHeaders: { Location: "/uploads/blargh" } }); expect(upload.url).toBe("http://tus.io/uploads/blargh"); req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/uploads/blargh"); expect(req.method).toBe("PATCH"); expect(req.requestHeaders["Tus-Resumable"]).toBe("1.0.0"); expect(req.requestHeaders["Upload-Offset"]).toBe(0); expect(req.contentType()).toBe("application/offset+octet-stream"); expect(req.params.size).toBe(7); req.respondWith({ status: 204, responseHeaders: { "Upload-Offset": 7 } }); req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/uploads/blargh"); expect(req.method).toBe("PATCH"); expect(req.requestHeaders["Tus-Resumable"]).toBe("1.0.0"); expect(req.requestHeaders["Upload-Offset"]).toBe(7); expect(req.contentType()).toBe("application/offset+octet-stream"); expect(req.params.size).toBe(4); req.respondWith({ status: 204, responseHeaders: { "Upload-Offset": 11 } }); expect(options.onProgress).toHaveBeenCalledWith(11, 11); expect(options.onChunkComplete).toHaveBeenCalledWith(7, 7, 11); expect(options.onChunkComplete).toHaveBeenCalledWith(4, 11, 11); done(); }); it("should add the original request to errors", function () { var file = new FakeBlob("hello world".split("")); var err; var options = { endpoint: "http://tus.io/uploads", onError: function (e) { err = e; } }; var upload = new tus.Upload(file, options); upload.start(); var req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/uploads"); expect(req.method).toBe("POST"); req.respondWith({ status: 500, responseHeaders: { Custom: "blargh" } }); expect(upload.url).toBe(null); expect(err.message).toBe("tus: unexpected response while creating upload, originated from request (response code: 500, response text: )"); expect(err.originalRequest).toBeDefined(); expect(err.originalRequest.getResponseHeader("Custom")).toBe("blargh"); }); it("should not resume a finished upload", function (done) { var file = new FakeBlob("hello world".split("")); var options = { endpoint: "http://tus.io/uploads", onProgress: function () {}, onSuccess: function () {}, uploadUrl: "http://tus.io/uploads/resuming" }; spyOn(options, "onProgress"); spyOn(options, "onSuccess"); var upload = new tus.Upload(file, options); upload.start(); var req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/uploads/resuming"); expect(req.method).toBe("HEAD"); expect(req.requestHeaders["Tus-Resumable"]).toBe("1.0.0"); req.respondWith({ status: 204, responseHeaders: { "Upload-Length": "11", "Upload-Offset": "11" } }); expect(options.onProgress).toHaveBeenCalledWith(11, 11); expect(options.onSuccess).toHaveBeenCalled(); done(); }); it("should resume an upload from a specified url", function (done) { var file = new FakeBlob("hello world".split("")); var options = { endpoint: "http://tus.io/uploads", uploadUrl: "http://tus.io/files/upload", onProgress: function () {}, fingerprint: function () {} }; spyOn(options, "fingerprint").and.returnValue("fingerprinted"); spyOn(options, "onProgress"); var upload = new tus.Upload(file, options); upload.start(); expect(options.fingerprint.calls.count()).toEqual(0); expect(upload.url).toBe("http://tus.io/files/upload"); var req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/upload"); expect(req.method).toBe("HEAD"); expect(req.requestHeaders["Tus-Resumable"]).toBe("1.0.0"); req.respondWith({ status: 204, responseHeaders: { "Upload-Length": 11, "Upload-Offset": 3 } }); req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/upload"); expect(req.method).toBe("PATCH"); expect(req.requestHeaders["Tus-Resumable"]).toBe("1.0.0"); expect(req.requestHeaders["Upload-Offset"]).toBe(3); expect(req.contentType()).toBe("application/offset+octet-stream"); expect(req.params.size).toBe(11 - 3); req.respondWith({ status: 204, responseHeaders: { "Upload-Offset": 11 } }); expect(options.onProgress).toHaveBeenCalledWith(11, 11); done(); }); it("should override the PATCH method", function (done) { var file = new FakeBlob("hello world".split("")); var options = { endpoint: "http://tus.io/uploads", uploadUrl: "http://tus.io/files/upload", overridePatchMethod: true }; var upload = new tus.Upload(file, options); upload.start(); var req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/upload"); expect(req.method).toBe("HEAD"); expect(req.requestHeaders["Tus-Resumable"]).toBe("1.0.0"); req.respondWith({ status: 204, responseHeaders: { "Upload-Length": 11, "Upload-Offset": 3 } }); req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/upload"); expect(req.method).toBe("POST"); expect(req.requestHeaders["Tus-Resumable"]).toBe("1.0.0"); expect(req.requestHeaders["Upload-Offset"]).toBe(3); expect(req.requestHeaders["X-HTTP-Method-Override"]).toBe("PATCH"); req.respondWith({ status: 204, responseHeaders: { "Upload-Offset": 11 } }); done(); }); it("should throw if retryDelays is not an array", function () { var file = new FakeBlob("hello world".split("")); var upload = new tus.Upload(file, { endpoint: "http://endpoint/", retryDelays: 44 }); expect(upload.start.bind(upload)).toThrowError("tus: the `retryDelays` option must either be an array or null"); }); it("should retry the upload", function (done) { var file = new FakeBlob("hello world".split("")); var options = { endpoint: "http://tus.io/files/", retryDelays: [10, 10, 10], onSuccess: function () {} }; spyOn(options, "onSuccess"); var upload = new tus.Upload(file, options); upload.start(); var req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/"); expect(req.method).toBe("POST"); req.respondWith({ status: 500 }); setTimeout(function () { req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/"); expect(req.method).toBe("POST"); req.respondWith({ status: 201, responseHeaders: { Location: "/files/foo" } }); req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/foo"); expect(req.method).toBe("PATCH"); req.respondWith({ status: 204, responseHeaders: { "Upload-Offset": 11 } }); expect(options.onSuccess).toHaveBeenCalled(); done(); }, 20); }); it("should not retry if the error has not been caused by a request", function () { var file = new FakeBlob("hello world".split("")); var options = { endpoint: "http://tus.io/files/", retryDelays: [10, 10, 10], onSuccess: function () {}, onError: function () {} }; spyOn(options, "onSuccess"); spyOn(options, "onError"); var upload = new tus.Upload(file, options); spyOn(upload, "_createUpload"); upload.start(); var error = new Error("custom error"); upload._emitError(error); expect(upload._createUpload).toHaveBeenCalledTimes(1); expect(options.onError).toHaveBeenCalledWith(error); expect(options.onSuccess).not.toHaveBeenCalled(); }); it("should stop retrying after all delays have been used", function (done) { var file = new FakeBlob("hello world".split("")); var options = { endpoint: "http://tus.io/files/", retryDelays: [10], onSuccess: function () {}, onError: function () {} }; spyOn(options, "onSuccess"); spyOn(options, "onError"); var upload = new tus.Upload(file, options); upload.start(); var req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/"); expect(req.method).toBe("POST"); req.respondWith({ status: 500 }); setTimeout(function () { expect(options.onError).not.toHaveBeenCalled(); var req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/"); expect(req.method).toBe("POST"); req.respondWith({ status: 500 }); expect(options.onSuccess).not.toHaveBeenCalled(); expect(options.onError).toHaveBeenCalledTimes(1); done(); }, 200); }); it("should stop retrying when the abort function is called", function (done) { var file = new FakeBlob("hello world".split("")); var options = { endpoint: "http://tus.io/files/", retryDelays: [100], onError: function () {} }; spyOn(options, "onError"); var upload = new tus.Upload(file, options); upload.start(); var req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/"); expect(req.method).toBe("POST"); spyOn(upload, "start"); req.respondWith({ status: 500 }); upload.abort(); setTimeout(function () { expect(upload.start).not.toHaveBeenCalled(); done(); }, 200); }); it("should reset the attempt counter if an upload proceeds", function (done) { var file = new FakeBlob("hello world".split("")); var options = { endpoint: "http://tus.io/files/", retryDelays: [10], onError: function () {}, onSuccess: function () {} }; spyOn(options, "onError"); spyOn(options, "onSuccess"); var upload = new tus.Upload(file, options); upload.start(); var req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/"); expect(req.method).toBe("POST"); req.respondWith({ status: 201, responseHeaders: { Location: "/files/foo" } }); req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/foo"); expect(req.method).toBe("PATCH"); req.respondWith({ status: 500 }); setTimeout(function () { req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/foo"); expect(req.method).toBe("HEAD"); req.respondWith({ status: 204, responseHeaders: { "Upload-Offset": 0, "Upload-Length": 11 } }); req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/foo"); expect(req.method).toBe("PATCH"); req.respondWith({ status: 204, responseHeaders: { "Upload-Offset": 5 } }); req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/foo"); expect(req.method).toBe("PATCH"); req.respondWith({ status: 500 }); setTimeout(function () { req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/foo"); expect(req.method).toBe("HEAD"); req.respondWith({ status: 204, responseHeaders: { "Upload-Offset": 5, "Upload-Length": 11 } }); req = jasmine.Ajax.requests.mostRecent(); expect(req.url).toBe("http://tus.io/files/foo"); expect(req.method).toBe("PATCH"); req.respondWith({ status: 204, responseHeaders: { "Upload-Offset": 11 } }); expect(options.onError).not.toHaveBeenCalled(); expect(options.onSuccess).toHaveBeenCalled(); done(); }, 20); }, 20); }); }); });