Skip to content

Commit

Permalink
Added chunk upload JS tests + small adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
Vincent Petry authored and gitmate-bot committed Apr 3, 2018
1 parent 281dcc7 commit 746adf7
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 12 deletions.
30 changes: 19 additions & 11 deletions apps/files/js/file-upload.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014
* Copyright (c) 2018
*
* This file is licensed under the Affero General Public License version 3
* or later.
Expand Down Expand Up @@ -329,8 +329,11 @@ OC.FileUpload.prototype = {
* retry the upload
*/
retry: function() {
this.data.stalled = true;
this.data.abort();
if (!this.data.stalled) {
console.log('Retrying upload ' + this.id);
this.data.stalled = true;
this.data.abort();
}
},

/**
Expand Down Expand Up @@ -828,7 +831,7 @@ OC.Uploader.prototype = _.extend({
this._progressBarInterval = window.setInterval(_.bind(this._updateProgressBar, this), 1000);
this._lastProgress = 0;
},

_updateProgressBar: function() {
var progress = parseInt(this.$uploadprogressbar.attr('data-loaded'), 10);
var total = parseInt(this.$uploadprogressbar.attr('data-total'), 10);
Expand All @@ -840,10 +843,14 @@ OC.Uploader.prototype = _.extend({
// change message if we stalled at 100%
this.$uploadprogressbar.find('.label .desktop').text(t('core', 'Processing files...'));
} else if (new Date().getTime() - this._lastProgressTime >= this._uploadStallTimeout * 1000 ) {
// TODO: move to "fileuploadprogress" event instead and use data.uploadedBytes
// stalling needs to be checked here because the file upload no longer triggers events
// restart upload
this.log('progress stalled'); // retry chunk (and prevent IE from dying)
$.each(this._uploads, function(i,e){ console.log(e.retry()) })
_.each(this._uploads, function(upload) {
// FIXME: harden by only retry pending, not the finished ones
upload.retry();
});
}
}
},
Expand Down Expand Up @@ -880,6 +887,9 @@ OC.Uploader.prototype = _.extend({
var self = this;
options = options || {};

this._uploads = {};
this._knownDirs = {};

this.fileList = options.fileList;
this.filesClient = options.filesClient || OC.Files.getClient();
this.davClient = new OC.Files.Client({
Expand All @@ -900,7 +910,7 @@ OC.Uploader.prototype = _.extend({
$uploadEl = $($uploadEl);
this.$uploadEl = $uploadEl;

this.$uploadprogressbar = $('#uploadprogressbar')
this.$uploadprogressbar = $('#uploadprogressbar');

if ($uploadEl.exists()) {
$('#uploadprogresswrapper .stop').on('click', function() {
Expand All @@ -912,12 +922,12 @@ OC.Uploader.prototype = _.extend({
dropZone: options.dropZone, // restrict dropZone to content div
autoUpload: false,
sequentialUploads: true,
maxRetries: options.uploadStallRetries,
maxRetries: options.uploadStallRetries || 3,
retryTimeout: 500,
//singleFileUploads is on by default, so the data.files array will always have length 1
/**
* on first add of every selection
* - check all files of originalFiles array with files in dir
* - on conflict show dialog
* - skip all -> remember as single skip action for all conflicting files
* - replace all -> remember as single replace action for all conflicting files
Expand Down Expand Up @@ -1296,9 +1306,7 @@ OC.Uploader.prototype = _.extend({
'/' + encodeURIComponent(chunkId);
delete data.contentRange;
delete data.headers['Content-Range'];
});
fileupload.on('fileuploadchunksend', function(e, data) {
var upload = self.getUpload(data);

// reset retries
upload.data.retries = 0;
});
Expand Down
144 changes: 143 additions & 1 deletion apps/files/tests/js/fileUploadSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ describe('OC.Upload tests', function() {
var testFile;
var uploader;
var failStub;
var currentUserStub;
var getFolderContentsStub;

beforeEach(function() {
testFile = {
Expand All @@ -37,6 +39,7 @@ describe('OC.Upload tests', function() {
'<input type="file" id="file_upload_start" name="files[]" multiple="multiple">' +
'<input type="hidden" id="free_space" name="free_space" value="50000000">' + // 50 MB
// TODO: handlebars!
'<div id="uploadprogressbar"></div>' +
'<div id="new">' +
'<a>New</a>' +
'<ul>' +
Expand All @@ -48,10 +51,17 @@ describe('OC.Upload tests', function() {
uploader = new OC.Uploader($dummyUploader);
failStub = sinon.stub();
uploader.on('fail', failStub);

currentUserStub = sinon.stub(OC, 'getCurrentUser').returns({uid: 'current@user'});
getFolderContentsStub = sinon.stub(OC.Files.Client.prototype, 'getFolderContents');
});
afterEach(function() {
$dummyUploader = undefined;
failStub = undefined;
currentUserStub.restore();
getFolderContentsStub.restore();
uploader.clear();
uploader = null;
});

/**
Expand All @@ -67,7 +77,8 @@ describe('OC.Upload tests', function() {
files: [file],
jqXHR: jqXHR,
response: sinon.stub.returns(jqXHR),
submit: sinon.stub()
submit: sinon.stub(),
abort: sinon.stub()
};
if (uploader.fileUploadParam.add.call(
$dummyUploader[0],
Expand Down Expand Up @@ -273,4 +284,135 @@ describe('OC.Upload tests', function() {
expect(upload.data.headers['OC-Autorename']).toEqual('1');
});
});
describe('Chunked upload', function() {
it('rewires data url when uploading chunks', function() {
var result = addFiles(uploader, [testFile]);
var upload = uploader.getUpload(result[0]);
expect(result[0]).not.toEqual(null);
expect(result[0].submit.calledOnce).toEqual(true);

result[0].contentRange = 'bytes 100-250/150';
result[0].headers['Content-Range'] = 'bytes 100-250/150';
result[0].retries = 3;

$dummyUploader.trigger('fileuploadchunksend', result[0]);

expect(result[0].contentRange).not.toBeDefined();
expect(result[0].headers['Content-Range']).not.toBeDefined();
expect(result[0].url)
.toEqual(OC.getRootPath() + '/remote.php/dav/uploads/current%40user/' + upload.getId() + '/100');
expect(result[0].retries).toEqual(0);
});

it('retries stalled chunk on failure', function() {
var clock = sinon.useFakeTimers();

uploader = new OC.Uploader($dummyUploader, {
maxChunkSize: 150
});
uploader.on('fail', failStub);

var result = addFiles(uploader, [testFile]);
var upload = uploader.getUpload(result[0]);

// chunk upload doesn't call "submit" initially
expect(result[0].submit.notCalled).toEqual(true);

upload.data.stalled = true;

result[0].headers['If-None-Match'] = '*';

result[0].uploadedBytes = 300; // less than expected

uploader.fileUploadParam.fail.call($dummyUploader[0], {}, result[0]);

expect(upload.data.retries).toEqual(1);
expect(failStub.notCalled).toEqual(true);

expect(getFolderContentsStub.notCalled).toEqual(true);

var deferred = $.Deferred();
getFolderContentsStub.returns(deferred.promise());

// trigger retry
clock.tick(10000);

expect(getFolderContentsStub.calledOnce).toEqual(true);
expect(getFolderContentsStub.getCall(0).args[0]).toEqual('uploads/current@user/' + upload.getId());

deferred.resolve(207, [
{
name: '0',
size: 150
},
{
name: '150',
size: 150
},
// this chunk is smaller and needs to be retried
{
name: '300',
size: 100
},
// ignored
{
name: '.file',
size: 10000
}
]);

// uploaded bytes was set to the sum of all chunk sizes
expect(result[0].uploadedBytes).toEqual(300);
expect(result[0].data).toBeFalsy();

// header was cleared for overwriting
expect(result[0].headers['If-None-Match']).not.toBeDefined();

expect(result[0].submit.calledOnce).toEqual(true);

clock.restore();
});

it('stalled progress will set stalled flag after a while', function() {
var clock = sinon.useFakeTimers();

uploader = new OC.Uploader($dummyUploader, {
uploadStallTimeout: 10
});

var result = addFiles(uploader, [testFile]);
var upload = uploader.getUpload(result[0]);

$dummyUploader.trigger('fileuploadstart', result[0]);

result[0].uploadedBytes = 300;

expect(upload.data.stalled).toBeFalsy();

$dummyUploader.trigger('fileuploadprogressall', {loaded: 300, total: 5000});
clock.tick(5000);

expect(upload.data.stalled).toBeFalsy();

result[0].uploadedBytes = 400; // some progress, no stall

$dummyUploader.trigger('fileuploadprogressall', {loaded: 400, total: 5000});
clock.tick(10000);

expect(upload.data.stalled).toBeFalsy();

expect(result[0].abort.notCalled).toEqual(true);

// no progress, stalled
$dummyUploader.trigger('fileuploadprogressall', {loaded: 400, total: 5000});

clock.tick(10000);

expect(upload.data.stalled).toEqual(true);

expect(result[0].abort.calledOnce).toEqual(true);

clock.restore();
});
});
});

0 comments on commit 746adf7

Please sign in to comment.