From cd15b501729b9bc4495edd28a7665389c2c69406 Mon Sep 17 00:00:00 2001 From: Tim Brust Date: Tue, 1 Sep 2020 15:08:24 +0000 Subject: [PATCH] refactor(eslint): use cordova-eslint /w fix (#275) --- .eslintrc.yml | 23 + .jscsrc | 23 - .jshintrc | 19 - package.json | 8 +- src/windows/FileTransferProxy.js | 550 +++---- tests/hooks/after_prepare.js | 69 +- tests/tests.js | 2331 +++++++++++++++++------------- www/FileTransfer.js | 127 +- www/FileTransferError.js | 4 +- www/browser/FileTransfer.js | 307 ++-- 10 files changed, 1884 insertions(+), 1577 deletions(-) create mode 100644 .eslintrc.yml delete mode 100644 .jscsrc delete mode 100644 .jshintrc diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 00000000..17277f7f --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +root: true +extends: '@cordova/eslint-config/browser' + +overrides: + - files: [tests/**/*.js] + extends: '@cordova/eslint-config/node-tests' diff --git a/.jscsrc b/.jscsrc deleted file mode 100644 index 2656b940..00000000 --- a/.jscsrc +++ /dev/null @@ -1,23 +0,0 @@ -{ - "disallowMixedSpacesAndTabs": true, - "disallowTrailingWhitespace": true, - "validateIndentation": 4, - "requireLineFeedAtFileEnd": true, - - "disallowSpaceAfterPrefixUnaryOperators": true, - "disallowSpaceBeforePostfixUnaryOperators": true, - "requireSpaceAfterLineComment": true, - "requireCapitalizedConstructors": true, - - "disallowSpacesInNamedFunctionExpression": { - "beforeOpeningRoundBrace": true - }, - - "requireSpaceAfterKeywords": [ - "if", - "else", - "for", - "while", - "do" - ] -} diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 93c3c13f..00000000 --- a/.jshintrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "browser": true - , "devel": true - , "bitwise": true - , "undef": true - , "trailing": true - , "quotmark": false - , "indent": 4 - , "unused": "vars" - , "latedef": "nofunc" - , "globals": { - "module": false, - "exports": false, - "require": false, - "FileTransferError": true, - "FileUploadResult": true, - "resolveLocalFileSystemURI": false - } -} diff --git a/package.json b/package.json index 3766722b..871efefb 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,8 @@ ] }, "scripts": { - "test": "npm run lint && npm run style", - "lint": "jshint www && jshint src && jshint tests", - "style": "jscs tests/tests.js" + "test": "npm run lint", + "lint": "eslint ." }, "repository": "github:apache/cordova-plugin-file-transfer", "bugs": "https://github.com/apache/cordova-plugin-file-transfer/issues", @@ -39,7 +38,6 @@ } }, "devDependencies": { - "jscs": "^2.6.0", - "jshint": "^2.8.0" + "@cordova/eslint-config": "^3.0.0" } } diff --git a/src/windows/FileTransferProxy.js b/src/windows/FileTransferProxy.js index 01e6e7b8..316779ff 100644 --- a/src/windows/FileTransferProxy.js +++ b/src/windows/FileTransferProxy.js @@ -17,28 +17,25 @@ * specific language governing permissions and limitations * under the License. * -*/ + */ -/*jshint -W030 */ -/*global Windows, WinJS*/ -/*global module, require*/ +/* global Windows, WinJS */ -var FTErr = require('./FileTransferError'), - ProgressEvent = require('cordova-plugin-file.ProgressEvent'), - FileUploadResult = require('cordova-plugin-file.FileUploadResult'), - FileProxy = require('cordova-plugin-file.FileProxy'); +var FTErr = require('./FileTransferError'); +var ProgressEvent = require('cordova-plugin-file.ProgressEvent'); +var FileUploadResult = require('cordova-plugin-file.FileUploadResult'); +var FileProxy = require('cordova-plugin-file.FileProxy'); var appData = Windows.Storage.ApplicationData.current; -var LINE_START = "--"; -var LINE_END = "\r\n"; +var LINE_START = '--'; +var LINE_END = '\r\n'; var BOUNDARY = '+++++'; var fileTransferOps = []; // Some private helper functions, hidden by the module -function cordovaPathToNative(path) { - +function cordovaPathToNative (path) { var cleanPath = String(path); // turn / into \\ cleanPath = cleanPath.replace(/\//g, '\\'); @@ -49,11 +46,11 @@ function cordovaPathToNative(path) { return cleanPath; } -function nativePathToCordova(path) { +function nativePathToCordova (path) { return String(path).replace(/\\/g, '/'); } -function alreadyCancelled(opId) { +function alreadyCancelled (opId) { var op = fileTransferOps[opId]; return op && op.state === FileTransferOperation.CANCELLED; } @@ -145,7 +142,7 @@ function doUpload (upload, uploadId, filePath, server, successCallback, errorCal ); } -function FileTransferOperation(state, promise) { +function FileTransferOperation (state, promise) { this.state = state; this.promise = promise; } @@ -157,9 +154,8 @@ FileTransferOperation.CANCELLED = 2; var HTTP_E_STATUS_NOT_MODIFIED = -2145844944; module.exports = { - -/* -exec(win, fail, 'FileTransfer', 'upload', + /* +exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]); */ upload: function (successCallback, errorCallback, options) { @@ -170,14 +166,14 @@ exec(win, fail, 'FileTransfer', 'upload', var mimeType = options[4]; var params = options[5]; // var trustAllHosts = options[6]; // todo - // var chunkedMode = options[7]; // todo + // var chunkedMode = options[7]; // todo var headers = options[8] || {}; var uploadId = options[9]; var httpMethod = options[10]; - var isMultipart = typeof headers["Content-Type"] === 'undefined'; + var isMultipart = typeof headers['Content-Type'] === 'undefined'; - function stringToByteArray(str) { + function stringToByteArray (str) { var byteCharacters = atob(str); var byteNumbers = new Array(byteCharacters.length); for (var i = 0; i < byteCharacters.length; i++) { @@ -186,21 +182,21 @@ exec(win, fail, 'FileTransfer', 'upload', return new Uint8Array(byteNumbers); } - if (!filePath || (typeof filePath !== 'string')) { + if (!filePath || typeof filePath !== 'string') { errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, null, server)); return; } - if (filePath.indexOf("data:") === 0 && filePath.indexOf("base64") !== -1) { - // First a DataWriter object is created, backed by an in-memory stream where + if (filePath.indexOf('data:') === 0 && filePath.indexOf('base64') !== -1) { + // First a DataWriter object is created, backed by an in-memory stream where // the data will be stored. var writer = Windows.Storage.Streams.DataWriter(new Windows.Storage.Streams.InMemoryRandomAccessStream()); writer.unicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.utf8; writer.byteOrder = Windows.Storage.Streams.ByteOrder.littleEndian; - var commaIndex = filePath.indexOf(","); + var commaIndex = filePath.indexOf(','); if (commaIndex === -1) { - errorCallback(new FTErr(FTErr.INVALID_URL_ERR, fileName, server, null, null, "No comma in data: URI")); + errorCallback(new FTErr(FTErr.INVALID_URL_ERR, fileName, server, null, null, 'No comma in data: URI')); return; } @@ -213,7 +209,7 @@ exec(win, fail, 'FileTransfer', 'upload', var uploader = new Windows.Networking.BackgroundTransfer.BackgroundUploader(); uploader.method = httpMethod; for (var header in headers) { - if (headers.hasOwnProperty(header)) { + if (Object.prototype.hasOwnProperty.call(headers, header)) { uploader.setRequestHeader(header, headers[header]); } } @@ -222,9 +218,9 @@ exec(win, fail, 'FileTransfer', 'upload', // adding params supplied to request payload var multipartParams = ''; for (var key in params) { - if (params.hasOwnProperty(key)) { + if (Object.prototype.hasOwnProperty.call(params, key)) { multipartParams += LINE_START + BOUNDARY + LINE_END; - multipartParams += "Content-Disposition: form-data; name=\"" + key + "\""; + multipartParams += 'Content-Disposition: form-data; name="' + key + '"'; multipartParams += LINE_END + LINE_END; multipartParams += params[key]; multipartParams += LINE_END; @@ -232,13 +228,13 @@ exec(win, fail, 'FileTransfer', 'upload', } var multipartFile = LINE_START + BOUNDARY + LINE_END; - multipartFile += "Content-Disposition: form-data; name=\"file\";"; - multipartFile += " filename=\"" + fileName + "\"" + LINE_END; - multipartFile += "Content-Type: " + mimeType + LINE_END + LINE_END; + multipartFile += 'Content-Disposition: form-data; name="file";'; + multipartFile += ' filename="' + fileName + '"' + LINE_END; + multipartFile += 'Content-Type: ' + mimeType + LINE_END + LINE_END; var bound = LINE_END + LINE_START + BOUNDARY + LINE_START + LINE_END; - uploader.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); + uploader.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + BOUNDARY); writer.writeString(multipartParams); writer.writeString(multipartFile); writer.writeBytes(stringToByteArray(fileDataString)); @@ -249,70 +245,76 @@ exec(win, fail, 'FileTransfer', 'upload', var stream; - // The call to store async sends the actual contents of the writer + // The call to store async sends the actual contents of the writer // to the backing stream. - writer.storeAsync().then(function () { - // For the in-memory stream implementation we are using, the flushAsync call - // is superfluous, but other types of streams may require it. - return writer.flushAsync(); - }).then(function () { - // We detach the stream to prolong its useful lifetime. Were we to fail - // to detach the stream, the call to writer.close() would close the underlying - // stream, preventing its subsequent use by the DataReader below. Most clients - // of DataWriter will have no reason to use the underlying stream after - // writer.close() is called, and will therefore have no reason to call - // writer.detachStream(). Note that once we detach the stream, we assume - // responsibility for closing the stream subsequently; after the stream - // has been detached, a call to writer.close() will have no effect on the stream. - stream = writer.detachStream(); - // Make sure the stream is read from the beginning in the reader - // we are creating below. - stream.seek(0); - // Most DataWriter clients will not call writer.detachStream(), - // and furthermore will be working with a file-backed or network-backed stream, - // rather than an in-memory-stream. In such cases, it would be particularly - // important to call writer.close(). Doing so is always a best practice. - writer.close(); - - if (alreadyCancelled(uploadId)) { - errorCallback(new FTErr(FTErr.ABORT_ERR, nativePathToCordova(filePath), server)); - return; - } + writer + .storeAsync() + .then(function () { + // For the in-memory stream implementation we are using, the flushAsync call + // is superfluous, but other types of streams may require it. + return writer.flushAsync(); + }) + .then(function () { + // We detach the stream to prolong its useful lifetime. Were we to fail + // to detach the stream, the call to writer.close() would close the underlying + // stream, preventing its subsequent use by the DataReader below. Most clients + // of DataWriter will have no reason to use the underlying stream after + // writer.close() is called, and will therefore have no reason to call + // writer.detachStream(). Note that once we detach the stream, we assume + // responsibility for closing the stream subsequently; after the stream + // has been detached, a call to writer.close() will have no effect on the stream. + stream = writer.detachStream(); + // Make sure the stream is read from the beginning in the reader + // we are creating below. + stream.seek(0); + // Most DataWriter clients will not call writer.detachStream(), + // and furthermore will be working with a file-backed or network-backed stream, + // rather than an in-memory-stream. In such cases, it would be particularly + // important to call writer.close(). Doing so is always a best practice. + writer.close(); + + if (alreadyCancelled(uploadId)) { + errorCallback(new FTErr(FTErr.ABORT_ERR, nativePathToCordova(filePath), server)); + return; + } - // create download object. This will throw an exception if URL is malformed - var uri = new Windows.Foundation.Uri(server); + // create download object. This will throw an exception if URL is malformed + var uri = new Windows.Foundation.Uri(server); - var createUploadOperation; - try { - createUploadOperation = uploader.createUploadFromStreamAsync(uri, stream); - } catch (e) { - errorCallback(new FTErr(FTErr.INVALID_URL_ERR)); - return; - } + var createUploadOperation; + try { + createUploadOperation = uploader.createUploadFromStreamAsync(uri, stream); + } catch (e) { + errorCallback(new FTErr(FTErr.INVALID_URL_ERR)); + return; + } - createUploadOperation.then( - function (upload) { - doUpload(upload, uploadId, filePath, server, successCallback, errorCallback); - }, - function (err) { - var errorObj = new FTErr(FTErr.INVALID_URL_ERR); - errorObj.exception = err; - errorCallback(errorObj); - }); - }); + createUploadOperation.then( + function (upload) { + doUpload(upload, uploadId, filePath, server, successCallback, errorCallback); + }, + function (err) { + var errorObj = new FTErr(FTErr.INVALID_URL_ERR); + errorObj.exception = err; + errorCallback(errorObj); + } + ); + }); return; } - if (filePath.substr(0, 8) === "file:///") { - filePath = appData.localFolder.path + filePath.substr(8).split("/").join("\\"); + if (filePath.substr(0, 8) === 'file:///') { + filePath = appData.localFolder.path + filePath.substr(8).split('/').join('\\'); } else if (filePath.indexOf('ms-appdata:///') === 0) { // Handle 'ms-appdata' scheme - filePath = filePath.replace('ms-appdata:///local', appData.localFolder.path) - .replace('ms-appdata:///temp', appData.temporaryFolder.path); + filePath = filePath + .replace('ms-appdata:///local', appData.localFolder.path) + .replace('ms-appdata:///temp', appData.temporaryFolder.path); } else if (filePath.indexOf('cdvfile://') === 0) { - filePath = filePath.replace('cdvfile://localhost/persistent', appData.localFolder.path) - .replace('cdvfile://localhost/temporary', appData.temporaryFolder.path); + filePath = filePath + .replace('cdvfile://localhost/persistent', appData.localFolder.path) + .replace('cdvfile://localhost/temporary', appData.temporaryFolder.path); } // normalize path separators @@ -321,82 +323,88 @@ exec(win, fail, 'FileTransfer', 'upload', // Create internal download operation object fileTransferOps[uploadId] = new FileTransferOperation(FileTransferOperation.PENDING, null); - Windows.Storage.StorageFile.getFileFromPathAsync(filePath) - .then(function (storageFile) { - - if (!fileName) { - fileName = storageFile.name; - } - if (!mimeType) { - // use the actual content type of the file, probably this should be the default way. - // other platforms probably can't look this up. - mimeType = storageFile.contentType; - } + Windows.Storage.StorageFile.getFileFromPathAsync(filePath).then( + function (storageFile) { + if (!fileName) { + fileName = storageFile.name; + } + if (!mimeType) { + // use the actual content type of the file, probably this should be the default way. + // other platforms probably can't look this up. + mimeType = storageFile.contentType; + } - if (alreadyCancelled(uploadId)) { - errorCallback(new FTErr(FTErr.ABORT_ERR, nativePathToCordova(filePath), server)); - return; - } + if (alreadyCancelled(uploadId)) { + errorCallback(new FTErr(FTErr.ABORT_ERR, nativePathToCordova(filePath), server)); + return; + } - // setting request headers for uploader - var uploader = new Windows.Networking.BackgroundTransfer.BackgroundUploader(); - uploader.method = httpMethod; - for (var header in headers) { - if (headers.hasOwnProperty(header)) { - uploader.setRequestHeader(header, headers[header]); + // setting request headers for uploader + var uploader = new Windows.Networking.BackgroundTransfer.BackgroundUploader(); + uploader.method = httpMethod; + for (var header in headers) { + if (Object.prototype.hasOwnProperty.call(headers, header)) { + uploader.setRequestHeader(header, headers[header]); + } } - } - // create download object. This will throw an exception if URL is malformed - var uri = new Windows.Foundation.Uri(server); - - var createUploadOperation; - try { - if (isMultipart) { - // adding params supplied to request payload - var transferParts = []; - for (var key in params) { - // Create content part for params only if value is specified because CreateUploadAsync fails otherwise - if (params.hasOwnProperty(key) && params[key] !== null && params[key] !== undefined && params[key].toString() !== "") { - var contentPart = new Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart(); - contentPart.setHeader("Content-Disposition", "form-data; name=\"" + key + "\""); - contentPart.setText(params[key]); - transferParts.push(contentPart); + // create download object. This will throw an exception if URL is malformed + var uri = new Windows.Foundation.Uri(server); + + var createUploadOperation; + try { + if (isMultipart) { + // adding params supplied to request payload + var transferParts = []; + for (var key in params) { + // Create content part for params only if value is specified because CreateUploadAsync fails otherwise + if ( + Object.prototype.hasOwnProperty.call(params, key) && + params[key] !== null && + params[key] !== undefined && + params[key].toString() !== '' + ) { + var contentPart = new Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart(); + contentPart.setHeader('Content-Disposition', 'form-data; name="' + key + '"'); + contentPart.setText(params[key]); + transferParts.push(contentPart); + } } - } - // Adding file to upload to request payload - var fileToUploadPart = new Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart(fileKey, fileName); - fileToUploadPart.setHeader("Content-Type", mimeType); - fileToUploadPart.setFile(storageFile); - transferParts.push(fileToUploadPart); + // Adding file to upload to request payload + var fileToUploadPart = new Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart(fileKey, fileName); + fileToUploadPart.setHeader('Content-Type', mimeType); + fileToUploadPart.setFile(storageFile); + transferParts.push(fileToUploadPart); - createUploadOperation = uploader.createUploadAsync(uri, transferParts); - } else { - createUploadOperation = WinJS.Promise.wrap(uploader.createUpload(uri, storageFile)); + createUploadOperation = uploader.createUploadAsync(uri, transferParts); + } else { + createUploadOperation = WinJS.Promise.wrap(uploader.createUpload(uri, storageFile)); + } + } catch (e) { + errorCallback(new FTErr(FTErr.INVALID_URL_ERR)); + return; } - } catch (e) { - errorCallback(new FTErr(FTErr.INVALID_URL_ERR)); - return; - } - createUploadOperation.then( - function (upload) { - doUpload(upload, uploadId, filePath, server, successCallback, errorCallback); - }, - function (err) { - var errorObj = new FTErr(FTErr.INVALID_URL_ERR); - errorObj.exception = err; - errorCallback(errorObj); - } - ); - }, function (err) { - errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, fileName, server, null, null, err)); - }); + createUploadOperation.then( + function (upload) { + doUpload(upload, uploadId, filePath, server, successCallback, errorCallback); + }, + function (err) { + var errorObj = new FTErr(FTErr.INVALID_URL_ERR); + errorObj.exception = err; + errorCallback(errorObj); + } + ); + }, + function (err) { + errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, fileName, server, null, null, err)); + } + ); }, // [source, target, trustAllHosts, id, headers] - download:function(successCallback, errorCallback, options) { + download: function (successCallback, errorCallback, options) { var source = options[0]; var target = options[1]; var downloadId = options[3]; @@ -406,25 +414,27 @@ exec(win, fail, 'FileTransfer', 'upload', errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR)); return; } - if (target.substr(0, 8) === "file:///") { - target = appData.localFolder.path + target.substr(8).split("/").join("\\"); + if (target.substr(0, 8) === 'file:///') { + target = appData.localFolder.path + target.substr(8).split('/').join('\\'); } else if (target.indexOf('ms-appdata:///') === 0) { // Handle 'ms-appdata' scheme - target = target.replace('ms-appdata:///local', appData.localFolder.path) - .replace('ms-appdata:///temp', appData.temporaryFolder.path); + target = target + .replace('ms-appdata:///local', appData.localFolder.path) + .replace('ms-appdata:///temp', appData.temporaryFolder.path); } else if (target.indexOf('cdvfile://') === 0) { - target = target.replace('cdvfile://localhost/persistent', appData.localFolder.path) - .replace('cdvfile://localhost/temporary', appData.temporaryFolder.path); + target = target + .replace('cdvfile://localhost/persistent', appData.localFolder.path) + .replace('cdvfile://localhost/temporary', appData.temporaryFolder.path); } target = cordovaPathToNative(target); - var path = target.substr(0, target.lastIndexOf("\\")); - var fileName = target.substr(target.lastIndexOf("\\") + 1); + var path = target.substr(0, target.lastIndexOf('\\')); + var fileName = target.substr(target.lastIndexOf('\\') + 1); if (path === null || fileName === null) { errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR)); return; } - // Download to a temp file to avoid the file deletion on 304 + // Download to a temp file to avoid the file deletion on 304 // CB-7006 Empty file is created on file transfer if server response is 304 var tempFileName = '~' + fileName; @@ -433,129 +443,136 @@ exec(win, fail, 'FileTransfer', 'upload', // Create internal download operation object fileTransferOps[downloadId] = new FileTransferOperation(FileTransferOperation.PENDING, null); - var downloadCallback = function(storageFolder) { - storageFolder.createFileAsync(tempFileName, Windows.Storage.CreationCollisionOption.replaceExisting).then(function (storageFile) { - - if (alreadyCancelled(downloadId)) { - errorCallback(new FTErr(FTErr.ABORT_ERR, source, target)); - return; - } - - // if download isn't cancelled, contunue with creating and preparing download operation - var downloader = new Windows.Networking.BackgroundTransfer.BackgroundDownloader(); - for (var header in headers) { - if (headers.hasOwnProperty(header)) { - downloader.setRequestHeader(header, headers[header]); + var downloadCallback = function (storageFolder) { + storageFolder.createFileAsync(tempFileName, Windows.Storage.CreationCollisionOption.replaceExisting).then( + function (storageFile) { + if (alreadyCancelled(downloadId)) { + errorCallback(new FTErr(FTErr.ABORT_ERR, source, target)); + return; } - } - // create download object. This will throw an exception if URL is malformed - try { - var uri = Windows.Foundation.Uri(source); - download = downloader.createDownload(uri, storageFile); - } catch (e) { - // so we handle this and call errorCallback - errorCallback(new FTErr(FTErr.INVALID_URL_ERR)); - return; - } - - var downloadOperation = download.startAsync(); - // update internal TransferOperation object with newly created promise - fileTransferOps[downloadId].promise = downloadOperation; - - downloadOperation.then(function () { + // if download isn't cancelled, contunue with creating and preparing download operation + var downloader = new Windows.Networking.BackgroundTransfer.BackgroundDownloader(); + for (var header in headers) { + if (Object.prototype.hasOwnProperty.call(headers, header)) { + downloader.setRequestHeader(header, headers[header]); + } + } - // Update TransferOperation object with new state, delete promise property - // since it is not actual anymore - var currentDownloadOp = fileTransferOps[downloadId]; - if (currentDownloadOp) { - currentDownloadOp.state = FileTransferOperation.DONE; - currentDownloadOp.promise = null; + // create download object. This will throw an exception if URL is malformed + try { + var uri = Windows.Foundation.Uri(source); + download = downloader.createDownload(uri, storageFile); + } catch (e) { + // so we handle this and call errorCallback + errorCallback(new FTErr(FTErr.INVALID_URL_ERR)); + return; } - storageFile.renameAsync(fileName, Windows.Storage.CreationCollisionOption.replaceExisting).done(function () { - var nativeURI = storageFile.path.replace(appData.localFolder.path, 'ms-appdata:///local') - .replace(appData.temporaryFolder.path, 'ms-appdata:///temp') - .replace(/\\/g, '/'); - - // Passing null as error callback here because downloaded file should exist in any case - // otherwise the error callback will be hit during file creation in another place - FileProxy.resolveLocalFileSystemURI(successCallback, null, [nativeURI]); - }, function(error) { - errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error)); - }); - }, function(error) { - - var getTransferError = new WinJS.Promise(function (resolve) { - // Handle download error here. If download was cancelled, - // message property will be specified - if (error.message === 'Canceled') { - resolve(new FTErr(FTErr.ABORT_ERR, source, target, null, null, error)); - } else if (error && error.number === HTTP_E_STATUS_NOT_MODIFIED) { - resolve(new FTErr(FTErr.NOT_MODIFIED_ERR, source, target, 304, null, error)); - } else { - // in the other way, try to get response property - var response = download.getResponseInformation(); - if (!response) { - resolve(new FTErr(FTErr.CONNECTION_ERR, source, target)); - } else { - if (download.progress.bytesReceived === 0) { - resolve(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, response.statusCode, null, error)); - return; - } - var reader = new Windows.Storage.Streams.DataReader(download.getResultStreamAt(0)); - reader.loadAsync(download.progress.bytesReceived).then(function (bytesLoaded) { - var payload = reader.readString(bytesLoaded); - resolve(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, response.statusCode, payload, error)); - }); + var downloadOperation = download.startAsync(); + // update internal TransferOperation object with newly created promise + fileTransferOps[downloadId].promise = downloadOperation; + + downloadOperation.then( + function () { + // Update TransferOperation object with new state, delete promise property + // since it is not actual anymore + var currentDownloadOp = fileTransferOps[downloadId]; + if (currentDownloadOp) { + currentDownloadOp.state = FileTransferOperation.DONE; + currentDownloadOp.promise = null; } - } - }); - getTransferError.then(function (fileTransferError) { - - // Update TransferOperation object with new state, delete promise property - // since it is not actual anymore - var currentDownloadOp = fileTransferOps[downloadId]; - if (currentDownloadOp) { - currentDownloadOp.state = FileTransferOperation.CANCELLED; - currentDownloadOp.promise = null; - } - // Cleanup, remove incompleted file - storageFile.deleteAsync().then(function() { - errorCallback(fileTransferError); - }); - }); - - }, function(evt) { - - var progressEvent = new ProgressEvent('progress', { - loaded: evt.progress.bytesReceived, - total: evt.progress.totalBytesToReceive, - target: evt.resultFile - }); - // when bytesReceived == 0, BackgroundDownloader has not yet differentiated whether it could get file length or not, - // when totalBytesToReceive == 0, BackgroundDownloader is unable to get file length - progressEvent.lengthComputable = (evt.progress.bytesReceived > 0) && (evt.progress.totalBytesToReceive > 0); + storageFile.renameAsync(fileName, Windows.Storage.CreationCollisionOption.replaceExisting).done( + function () { + var nativeURI = storageFile.path + .replace(appData.localFolder.path, 'ms-appdata:///local') + .replace(appData.temporaryFolder.path, 'ms-appdata:///temp') + .replace(/\\/g, '/'); + + // Passing null as error callback here because downloaded file should exist in any case + // otherwise the error callback will be hit during file creation in another place + FileProxy.resolveLocalFileSystemURI(successCallback, null, [nativeURI]); + }, + function (error) { + errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error)); + } + ); + }, + function (error) { + var getTransferError = new WinJS.Promise(function (resolve) { + // Handle download error here. If download was cancelled, + // message property will be specified + if (error.message === 'Canceled') { + resolve(new FTErr(FTErr.ABORT_ERR, source, target, null, null, error)); + } else if (error && error.number === HTTP_E_STATUS_NOT_MODIFIED) { + resolve(new FTErr(FTErr.NOT_MODIFIED_ERR, source, target, 304, null, error)); + } else { + // in the other way, try to get response property + var response = download.getResponseInformation(); + if (!response) { + resolve(new FTErr(FTErr.CONNECTION_ERR, source, target)); + } else { + if (download.progress.bytesReceived === 0) { + resolve(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, response.statusCode, null, error)); + return; + } + var reader = new Windows.Storage.Streams.DataReader(download.getResultStreamAt(0)); + reader.loadAsync(download.progress.bytesReceived).then(function (bytesLoaded) { + var payload = reader.readString(bytesLoaded); + resolve( + new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, response.statusCode, payload, error) + ); + }); + } + } + }); + getTransferError.then(function (fileTransferError) { + // Update TransferOperation object with new state, delete promise property + // since it is not actual anymore + var currentDownloadOp = fileTransferOps[downloadId]; + if (currentDownloadOp) { + currentDownloadOp.state = FileTransferOperation.CANCELLED; + currentDownloadOp.promise = null; + } - successCallback(progressEvent, { keepCallback: true }); - }); - }, function(error) { - errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error)); - }); + // Cleanup, remove incompleted file + storageFile.deleteAsync().then(function () { + errorCallback(fileTransferError); + }); + }); + }, + function (evt) { + var progressEvent = new ProgressEvent('progress', { + loaded: evt.progress.bytesReceived, + total: evt.progress.totalBytesToReceive, + target: evt.resultFile + }); + // when bytesReceived == 0, BackgroundDownloader has not yet differentiated whether it could get file length or not, + // when totalBytesToReceive == 0, BackgroundDownloader is unable to get file length + progressEvent.lengthComputable = evt.progress.bytesReceived > 0 && evt.progress.totalBytesToReceive > 0; + + successCallback(progressEvent, { keepCallback: true }); + } + ); + }, + function (error) { + errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error)); + } + ); }; - var fileNotFoundErrorCallback = function(error) { + var fileNotFoundErrorCallback = function (error) { errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error)); }; Windows.Storage.StorageFolder.getFolderFromPathAsync(path).then(downloadCallback, function (error) { // Handle non-existent directory if (error.number === -2147024894) { - var parent = path.substr(0, path.lastIndexOf('\\')), - folderNameToCreate = path.substr(path.lastIndexOf('\\') + 1); + var parent = path.substr(0, path.lastIndexOf('\\')); + var folderNameToCreate = path.substr(path.lastIndexOf('\\') + 1); - Windows.Storage.StorageFolder.getFolderFromPathAsync(parent).then(function(parentFolder) { + Windows.Storage.StorageFolder.getFolderFromPathAsync(parent).then(function (parentFolder) { parentFolder.createFolderAsync(folderNameToCreate).then(downloadCallback, fileNotFoundErrorCallback); }, fileNotFoundErrorCallback); } else { @@ -577,7 +594,6 @@ exec(win, fail, 'FileTransfer', 'upload', fileTransferOps[fileTransferOpId] = new FileTransferOperation(FileTransferOperation.CANCELLED, null); } } - }; -require("cordova/exec/proxy").add("FileTransfer",module.exports); +require('cordova/exec/proxy').add('FileTransfer', module.exports); diff --git a/tests/hooks/after_prepare.js b/tests/hooks/after_prepare.js index 6101c627..7c5b34ca 100644 --- a/tests/hooks/after_prepare.js +++ b/tests/hooks/after_prepare.js @@ -1,46 +1,48 @@ #!/usr/bin/env node /* -* -* Licensed to the Apache Software Foundation (ASF) under one -* or more contributor license agreements. See the NOTICE file -* distributed with this work for additional information -* regarding copyright ownership. The ASF licenses this file -* to you under the Apache License, Version 2.0 (the -* "License"); you may not use this file except in compliance -* with the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -* -*/ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ var path = require('path'); var fs = require('fs'); -module.exports = function(context) { - function main() { +module.exports = function (context) { + function main () { // get the file transfer server address from the specified variables var fileTransferServerAddress = getFileTransferServerAddress(context) || getDefaultFileTransferServerAddress(context); console.log('Tests will use the following file transfer server address: ' + fileTransferServerAddress); - console.log('If you\'re using cordova@6.3.1 and the above address is wrong at "platform add", don\'t worry, it\'ll fix itself on "cordova run" or "cordova prepare".'); + console.log( + 'If you\'re using cordova@6.3.1 and the above address is wrong at "platform add", don\'t worry, it\'ll fix itself on "cordova run" or "cordova prepare".' + ); // pass it to the tests writeFileTransferOptions(fileTransferServerAddress, context); } - function getDefaultFileTransferServerAddress(context) { + function getDefaultFileTransferServerAddress (context) { var address = null; var configNodes = context.opts.plugin.pluginInfo._et._root._children; for (var node in configNodes) { - if (configNodes[node].attrib.name == 'FILETRANSFER_SERVER_ADDRESS') { + if (configNodes[node].attrib.name === 'FILETRANSFER_SERVER_ADDRESS') { address = configNodes[node].attrib.default; } } @@ -48,18 +50,28 @@ module.exports = function(context) { return address; } - function getFileTransferServerAddress(context) { - var platformJsonFile = path.join(context.opts.projectRoot, 'platforms', context.opts.platforms[0], context.opts.platforms[0] + '.json'); + function getFileTransferServerAddress (context) { + var platformJsonFile = path.join( + context.opts.projectRoot, + 'platforms', + context.opts.platforms[0], + context.opts.platforms[0] + '.json' + ); var platformJson = JSON.parse(fs.readFileSync(platformJsonFile, 'utf8')); - if (platformJson && platformJson.installed_plugins && platformJson.installed_plugins['cordova-plugin-file-transfer-tests'] && platformJson.installed_plugins['cordova-plugin-file-transfer-tests'].FILETRANSFER_SERVER_ADDRESS) { + if ( + platformJson && + platformJson.installed_plugins && + platformJson.installed_plugins['cordova-plugin-file-transfer-tests'] && + platformJson.installed_plugins['cordova-plugin-file-transfer-tests'].FILETRANSFER_SERVER_ADDRESS + ) { return platformJson.installed_plugins['cordova-plugin-file-transfer-tests'].FILETRANSFER_SERVER_ADDRESS; } else { return null; } } - function writeFileTransferOptions(address, context) { + function writeFileTransferOptions (address, context) { for (var p in context.opts.paths) { var ftOpts = { serverAddress: address @@ -71,5 +83,4 @@ module.exports = function(context) { } main(); - }; diff --git a/tests/tests.js b/tests/tests.js index 63c9c1c2..50b128ad 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -1,44 +1,42 @@ /* -* -* Licensed to the Apache Software Foundation (ASF) under one -* or more contributor license agreements. See the NOTICE file -* distributed with this work for additional information -* regarding copyright ownership. The ASF licenses this file -* to you under the Apache License, Version 2.0 (the -* "License"); you may not use this file except in compliance -* with the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -* -*/ - -/* global exports, cordova, FileTransfer, FileTransferError, FileUploadOptions, LocalFileSystem, WinJS */ - -/* jshint jasmine: true */ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/* global cordova, FileTransfer, FileTransferError, FileUploadOptions, WinJS, LocalFileSystem */ exports.defineAutoTests = function () { - - "use strict"; + 'use strict'; // constants var ONE_SECOND = 1000; // in milliseconds var GRACE_TIME_DELTA = 600; // in milliseconds var DEFAULT_FILESYSTEM_SIZE = 1024 * 50; // filesystem size in bytes - var UNKNOWN_HOST = "http://foobar.apache.org"; + var UNKNOWN_HOST = 'http://foobar.apache.org'; var DOWNLOAD_TIMEOUT = 15 * ONE_SECOND; var LONG_TIMEOUT = 60 * ONE_SECOND; var UPLOAD_TIMEOUT = 15 * ONE_SECOND; var ABORT_DELAY = 100; // for abort() tests var LATIN1_SYMBOLS = '¥§©ÆÖÑøøø¼'; - var DATA_URI_PREFIX = "data:image/png;base64,"; - var DATA_URI_CONTENT = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; + var DATA_URI_PREFIX = 'data:image/png;base64,'; + var DATA_URI_CONTENT = + 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; var DATA_URI_CONTENT_LENGTH = 85; // bytes. (This is the raw file size: used https://en.wikipedia.org/wiki/File:Red-dot-5px.png from https://en.wikipedia.org/wiki/Data_URI_scheme) var RETRY_COUNT = 100; // retry some flaky tests (yes, THIS many times, due to Heroku server instability) var RETRY_INTERVAL = 100; @@ -49,31 +47,29 @@ exports.defineAutoTests = function () { // Will get it from the config // you can specify it as a 'FILETRANSFER_SERVER_ADDRESS' variable upon test plugin installation // or change the default value in plugin.xml - var SERVER = ""; - var SERVER_WITH_CREDENTIALS = ""; + var SERVER = ''; + var SERVER_WITH_CREDENTIALS = ''; // flags - var isWindows = cordova.platformId === "windows"; - var isBrowser = cordova.platformId === "browser"; + var isWindows = cordova.platformId === 'windows'; + var isBrowser = cordova.platformId === 'browser'; var isWindowsPhone = isWindows && WinJS.Utilities.isPhone; - var isIE = isBrowser && navigator.userAgent.indexOf("Trident") >= 0; - var isIos = cordova.platformId === "ios"; - var isIot = cordova.platformId === "android" && navigator.userAgent.indexOf("iot") >= 0; + var isIE = isBrowser && navigator.userAgent.indexOf('Trident') >= 0; + var isIos = cordova.platformId === 'ios'; + var isIot = cordova.platformId === 'android' && navigator.userAgent.indexOf('iot') >= 0; // tests - describe("FileTransferError", function () { - - it("should exist", function () { + describe('FileTransferError', function () { + it('should exist', function () { expect(FileTransferError).toBeDefined(); }); - it("should be constructable", function () { + it('should be constructable', function () { var transferError = new FileTransferError(); expect(transferError).toBeDefined(); }); - it("filetransfer.spec.3 should expose proper constants", function () { - + it('filetransfer.spec.3 should expose proper constants', function () { expect(FileTransferError.FILE_NOT_FOUND_ERR).toBeDefined(); expect(FileTransferError.INVALID_URL_ERR).toBeDefined(); expect(FileTransferError.CONNECTION_ERR).toBeDefined(); @@ -88,41 +84,42 @@ exports.defineAutoTests = function () { }); }); - describe("FileUploadOptions", function () { - - it("should exist", function () { + describe('FileUploadOptions', function () { + it('should exist', function () { expect(FileUploadOptions).toBeDefined(); }); - it("should be constructable", function () { + it('should be constructable', function () { var transferOptions = new FileUploadOptions(); expect(transferOptions).toBeDefined(); }); }); - describe("FileTransfer", function () { + describe('FileTransfer', function () { this.persistentRoot = null; - this.tempRoot = null; + this.tempRoot = null; // named callbacks var unexpectedCallbacks = { - httpFail: function () {}, - httpWin: function () {}, - fileSystemFail: function () {}, - fileSystemWin: function () {}, + httpFail: function () {}, + httpWin: function () {}, + fileSystemFail: function () {}, + fileSystemWin: function () {}, fileOperationFail: function () {}, - fileOperationWin: function () {}, + fileOperationWin: function () {} }; var expectedCallbacks = { unsupportedOperation: function (response) { - console.log("spec called unsupported functionality; response:", response); - }, + console.log('spec called unsupported functionality; response:', response); + } }; // helpers var deleteFile = function (fileSystem, name, done) { - fileSystem.getFile(name, null, + fileSystem.getFile( + name, + null, function (fileEntry) { fileEntry.remove( function () { @@ -140,15 +137,16 @@ exports.defineAutoTests = function () { }; var writeFile = function (fileSystem, name, content, success, done) { - var fileOperationFail = function() { + var fileOperationFail = function () { unexpectedCallbacks.fileOperationFail(); done(); }; - fileSystem.getFile(name, { create: true }, + fileSystem.getFile( + name, + { create: true }, function (fileEntry) { fileEntry.createWriter(function (writer) { - writer.onwrite = function () { success(fileEntry); }; @@ -161,13 +159,12 @@ exports.defineAutoTests = function () { throw new Error("aborted creating test file '" + name + "': " + evt); }; - if (cordova.platformId === "browser") { - var blob = new Blob([content + "\n"], { type: "text/plain" }); + if (cordova.platformId === 'browser') { + var blob = new Blob([content + '\n'], { type: 'text/plain' }); writer.write(blob); } else { - writer.write(content + "\n"); + writer.write(content + '\n'); } - }, fileOperationFail); }, function () { @@ -181,7 +178,7 @@ exports.defineAutoTests = function () { expect(event.loaded).toBeGreaterThan(1); expect(event.total).toBeGreaterThan(0); expect(event.total).not.toBeLessThan(event.loaded); - expect(event.lengthComputable).toBe(true, "lengthComputable"); + expect(event.lengthComputable).toBe(true, 'lengthComputable'); } else { // In IE, when lengthComputable === false, event.total somehow is equal to 2^64 if (isIE) { @@ -194,12 +191,12 @@ exports.defineAutoTests = function () { }; var getMalformedUrl = function () { - if (cordova.platformId === "android") { + if (cordova.platformId === 'android') { // bad protocol causes a MalformedUrlException on Android - return "httpssss://example.com"; + return 'httpssss://example.com'; } else { // iOS doesn't care about protocol, space in hostname causes error - return "httpssss://exa mple.com"; + return 'httpssss://exa mple.com'; } }; @@ -215,13 +212,15 @@ exports.defineAutoTests = function () { beforeEach(function (done) { var specContext = this; - window.requestFileSystem(LocalFileSystem.PERSISTENT, DEFAULT_FILESYSTEM_SIZE, + window.requestFileSystem( + LocalFileSystem.PERSISTENT, + DEFAULT_FILESYSTEM_SIZE, function (fileSystem) { specContext.persistentRoot = fileSystem.root; done(); }, function () { - throw new Error("Failed to initialize persistent file system."); + throw new Error('Failed to initialize persistent file system.'); } ); }); @@ -229,30 +228,31 @@ exports.defineAutoTests = function () { beforeEach(function (done) { var specContext = this; - window.requestFileSystem(LocalFileSystem.TEMPORARY, DEFAULT_FILESYSTEM_SIZE, + window.requestFileSystem( + LocalFileSystem.TEMPORARY, + DEFAULT_FILESYSTEM_SIZE, function (fileSystem) { specContext.tempRoot = fileSystem.root; done(); }, function () { - throw new Error("Failed to initialize temporary file system."); + throw new Error('Failed to initialize temporary file system.'); } ); }); // spy on all named callbacks - beforeEach(function() { - + beforeEach(function () { // ignore the actual implementations of the unexpected callbacks for (var callback in unexpectedCallbacks) { - if (unexpectedCallbacks.hasOwnProperty(callback)) { + if (Object.prototype.hasOwnProperty.call(unexpectedCallbacks, callback)) { spyOn(unexpectedCallbacks, callback); } } // but run the implementations of the expected callbacks - for (callback in expectedCallbacks) { // jshint ignore: line - if (expectedCallbacks.hasOwnProperty(callback)) { + for (callback in expectedCallbacks) { + if (Object.prototype.hasOwnProperty.call(expectedCallbacks, callback)) { spyOn(expectedCallbacks, callback).and.callThrough(); } } @@ -260,9 +260,9 @@ exports.defineAutoTests = function () { // at the end, check that none of the unexpected callbacks got called, // and act on the expected callbacks - afterEach(function() { + afterEach(function () { for (var callback in unexpectedCallbacks) { - if (unexpectedCallbacks.hasOwnProperty(callback)) { + if (Object.prototype.hasOwnProperty.call(unexpectedCallbacks, callback)) { expect(unexpectedCallbacks[callback]).not.toHaveBeenCalled(); } } @@ -272,11 +272,11 @@ exports.defineAutoTests = function () { } }); - it ("util spec: get file transfer server url", function () { + it('util spec: get file transfer server url', function () { try { // attempt to synchronously load medic config var xhr = new XMLHttpRequest(); - xhr.open("GET", "../fileTransferOpts.json", false); + xhr.open('GET', '../fileTransferOpts.json', false); xhr.send(null); var parsedCfg = JSON.parse(xhr.responseText); if (parsedCfg.serverAddress) { @@ -284,27 +284,28 @@ exports.defineAutoTests = function () { } } catch (ex) { console.error('Unable to load file transfer server url: ' + ex); - console.error('Note: if you are testing this on cordova-ios with cordova-plugin-wkwebview-engine, that may be why you are getting this error. See https://issues.apache.org/jira/browse/CB-10144.'); + console.error( + 'Note: if you are testing this on cordova-ios with cordova-plugin-wkwebview-engine, that may be why you are getting this error. See https://issues.apache.org/jira/browse/CB-10144.' + ); fail(ex); } }); - it("should initialise correctly", function() { + it('should initialise correctly', function () { expect(this.persistentRoot).toBeDefined(); expect(this.tempRoot).toBeDefined(); }); - it("should exist", function () { + it('should exist', function () { expect(FileTransfer).toBeDefined(); }); - it("filetransfer.spec.1 should be constructable", function () { + it('filetransfer.spec.1 should be constructable', function () { var transfer = new FileTransfer(); expect(transfer).toBeDefined(); }); - it("filetransfer.spec.2 should expose proper functions", function () { - + it('filetransfer.spec.2 should expose proper functions', function () { var transfer = new FileTransfer(); expect(transfer.upload).toBeDefined(); @@ -314,24 +315,23 @@ exports.defineAutoTests = function () { expect(transfer.download).toEqual(jasmine.any(Function)); }); - describe("methods", function() { - this.transfer = null; - this.root = null; - this.fileName = null; - this.localFilePath = null; - - beforeEach(function() { + describe('methods', function () { + this.transfer = null; + this.root = null; + this.fileName = null; + this.localFilePath = null; + beforeEach(function () { this.transfer = new FileTransfer(); // assign onprogress handler this.transfer.onprogress = defaultOnProgressHandler; // spy on the onprogress handler, but still call through to it - spyOn(this.transfer, "onprogress").and.callThrough(); + spyOn(this.transfer, 'onprogress').and.callThrough(); - this.root = this.persistentRoot; - this.fileName = "testFile.txt"; + this.root = this.persistentRoot; + this.fileName = 'testFile.txt'; this.localFilePath = this.root.toURL() + this.fileName; }); @@ -341,8 +341,7 @@ exports.defineAutoTests = function () { // - 'httpssss://example.com' // - 'apache.org', with subdomains="true" // - 'cordova-filetransfer.jitsu.com' - describe("download", function () { - + describe('download', function () { // helpers var verifyDownload = function (fileEntry, specContext) { expect(fileEntry.name).toBe(specContext.fileName); @@ -353,340 +352,379 @@ exports.defineAutoTests = function () { deleteFile(this.root, this.fileName, done); }); - it("ensures that test file does not exist", function (done) { + it('ensures that test file does not exist', function (done) { deleteFile(this.root, this.fileName, done); }); - it("filetransfer.spec.4 should download a file", function (done) { - var fileURL = SERVER + "/robots.txt"; - var specContext = this; - - var fileWin = function (blob) { - - if (specContext.transfer.onprogress.calls.any()) { - var lastProgressEvent = specContext.transfer.onprogress.calls.mostRecent().args[0]; - expect(lastProgressEvent.loaded).not.toBeGreaterThan(blob.size); - } else { - console.log("no progress events were emitted"); - } - - done(); - }; - - var fileSystemFail = function() { - unexpectedCallbacks.fileSystemFail(); - done(); - }; - - var downloadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; - - var downloadWin = function (entry) { - - verifyDownload(entry, specContext); + it( + 'filetransfer.spec.4 should download a file', + function (done) { + var fileURL = SERVER + '/robots.txt'; + var specContext = this; - // verify the FileEntry representing this file - entry.file(fileWin, fileSystemFail); - }; + var fileWin = function (blob) { + if (specContext.transfer.onprogress.calls.any()) { + var lastProgressEvent = specContext.transfer.onprogress.calls.mostRecent().args[0]; + expect(lastProgressEvent.loaded).not.toBeGreaterThan(blob.size); + } else { + console.log('no progress events were emitted'); + } - specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); - }, DOWNLOAD_TIMEOUT * 10); // to give Heroku server some time to wake up + done(); + }; - it("filetransfer.spec.4.1 should download a file using target name with space", function (done) { + var fileSystemFail = function () { + unexpectedCallbacks.fileSystemFail(); + done(); + }; - var fileURL = SERVER + "/robots.txt"; - this.fileName = "test file.txt"; - this.localFilePath = this.root.toURL() + this.fileName; + var downloadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - var specContext = this; + var downloadWin = function (entry) { + verifyDownload(entry, specContext); - var fileWin = function (blob) { + // verify the FileEntry representing this file + entry.file(fileWin, fileSystemFail); + }; - if (specContext.transfer.onprogress.calls.any()) { - var lastProgressEvent = specContext.transfer.onprogress.calls.mostRecent().args[0]; - expect(lastProgressEvent.loaded).not.toBeGreaterThan(blob.size); - } else { - console.log("no progress events were emitted"); - } + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); + }, + DOWNLOAD_TIMEOUT * 10 + ); // to give Heroku server some time to wake up - done(); - }; + it( + 'filetransfer.spec.4.1 should download a file using target name with space', + function (done) { + var fileURL = SERVER + '/robots.txt'; + this.fileName = 'test file.txt'; + this.localFilePath = this.root.toURL() + this.fileName; - var fileSystemFail = function() { - unexpectedCallbacks.fileSystemFail(); - done(); - }; + var specContext = this; - var downloadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; + var fileWin = function (blob) { + if (specContext.transfer.onprogress.calls.any()) { + var lastProgressEvent = specContext.transfer.onprogress.calls.mostRecent().args[0]; + expect(lastProgressEvent.loaded).not.toBeGreaterThan(blob.size); + } else { + console.log('no progress events were emitted'); + } - var downloadWin = function (entry) { + done(); + }; - verifyDownload(entry, specContext); + var fileSystemFail = function () { + unexpectedCallbacks.fileSystemFail(); + done(); + }; - // verify the FileEntry representing this file - entry.file(fileWin, fileSystemFail); - }; + var downloadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); - }, DOWNLOAD_TIMEOUT); + var downloadWin = function (entry) { + verifyDownload(entry, specContext); - it("filetransfer.spec.5 should download a file using http basic auth", function (done) { - var fileURL = SERVER_WITH_CREDENTIALS + "/download_basic_auth"; - var specContext = this; + // verify the FileEntry representing this file + entry.file(fileWin, fileSystemFail); + }; - var downloadWin = function (entry) { - verifyDownload(entry, specContext); - done(); - }; + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); + }, + DOWNLOAD_TIMEOUT + ); - var downloadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; + it( + 'filetransfer.spec.5 should download a file using http basic auth', + function (done) { + var fileURL = SERVER_WITH_CREDENTIALS + '/download_basic_auth'; + var specContext = this; - specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); - }, DOWNLOAD_TIMEOUT); + var downloadWin = function (entry) { + verifyDownload(entry, specContext); + done(); + }; - it("filetransfer.spec.6 should get 401 status on http basic auth failure", function (done) { - // NOTE: - // using server without credentials - var fileURL = SERVER + "/download_basic_auth"; + var downloadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - var downloadFail = function (error) { - expect(error.http_status).toBe(401); - expect(error.http_status).not.toBe(404, "Ensure " + fileURL + " is in the white list"); - done(); - }; + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); + }, + DOWNLOAD_TIMEOUT + ); + + it( + 'filetransfer.spec.6 should get 401 status on http basic auth failure', + function (done) { + // NOTE: + // using server without credentials + var fileURL = SERVER + '/download_basic_auth'; + + var downloadFail = function (error) { + expect(error.http_status).toBe(401); + expect(error.http_status).not.toBe(404, 'Ensure ' + fileURL + ' is in the white list'); + done(); + }; - var downloadWin = function() { - unexpectedCallbacks.httpWin(); - done(); - }; + var downloadWin = function () { + unexpectedCallbacks.httpWin(); + done(); + }; - this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail, null, - { + this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail, null, { headers: { - "If-Modified-Since": "Thu, 19 Mar 2015 00:00:00 GMT" + 'If-Modified-Since': 'Thu, 19 Mar 2015 00:00:00 GMT' } }); - }, DOWNLOAD_TIMEOUT); - - it("filetransfer.spec.7 should download a file using file:// (when hosted from file://)", function (done) { - // for Windows platform it's ms-appdata:/// by default, not file:// - if (isWindows) { - pending(); - return; - } - - var fileURL = window.location.protocol + "//" + window.location.pathname.replace(/ /g, "%20"); - var specContext = this; - - if (!/^file:/.exec(fileURL)) { - done(); - return; - } - - var downloadWin = function (entry) { - verifyDownload(entry, specContext); - done(); - }; - - var downloadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; - - specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); - }, DOWNLOAD_TIMEOUT); + }, + DOWNLOAD_TIMEOUT + ); + + it( + 'filetransfer.spec.7 should download a file using file:// (when hosted from file://)', + function (done) { + // for Windows platform it's ms-appdata:/// by default, not file:// + if (isWindows) { + pending(); + return; + } - it("filetransfer.spec.8 should download a file using https://", function (done) { - var fileURL = "https://www.apache.org/licenses/"; - var specContext = this; + var fileURL = window.location.protocol + '//' + window.location.pathname.replace(/ /g, '%20'); + var specContext = this; - var downloadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; + if (!/^file:/.exec(fileURL)) { + done(); + return; + } - var fileOperationFail = function() { - unexpectedCallbacks.fileOperationFail(); - done(); - }; + var downloadWin = function (entry) { + verifyDownload(entry, specContext); + done(); + }; - var fileSystemFail = function() { - unexpectedCallbacks.fileSystemFail(); - done(); - }; + var downloadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - var fileWin = function (file) { + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); + }, + DOWNLOAD_TIMEOUT + ); - var reader = new FileReader(); + it( + 'filetransfer.spec.8 should download a file using https://', + function (done) { + var fileURL = 'https://www.apache.org/licenses/'; + var specContext = this; - reader.onerror = fileOperationFail; - reader.onload = function () { - expect(reader.result).toMatch(/The Apache Software Foundation/); + var downloadFail = function () { + unexpectedCallbacks.httpFail(); done(); }; - reader.readAsText(file); - }; - - var downloadWin = function (entry) { - verifyDownload(entry, specContext); - entry.file(fileWin, fileSystemFail); - }; + var fileOperationFail = function () { + unexpectedCallbacks.fileOperationFail(); + done(); + }; - specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); - }, isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT); + var fileSystemFail = function () { + unexpectedCallbacks.fileSystemFail(); + done(); + }; - it("filetransfer.spec.11 should call the error callback on abort()", function (done) { - var fileURL = "http://cordova.apache.org/downloads/BlueZedEx.mp3"; - fileURL = fileURL + "?q=" + (new Date()).getTime(); - var specContext = this; + var fileWin = function (file) { + var reader = new FileReader(); - var downloadWin = function () { - unexpectedCallbacks.httpWin(); - done(); - }; + reader.onerror = fileOperationFail; + reader.onload = function () { + expect(reader.result).toMatch(/The Apache Software Foundation/); + done(); + }; - specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, done); - setTimeout(function() { - specContext.transfer.abort(); - }, ABORT_DELAY); - }, DOWNLOAD_TIMEOUT); + reader.readAsText(file); + }; - it("filetransfer.spec.9 should not leave partial file due to abort", function (done) { - var fileURL = "http://cordova.apache.org/downloads/logos_2.zip"; - var specContext = this; + var downloadWin = function (entry) { + verifyDownload(entry, specContext); + entry.file(fileWin, fileSystemFail); + }; - var fileSystemWin = function() { - unexpectedCallbacks.fileSystemWin(); - done(); - }; + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); + }, + isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT + ); - var downloadWin = function() { - unexpectedCallbacks.httpWin(); - done(); - }; + it( + 'filetransfer.spec.11 should call the error callback on abort()', + function (done) { + var fileURL = 'http://cordova.apache.org/downloads/BlueZedEx.mp3'; + fileURL = fileURL + '?q=' + new Date().getTime(); + var specContext = this; - var downloadFail = function (error) { + var downloadWin = function () { + unexpectedCallbacks.httpWin(); + done(); + }; - var result = (error.code === FileTransferError.ABORT_ERR || error.code === FileTransferError.CONNECTION_ERR)? true: false; - if (!result) { - fail("Expected " + error.code + " to be " + FileTransferError.ABORT_ERR + " or " + FileTransferError.CONNECTION_ERR); - } - expect(specContext.transfer.onprogress).toHaveBeenCalled(); + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, done); + setTimeout(function () { + specContext.transfer.abort(); + }, ABORT_DELAY); + }, + DOWNLOAD_TIMEOUT + ); + + it( + 'filetransfer.spec.9 should not leave partial file due to abort', + function (done) { + var fileURL = 'http://cordova.apache.org/downloads/logos_2.zip'; + var specContext = this; - // check that there is no file - specContext.root.getFile(specContext.fileName, null, fileSystemWin, done); - }; + var fileSystemWin = function () { + unexpectedCallbacks.fileSystemWin(); + done(); + }; - // abort at the first onprogress event - specContext.transfer.onprogress = function (event) { - if (event.loaded > 0) { - specContext.transfer.abort(); - } - }; + var downloadWin = function () { + unexpectedCallbacks.httpWin(); + done(); + }; - spyOn(specContext.transfer, "onprogress").and.callThrough(); + var downloadFail = function (error) { + var result = !!(error.code === FileTransferError.ABORT_ERR || error.code === FileTransferError.CONNECTION_ERR); + if (!result) { + fail( + 'Expected ' + + error.code + + ' to be ' + + FileTransferError.ABORT_ERR + + ' or ' + + FileTransferError.CONNECTION_ERR + ); + } + expect(specContext.transfer.onprogress).toHaveBeenCalled(); - specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); - }, isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT); + // check that there is no file + specContext.root.getFile(specContext.fileName, null, fileSystemWin, done); + }; - it("filetransfer.spec.10 should be stopped by abort()", function (done) { - var fileURL = "http://cordova.apache.org/downloads/BlueZedEx.mp3"; - fileURL = fileURL + "?q=" + (new Date()).getTime(); - var specContext = this; + // abort at the first onprogress event + specContext.transfer.onprogress = function (event) { + if (event.loaded > 0) { + specContext.transfer.abort(); + } + }; - expect(specContext.transfer.abort).not.toThrow(); // should be a no-op. + spyOn(specContext.transfer, 'onprogress').and.callThrough(); - var downloadWin = function() { - unexpectedCallbacks.httpWin(); - done(); - }; + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); + }, + isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT + ); - var downloadFail = function (error) { + it( + 'filetransfer.spec.10 should be stopped by abort()', + function (done) { + var fileURL = 'http://cordova.apache.org/downloads/BlueZedEx.mp3'; + fileURL = fileURL + '?q=' + new Date().getTime(); + var specContext = this; - expect(error.code).toBe(FileTransferError.ABORT_ERR); + expect(specContext.transfer.abort).not.toThrow(); // should be a no-op. - // delay calling done() to wait for the bogus abort() - setTimeout(done, GRACE_TIME_DELTA * 2); - }; + var downloadWin = function () { + unexpectedCallbacks.httpWin(); + done(); + }; - specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); - setTimeout(function() { - specContext.transfer.abort(); - }, ABORT_DELAY); + var downloadFail = function (error) { + expect(error.code).toBe(FileTransferError.ABORT_ERR); - // call abort() again, after a time greater than the grace period - setTimeout(function () { - expect(specContext.transfer.abort).not.toThrow(); - }, GRACE_TIME_DELTA); - }, DOWNLOAD_TIMEOUT); + // delay calling done() to wait for the bogus abort() + setTimeout(done, GRACE_TIME_DELTA * 2); + }; - it("filetransfer.spec.12 should get http status on failure", function (done) { - var fileURL = SERVER + "/404"; + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); + setTimeout(function () { + specContext.transfer.abort(); + }, ABORT_DELAY); - var downloadFail = function (error) { + // call abort() again, after a time greater than the grace period + setTimeout(function () { + expect(specContext.transfer.abort).not.toThrow(); + }, GRACE_TIME_DELTA); + }, + DOWNLOAD_TIMEOUT + ); - expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); - expect(error.http_status).toBe(404); + it( + 'filetransfer.spec.12 should get http status on failure', + function (done) { + var fileURL = SERVER + '/404'; - expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR); + var downloadFail = function (error) { + expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list'); + expect(error.http_status).toBe(404); - done(); - }; + expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR); - var downloadWin = function() { - unexpectedCallbacks.httpWin(); - done(); - }; + done(); + }; - this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); - }, DOWNLOAD_TIMEOUT); + var downloadWin = function () { + unexpectedCallbacks.httpWin(); + done(); + }; - it("filetransfer.spec.13 should get http body on failure", function (done) { - var fileURL = SERVER + "/404"; + this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); + }, + DOWNLOAD_TIMEOUT + ); - var downloadFail = function (error) { + it( + 'filetransfer.spec.13 should get http body on failure', + function (done) { + var fileURL = SERVER + '/404'; - expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); - expect(error.http_status).toBe(404); + var downloadFail = function (error) { + expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list'); + expect(error.http_status).toBe(404); - expect(error.body).toBeDefined(); - expect(error.body).toMatch("You requested a 404"); + expect(error.body).toBeDefined(); + expect(error.body).toMatch('You requested a 404'); - done(); - }; + done(); + }; - var downloadWin = function() { - unexpectedCallbacks.httpWin(); - done(); - }; + var downloadWin = function () { + unexpectedCallbacks.httpWin(); + done(); + }; - this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); - }, DOWNLOAD_TIMEOUT); + this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); + }, + DOWNLOAD_TIMEOUT + ); - it("filetransfer.spec.14 should handle malformed urls", function (done) { + it('filetransfer.spec.14 should handle malformed urls', function (done) { var fileURL = getMalformedUrl(); var downloadFail = function (error) { - // Note: Android needs the bad protocol to be added to the access list // won't match because ^https?:// is prepended to the regex // The bad protocol must begin with http to avoid automatic prefix - expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); + expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list'); expect(error.code).toBe(FileTransferError.INVALID_URL_ERR); done(); }; - var downloadWin = function() { + var downloadWin = function () { unexpectedCallbacks.httpWin(); done(); }; @@ -694,90 +732,101 @@ exports.defineAutoTests = function () { this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); }); - it("filetransfer.spec.15 should handle unknown host", function (done) { - var fileURL = UNKNOWN_HOST; + it( + 'filetransfer.spec.15 should handle unknown host', + function (done) { + var fileURL = UNKNOWN_HOST; - var downloadFail = function (error) { - expect(error.code).toBe(FileTransferError.CONNECTION_ERR); - done(); - }; + var downloadFail = function (error) { + expect(error.code).toBe(FileTransferError.CONNECTION_ERR); + done(); + }; - var downloadWin = function() { - unexpectedCallbacks.httpWin(); - done(); - }; + var downloadWin = function () { + unexpectedCallbacks.httpWin(); + done(); + }; - // turn off the onprogress handler - this.transfer.onprogress = function () {}; + // turn off the onprogress handler + this.transfer.onprogress = function () {}; - this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); - }, isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT); + this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); + }, + isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT + ); - it("filetransfer.spec.16 should handle bad file path", function (done) { + it('filetransfer.spec.16 should handle bad file path', function (done) { var fileURL = SERVER; - var downloadWin = function() { + var downloadWin = function () { unexpectedCallbacks.httpWin(); done(); }; - this.transfer.download(fileURL, "c:\\54321", downloadWin, done); + this.transfer.download(fileURL, 'c:\\54321', downloadWin, done); }); - it("filetransfer.spec.17 progress should work with gzip encoding", function (done) { - var fileURL = "http://www.apache.org/"; - var specContext = this; + it( + 'filetransfer.spec.17 progress should work with gzip encoding', + function (done) { + var fileURL = 'http://www.apache.org/'; + var specContext = this; - var downloadWin = function (entry) { - verifyDownload(entry, specContext); - done(); - }; + var downloadWin = function (entry) { + verifyDownload(entry, specContext); + done(); + }; - var downloadFail = function () { - unexpectedCallbacks.httpFail(); - done(); - }; + var downloadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin,downloadFail); - }, DOWNLOAD_TIMEOUT); + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); + }, + DOWNLOAD_TIMEOUT + ); - it("filetransfer.spec.30 downloaded file entries should have a toNativeURL method", function (done) { - if (cordova.platformId === "browser") { - pending(); - return; - } + it( + 'filetransfer.spec.30 downloaded file entries should have a toNativeURL method', + function (done) { + if (cordova.platformId === 'browser') { + pending(); + return; + } - var fileURL = SERVER + "/robots.txt"; + var fileURL = SERVER + '/robots.txt'; - var downloadWin = function (entry) { + var downloadWin = function (entry) { + expect(entry.toNativeURL).toBeDefined(); + expect(entry.toNativeURL).toEqual(jasmine.any(Function)); - expect(entry.toNativeURL).toBeDefined(); - expect(entry.toNativeURL).toEqual(jasmine.any(Function)); + var nativeURL = entry.toNativeURL(); - var nativeURL = entry.toNativeURL(); + expect(nativeURL).toBeTruthy(); + expect(nativeURL).toEqual(jasmine.any(String)); - expect(nativeURL).toBeTruthy(); - expect(nativeURL).toEqual(jasmine.any(String)); + if (isWindows) { + expect(nativeURL.substring(0, 14)).toBe('ms-appdata:///'); + } else { + expect(nativeURL.substring(0, 7)).toBe('file://'); + } - if (isWindows) { - expect(nativeURL.substring(0, 14)).toBe("ms-appdata:///"); - } else { - expect(nativeURL.substring(0, 7)).toBe("file://"); - } - - done(); - }; + done(); + }; - var downloadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; + var downloadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); - }, DOWNLOAD_TIMEOUT); + this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); + }, + DOWNLOAD_TIMEOUT + ); - it("filetransfer.spec.28 (compatibility) should be able to download a file using local paths", function (done) { - var fileURL = SERVER + "/robots.txt"; + it('filetransfer.spec.28 (compatibility) should be able to download a file using local paths', function (done) { + var fileURL = SERVER + '/robots.txt'; var specContext = this; var unsupported = function (response) { @@ -797,7 +846,7 @@ exports.defineAutoTests = function () { internalFilePath = specContext.localFilePath; } - var downloadFail = function() { + var downloadFail = function () { unexpectedCallbacks.httpFail(); done(); }; @@ -806,149 +855,173 @@ exports.defineAutoTests = function () { // backwards compatibilty. By obtaining the raw filesystem path of the download // location, we can pass that to transfer.download() to make sure that previously-stored // paths are still valid. - cordova.exec(function (localPath) { - specContext.transfer.download(fileURL, localPath, downloadWin, downloadFail); - }, unsupported, "File", "_getLocalFilesystemPath", [internalFilePath]); + cordova.exec( + function (localPath) { + specContext.transfer.download(fileURL, localPath, downloadWin, downloadFail); + }, + unsupported, + 'File', + '_getLocalFilesystemPath', + [internalFilePath] + ); }); - it("filetransfer.spec.33 should properly handle 304", function (done) { - var downloadFail = function (error) { - expect(error.http_status).toBe(304); - expect(error.code).toBe(FileTransferError.NOT_MODIFIED_ERR); - done(); - }; + it( + 'filetransfer.spec.33 should properly handle 304', + function (done) { + var downloadFail = function (error) { + expect(error.http_status).toBe(304); + expect(error.code).toBe(FileTransferError.NOT_MODIFIED_ERR); + done(); + }; - var downloadWin = function() { - unexpectedCallbacks.httpWin(); - done(); - }; + var downloadWin = function () { + unexpectedCallbacks.httpWin(); + done(); + }; - this.transfer.download(SERVER + '/304', this.localFilePath, downloadWin, downloadFail); - }, DOWNLOAD_TIMEOUT); + this.transfer.download(SERVER + '/304', this.localFilePath, downloadWin, downloadFail); + }, + DOWNLOAD_TIMEOUT + ); - it("filetransfer.spec.35 304 should not result in the deletion of a cached file", function (done) { - var specContext = this; + it( + 'filetransfer.spec.35 304 should not result in the deletion of a cached file', + function (done) { + var specContext = this; - var fileOperationFail = function() { - unexpectedCallbacks.fileOperationFail(); - done(); - }; + var fileOperationFail = function () { + unexpectedCallbacks.fileOperationFail(); + done(); + }; - var fileSystemFail = function() { - unexpectedCallbacks.fileSystemFail(); - done(); - }; + var fileSystemFail = function () { + unexpectedCallbacks.fileSystemFail(); + done(); + }; - var httpWin = function() { - unexpectedCallbacks.httpWin(); - done(); - }; + var httpWin = function () { + unexpectedCallbacks.httpWin(); + done(); + }; - var downloadFail = function (error) { - expect(error.http_status).toBe(304); - expect(error.code).toBe(FileTransferError.NOT_MODIFIED_ERR); + var downloadFail = function (error) { + expect(error.http_status).toBe(304); + expect(error.code).toBe(FileTransferError.NOT_MODIFIED_ERR); - specContext.persistentRoot.getFile(specContext.fileName, { create: false }, - function (entry) { - var fileWin = function (file) { - var reader = new FileReader(); + specContext.persistentRoot.getFile( + specContext.fileName, + { create: false }, + function (entry) { + var fileWin = function (file) { + var reader = new FileReader(); - reader.onerror = fileOperationFail; - reader.onloadend = function () { + reader.onerror = fileOperationFail; + reader.onloadend = function () { + expect(reader.result).toBeTruthy(); + if (reader.result !== null) { + expect(reader.result.length).toBeGreaterThan(0); + } - expect(reader.result).toBeTruthy(); - if (reader.result !== null) { - expect(reader.result.length).toBeGreaterThan(0); - } + done(); + }; - done(); + reader.readAsBinaryString(file); }; - reader.readAsBinaryString(file); - }; + entry.file(fileWin, fileSystemFail); + }, + function (err) { + expect( + "Could not open test file '" + specContext.fileName + "': " + JSON.stringify(err) + ).not.toBeDefined(); + done(); + } + ); + }; - entry.file(fileWin, fileSystemFail); + writeFile( + specContext.root, + specContext.fileName, + 'Temp data', + function () { + specContext.transfer.download(SERVER + '/304', specContext.localFilePath, httpWin, downloadFail); }, - function (err) { - expect("Could not open test file '" + specContext.fileName + "': " + JSON.stringify(err)).not.toBeDefined(); - done(); - } + fileOperationFail ); - }; - - writeFile(specContext.root, specContext.fileName, 'Temp data', function () { - specContext.transfer.download(SERVER + '/304', specContext.localFilePath, httpWin, downloadFail); - }, fileOperationFail); - }, DOWNLOAD_TIMEOUT); - - it("filetransfer.spec.36 should handle non-UTF8 encoded download response", function (done) { - // Only iOS is supported: https://issues.apache.org/jira/browse/CB-9840 - if (!isIos) { - pending(); - } + }, + DOWNLOAD_TIMEOUT + ); + + it( + 'filetransfer.spec.36 should handle non-UTF8 encoded download response', + function (done) { + // Only iOS is supported: https://issues.apache.org/jira/browse/CB-9840 + if (!isIos) { + pending(); + } - var fileURL = SERVER + '/download_non_utf'; - var specContext = this; + var fileURL = SERVER + '/download_non_utf'; + var specContext = this; - var fileOperationFail = function() { - unexpectedCallbacks.fileOperationFail(); - done(); - }; + var fileOperationFail = function () { + unexpectedCallbacks.fileOperationFail(); + done(); + }; - var fileSystemFail = function() { - unexpectedCallbacks.fileSystemFail(); - done(); - }; + var fileSystemFail = function () { + unexpectedCallbacks.fileSystemFail(); + done(); + }; - var httpFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; + var httpFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - var fileWin = function (blob) { + var fileWin = function (blob) { + if (specContext.transfer.onprogress.calls.any()) { + var lastProgressEvent = specContext.transfer.onprogress.calls.mostRecent().args[0]; + expect(lastProgressEvent.loaded).not.toBeGreaterThan(blob.size); + } else { + console.log('no progress events were emitted'); + } - if (specContext.transfer.onprogress.calls.any()) { - var lastProgressEvent = specContext.transfer.onprogress.calls.mostRecent().args[0]; - expect(lastProgressEvent.loaded).not.toBeGreaterThan(blob.size); - } else { - console.log("no progress events were emitted"); - } + expect(blob.size).toBeGreaterThan(0); - expect(blob.size).toBeGreaterThan(0); + var reader = new FileReader(); - var reader = new FileReader(); + reader.onerror = fileOperationFail; + reader.onloadend = function () { + expect(reader.result.indexOf(LATIN1_SYMBOLS)).not.toBe(-1); + done(); + }; - reader.onerror = fileOperationFail; - reader.onloadend = function () { - expect(reader.result.indexOf(LATIN1_SYMBOLS)).not.toBe(-1); - done(); + reader.readAsBinaryString(blob); }; - reader.readAsBinaryString(blob); - }; - - var downloadWin = function (entry) { + var downloadWin = function (entry) { + verifyDownload(entry, specContext); - verifyDownload(entry, specContext); - - // verify the FileEntry representing this file - entry.file(fileWin, fileSystemFail); - }; + // verify the FileEntry representing this file + entry.file(fileWin, fileSystemFail); + }; - specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, httpFail); - }, UPLOAD_TIMEOUT); + specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, httpFail); + }, + UPLOAD_TIMEOUT + ); }); - describe("upload", function() { - this.uploadParams = null; - this.uploadOptions = null; - this.fileName = null; - this.fileContents = null; - this.localFilePath = null; + describe('upload', function () { + this.uploadParams = null; + this.uploadOptions = null; + this.fileName = null; + this.fileContents = null; + this.localFilePath = null; // helpers var verifyUpload = function (uploadResult, specContext) { - expect(uploadResult.bytesSent).toBeGreaterThan(0); expect(uploadResult.responseCode).toBe(200); @@ -956,30 +1029,30 @@ exports.defineAutoTests = function () { try { obj = JSON.parse(uploadResult.response); expect(obj.fields).toBeDefined(); - expect(obj.fields.value1).toBe("test"); - expect(obj.fields.value2).toBe("param"); + expect(obj.fields.value1).toBe('test'); + expect(obj.fields.value2).toBe('param'); } catch (e) { - expect(obj).not.toBeNull("returned data from server should be valid json"); + expect(obj).not.toBeNull('returned data from server should be valid json'); } expect(specContext.transfer.onprogress).toHaveBeenCalled(); }; - beforeEach(function(done) { + beforeEach(function (done) { var specContext = this; - specContext.fileName = "fileToUpload.txt"; - specContext.fileContents = "upload test file"; + specContext.fileName = 'fileToUpload.txt'; + specContext.fileContents = 'upload test file'; - specContext.uploadParams = {}; - specContext.uploadParams.value1 = "test"; - specContext.uploadParams.value2 = "param"; + specContext.uploadParams = {}; + specContext.uploadParams.value1 = 'test'; + specContext.uploadParams.value2 = 'param'; - specContext.uploadOptions = new FileUploadOptions(); - specContext.uploadOptions.fileKey = "file"; + specContext.uploadOptions = new FileUploadOptions(); + specContext.uploadOptions.fileKey = 'file'; specContext.uploadOptions.fileName = specContext.fileName; - specContext.uploadOptions.mimeType = "text/plain"; - specContext.uploadOptions.params = specContext.uploadParams; + specContext.uploadOptions.mimeType = 'text/plain'; + specContext.uploadOptions.params = specContext.uploadParams; var fileWin = function (entry) { specContext.localFilePath = entry.toURL(); @@ -995,123 +1068,143 @@ exports.defineAutoTests = function () { deleteFile(this.root, this.fileName, done); }); - it("filetransfer.spec.18 should be able to upload a file", function (done) { - var fileURL = SERVER + "/upload"; - var specContext = this; - - var uploadWin = function (uploadResult) { - - verifyUpload(uploadResult, specContext); - - if (cordova.platformId === "ios") { - expect(uploadResult.headers).toBeDefined("Expected headers to be defined."); - expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined."); - } - - done(); - }; - - var uploadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; - - // NOTE: removing uploadOptions cause Android to timeout - specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); - }, UPLOAD_TIMEOUT); + it( + 'filetransfer.spec.18 should be able to upload a file', + function (done) { + var fileURL = SERVER + '/upload'; + var specContext = this; - it("filetransfer.spec.19 should be able to upload a file with http basic auth", function (done) { - var fileURL = SERVER_WITH_CREDENTIALS + "/upload_basic_auth"; - var specContext = this; + var uploadWin = function (uploadResult) { + verifyUpload(uploadResult, specContext); - var uploadWin = function (uploadResult) { - verifyUpload(uploadResult, specContext); - done(); - }; + if (cordova.platformId === 'ios') { + expect(uploadResult.headers).toBeDefined('Expected headers to be defined.'); + expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.'); + } - var uploadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; + done(); + }; - // NOTE: removing uploadOptions cause Android to timeout - specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); - }, UPLOAD_TIMEOUT); + var uploadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - it("filetransfer.spec.21 should be stopped by abort()", function (done) { - var fileURL = SERVER + "/upload"; - var specContext = this; + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); + }, + UPLOAD_TIMEOUT + ); + + it( + 'filetransfer.spec.19 should be able to upload a file with http basic auth', + function (done) { + var fileURL = SERVER_WITH_CREDENTIALS + '/upload_basic_auth'; + var specContext = this; - var uploadFail = function (e) { - expect(e.code).toBe(FileTransferError.ABORT_ERR); + var uploadWin = function (uploadResult) { + verifyUpload(uploadResult, specContext); + done(); + }; - // delay calling done() to wait for the bogus abort() - setTimeout(done, GRACE_TIME_DELTA * 2); - }; + var uploadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - var uploadWin = function() { - unexpectedCallbacks.httpWin(); - done(); - }; + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); + }, + UPLOAD_TIMEOUT + ); + + it( + 'filetransfer.spec.21 should be stopped by abort()', + function (done) { + var fileURL = SERVER + '/upload'; + var specContext = this; - var fileWin = function () { + var uploadFail = function (e) { + expect(e.code).toBe(FileTransferError.ABORT_ERR); - expect(specContext.transfer.abort).not.toThrow(); + // delay calling done() to wait for the bogus abort() + setTimeout(done, GRACE_TIME_DELTA * 2); + }; - // NOTE: removing uploadOptions cause Android to timeout - specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); - setTimeout(function() { - specContext.transfer.abort(); - }, ABORT_DELAY); + var uploadWin = function () { + unexpectedCallbacks.httpWin(); + done(); + }; - setTimeout(function () { + var fileWin = function () { expect(specContext.transfer.abort).not.toThrow(); - }, GRACE_TIME_DELTA); - }; - // windows store and ios are too fast, win is called before we have a chance to abort - // so let's get them busy - while not providing an extra load to the slow Android emulators - var arrayLength = ((isWindows && !isWindowsPhone) || isIos) ? 3000000 : isIot ? 150000 : 200000; - writeFile(specContext.root, specContext.fileName, new Array(arrayLength).join("aborttest!"), fileWin, done); - }, UPLOAD_TIMEOUT); + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload( + specContext.localFilePath, + fileURL, + uploadWin, + uploadFail, + specContext.uploadOptions + ); + setTimeout(function () { + specContext.transfer.abort(); + }, ABORT_DELAY); - it("filetransfer.spec.22 should get http status and body on failure", function (done) { - var fileURL = SERVER + "/403"; - var retryCount = 0; - var self = this; + setTimeout(function () { + expect(specContext.transfer.abort).not.toThrow(); + }, GRACE_TIME_DELTA); + }; - var uploadWin = function() { - unexpectedCallbacks.httpWin(); - done(); - }; + // windows store and ios are too fast, win is called before we have a chance to abort + // so let's get them busy - while not providing an extra load to the slow Android emulators + var arrayLength = (isWindows && !isWindowsPhone) || isIos ? 3000000 : isIot ? 150000 : 200000; + writeFile(specContext.root, specContext.fileName, new Array(arrayLength).join('aborttest!'), fileWin, done); + }, + UPLOAD_TIMEOUT + ); + + it( + 'filetransfer.spec.22 should get http status and body on failure', + function (done) { + var fileURL = SERVER + '/403'; + var retryCount = 0; + var self = this; - var uploadFail = function (error) { - if (error.http_status === 503 && ++retryCount <= RETRY_COUNT) { - // Heroku often gives this error, retry in 1 second - console.log('retrying... ' + retryCount); - setTimeout(function () { - self.transfer.upload(self.localFilePath, fileURL, uploadWin, uploadFail, self.uploadOptions); - }, RETRY_INTERVAL); - } else { - expect(error.http_status).toBe(403); - expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); + var uploadWin = function () { + unexpectedCallbacks.httpWin(); done(); - } - }; + }; - self.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, this.uploadOptions); - }, UPLOAD_TIMEOUT * 11); + var uploadFail = function (error) { + if (error.http_status === 503 && ++retryCount <= RETRY_COUNT) { + // Heroku often gives this error, retry in 1 second + console.log('retrying... ' + retryCount); + setTimeout(function () { + self.transfer.upload(self.localFilePath, fileURL, uploadWin, uploadFail, self.uploadOptions); + }, RETRY_INTERVAL); + } else { + expect(error.http_status).toBe(403); + expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list'); + done(); + } + }; + + self.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, this.uploadOptions); + }, + UPLOAD_TIMEOUT * 11 + ); - it("filetransfer.spec.24 should handle malformed urls", function (done) { + it('filetransfer.spec.24 should handle malformed urls', function (done) { var fileURL = getMalformedUrl(); var uploadFail = function (error) { expect(error.code).toBe(FileTransferError.INVALID_URL_ERR); - expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); + expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list'); done(); }; - var uploadWin = function() { + var uploadWin = function () { unexpectedCallbacks.httpWin(); done(); }; @@ -1119,424 +1212,492 @@ exports.defineAutoTests = function () { this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, {}); }); - it("filetransfer.spec.25 should handle unknown host", function (done) { - var fileURL = UNKNOWN_HOST; + it( + 'filetransfer.spec.25 should handle unknown host', + function (done) { + var fileURL = UNKNOWN_HOST; - var uploadFail = function (error) { - expect(error.code).toBe(FileTransferError.CONNECTION_ERR); - expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); - done(); - }; + var uploadFail = function (error) { + expect(error.code).toBe(FileTransferError.CONNECTION_ERR); + expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list'); + done(); + }; - var uploadWin = function() { - unexpectedCallbacks.httpWin(); - done(); - }; + var uploadWin = function () { + unexpectedCallbacks.httpWin(); + done(); + }; - this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, {}); - }, UPLOAD_TIMEOUT); + this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, {}); + }, + UPLOAD_TIMEOUT + ); - it("filetransfer.spec.25 should handle missing file", function (done) { - var fileURL = SERVER + "/upload"; + it( + 'filetransfer.spec.25 should handle missing file', + function (done) { + var fileURL = SERVER + '/upload'; - var uploadFail = function (error) { - expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR); - expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); - done(); - }; + var uploadFail = function (error) { + expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR); + expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list'); + done(); + }; - var uploadWin = function() { - unexpectedCallbacks.httpWin(); - done(); - }; + var uploadWin = function () { + unexpectedCallbacks.httpWin(); + done(); + }; - this.transfer.upload("does_not_exist.txt", fileURL, uploadWin, uploadFail); - }, UPLOAD_TIMEOUT); + this.transfer.upload('does_not_exist.txt', fileURL, uploadWin, uploadFail); + }, + UPLOAD_TIMEOUT + ); - it("filetransfer.spec.26 should handle bad file path", function (done) { - var fileURL = SERVER + "/upload"; + it('filetransfer.spec.26 should handle bad file path', function (done) { + var fileURL = SERVER + '/upload'; var uploadFail = function (error) { - expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); + expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list'); done(); }; - var uploadWin = function() { + var uploadWin = function () { unexpectedCallbacks.httpWin(); done(); }; - this.transfer.upload("c:\\54321", fileURL, uploadWin, uploadFail); + this.transfer.upload('c:\\54321', fileURL, uploadWin, uploadFail); }); - it("filetransfer.spec.27 should be able to set custom headers", function (done) { - var fileURL = SERVER + '/upload_echo_headers'; - var retryCount = 0; - var self = this; - - var uploadWin = function (uploadResult) { - - expect(uploadResult.bytesSent).toBeGreaterThan(0); - expect(uploadResult.responseCode).toBe(200); - expect(uploadResult.response).toBeDefined(); + it( + 'filetransfer.spec.27 should be able to set custom headers', + function (done) { + var fileURL = SERVER + '/upload_echo_headers'; + var retryCount = 0; + var self = this; - var responseHtml = decodeURIComponent(uploadResult.response); + var uploadWin = function (uploadResult) { + expect(uploadResult.bytesSent).toBeGreaterThan(0); + expect(uploadResult.responseCode).toBe(200); + expect(uploadResult.response).toBeDefined(); - expect(responseHtml).toMatch(/CustomHeader1[\s\S]*CustomValue1/i); - expect(responseHtml).toMatch(/CustomHeader2[\s\S]*CustomValue2[\s\S]*CustomValue3/i, "Should allow array values"); + var responseHtml = decodeURIComponent(uploadResult.response); - done(); - }; + expect(responseHtml).toMatch(/CustomHeader1[\s\S]*CustomValue1/i); + expect(responseHtml).toMatch( + /CustomHeader2[\s\S]*CustomValue2[\s\S]*CustomValue3/i, + 'Should allow array values' + ); - var uploadFail = function() { - if (++retryCount >= RETRY_COUNT) { - unexpectedCallbacks.httpFail(); done(); - } else { - console.log('retrying... ' + retryCount); - setTimeout(function () { - // NOTE: removing uploadOptions will cause Android to timeout - self.transfer.upload(self.localFilePath, fileURL, uploadWin, uploadFail, self.uploadOptions); - }, RETRY_INTERVAL); - } - }; - - this.uploadOptions.headers = { - "CustomHeader1": "CustomValue1", - "CustomHeader2": ["CustomValue2", "CustomValue3"], - }; - - // http://whatheaders.com does not support Transfer-Encoding: chunked - this.uploadOptions.chunkedMode = false; - - // NOTE: removing uploadOptions cause Android to timeout - this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, this.uploadOptions); - }, UPLOAD_TIMEOUT * 11); - - it("filetransfer.spec.29 (compatibility) should be able to upload a file using local paths", function (done) { - var fileURL = SERVER + "/upload"; - var specContext = this; - - var unsupported = function (response) { - expectedCallbacks.unsupportedOperation(response); - done(); - }; + }; - var uploadWin = function (uploadResult) { - verifyUpload(uploadResult, specContext); - done(); - }; + var uploadFail = function () { + if (++retryCount >= RETRY_COUNT) { + unexpectedCallbacks.httpFail(); + done(); + } else { + console.log('retrying... ' + retryCount); + setTimeout(function () { + // NOTE: removing uploadOptions will cause Android to timeout + self.transfer.upload(self.localFilePath, fileURL, uploadWin, uploadFail, self.uploadOptions); + }, RETRY_INTERVAL); + } + }; - var uploadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; + this.uploadOptions.headers = { + CustomHeader1: 'CustomValue1', + CustomHeader2: ['CustomValue2', 'CustomValue3'] + }; - var internalFilePath; - if (specContext.root.toInternalURL) { - internalFilePath = specContext.root.toInternalURL() + specContext.fileName; - } else { - internalFilePath = specContext.localFilePath; - } + // http://whatheaders.com does not support Transfer-Encoding: chunked + this.uploadOptions.chunkedMode = false; - // This is an undocumented interface to File which exists only for testing - // backwards compatibilty. By obtaining the raw filesystem path of the download - // location, we can pass that to transfer.download() to make sure that previously-stored - // paths are still valid. - cordova.exec(function (localPath) { - specContext.transfer.upload(localPath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); - }, unsupported, "File", "_getLocalFilesystemPath", [internalFilePath]); - }, UPLOAD_TIMEOUT); + // NOTE: removing uploadOptions cause Android to timeout + this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, this.uploadOptions); + }, + UPLOAD_TIMEOUT * 11 + ); + + it( + 'filetransfer.spec.29 (compatibility) should be able to upload a file using local paths', + function (done) { + var fileURL = SERVER + '/upload'; + var specContext = this; - it("filetransfer.spec.31 should be able to upload a file using PUT method", function (done) { - var fileURL = SERVER + "/upload"; - var specContext = this; + var unsupported = function (response) { + expectedCallbacks.unsupportedOperation(response); + done(); + }; - var uploadWin = function (uploadResult) { + var uploadWin = function (uploadResult) { + verifyUpload(uploadResult, specContext); + done(); + }; - verifyUpload(uploadResult, specContext); + var uploadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - if (cordova.platformId === "ios") { - expect(uploadResult.headers).toBeDefined("Expected headers to be defined."); - expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined."); + var internalFilePath; + if (specContext.root.toInternalURL) { + internalFilePath = specContext.root.toInternalURL() + specContext.fileName; + } else { + internalFilePath = specContext.localFilePath; } - done(); - }; - - var uploadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; - - specContext.uploadOptions.httpMethod = "PUT"; - - // NOTE: removing uploadOptions cause Android to timeout - specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); - }, UPLOAD_TIMEOUT); - - it("filetransfer.spec.32 should be able to upload a file (non-multipart)", function (done) { - var fileURL = SERVER + "/upload"; - var specContext = this; - - var uploadWin = function (uploadResult) { - - expect(uploadResult.bytesSent).toBeGreaterThan(0); - expect(uploadResult.responseCode).toBe(200); - expect(uploadResult.response).toBeDefined(); - if (uploadResult.response) { - expect(uploadResult.response).toEqual(specContext.fileContents + "\n"); - } - expect(specContext.transfer.onprogress).toHaveBeenCalled(); + // This is an undocumented interface to File which exists only for testing + // backwards compatibilty. By obtaining the raw filesystem path of the download + // location, we can pass that to transfer.download() to make sure that previously-stored + // paths are still valid. + cordova.exec( + function (localPath) { + specContext.transfer.upload(localPath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); + }, + unsupported, + 'File', + '_getLocalFilesystemPath', + [internalFilePath] + ); + }, + UPLOAD_TIMEOUT + ); + + it( + 'filetransfer.spec.31 should be able to upload a file using PUT method', + function (done) { + var fileURL = SERVER + '/upload'; + var specContext = this; - if (cordova.platformId === "ios") { - expect(uploadResult.headers).toBeDefined("Expected headers to be defined."); - expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined."); - } + var uploadWin = function (uploadResult) { + verifyUpload(uploadResult, specContext); - done(); - }; + if (cordova.platformId === 'ios') { + expect(uploadResult.headers).toBeDefined('Expected headers to be defined.'); + expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.'); + } - var uploadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; + done(); + }; - // Content-Type header disables multipart - specContext.uploadOptions.headers = { - "Content-Type": "text/plain" - }; + var uploadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - // NOTE: removing uploadOptions cause Android to timeout - specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); - }, UPLOAD_TIMEOUT); + specContext.uploadOptions.httpMethod = 'PUT'; - it("filetransfer.spec.34 should not delete a file on upload error", function (done) { + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); + }, + UPLOAD_TIMEOUT + ); + + it( + 'filetransfer.spec.32 should be able to upload a file (non-multipart)', + function (done) { + var fileURL = SERVER + '/upload'; + var specContext = this; - var fileURL = SERVER + "/upload"; - var specContext = this; + var uploadWin = function (uploadResult) { + expect(uploadResult.bytesSent).toBeGreaterThan(0); + expect(uploadResult.responseCode).toBe(200); + expect(uploadResult.response).toBeDefined(); + if (uploadResult.response) { + expect(uploadResult.response).toEqual(specContext.fileContents + '\n'); + } + expect(specContext.transfer.onprogress).toHaveBeenCalled(); - var uploadFail = function (e) { - expect(e.code).toBe(FileTransferError.ABORT_ERR); + if (cordova.platformId === 'ios') { + expect(uploadResult.headers).toBeDefined('Expected headers to be defined.'); + expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.'); + } - // check that the file is there - specContext.root.getFile(specContext.fileName, null, function(entry) { - expect(entry).toBeDefined(); - // delay calling done() to wait for the bogus abort() - setTimeout(done, GRACE_TIME_DELTA * 2); - }, function(err) { - expect(err).not.toBeDefined(err && err.code); done(); - }); - }; - - var uploadWin = function() { - unexpectedCallbacks.httpWin(); - done(); - }; - - var fileWin = function () { + }; - expect(specContext.transfer.abort).not.toThrow(); + var uploadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - // abort at the first onprogress event - specContext.transfer.onprogress = function (event) { - if (event.loaded > 0) { - specContext.transfer.abort(); - } + // Content-Type header disables multipart + specContext.uploadOptions.headers = { + 'Content-Type': 'text/plain' }; // NOTE: removing uploadOptions cause Android to timeout specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); - }; + }, + UPLOAD_TIMEOUT + ); + + it( + 'filetransfer.spec.34 should not delete a file on upload error', + function (done) { + var fileURL = SERVER + '/upload'; + var specContext = this; - writeFile(specContext.root, specContext.fileName, new Array(100000).join("aborttest!"), fileWin, done); - }, UPLOAD_TIMEOUT); + var uploadFail = function (e) { + expect(e.code).toBe(FileTransferError.ABORT_ERR); + + // check that the file is there + specContext.root.getFile( + specContext.fileName, + null, + function (entry) { + expect(entry).toBeDefined(); + // delay calling done() to wait for the bogus abort() + setTimeout(done, GRACE_TIME_DELTA * 2); + }, + function (err) { + expect(err).not.toBeDefined(err && err.code); + done(); + } + ); + }; - it("filetransfer.spec.37 should handle non-UTF8 encoded upload response", function (done) { - // Only iOS is supported: https://issues.apache.org/jira/browse/CB-9840 - if (!isIos) { - pending(); - } + var uploadWin = function () { + unexpectedCallbacks.httpWin(); + done(); + }; - var fileURL = SERVER + '/upload_non_utf'; - var specContext = this; + var fileWin = function () { + expect(specContext.transfer.abort).not.toThrow(); - var uploadWin = function (uploadResult) { + // abort at the first onprogress event + specContext.transfer.onprogress = function (event) { + if (event.loaded > 0) { + specContext.transfer.abort(); + } + }; - verifyUpload(uploadResult, specContext); + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload( + specContext.localFilePath, + fileURL, + uploadWin, + uploadFail, + specContext.uploadOptions + ); + }; - var obj = null; - try { - obj = JSON.parse(uploadResult.response); - expect(obj.latin1Symbols).toBe(LATIN1_SYMBOLS); - } catch (e) { - expect(obj).not.toBeNull("returned data from server should be valid json"); - } + writeFile(specContext.root, specContext.fileName, new Array(100000).join('aborttest!'), fileWin, done); + }, + UPLOAD_TIMEOUT + ); - if (cordova.platformId === 'ios') { - expect(uploadResult.headers).toBeDefined('Expected headers to be defined.'); - expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.'); + it( + 'filetransfer.spec.37 should handle non-UTF8 encoded upload response', + function (done) { + // Only iOS is supported: https://issues.apache.org/jira/browse/CB-9840 + if (!isIos) { + pending(); } - done(); - }; - - var uploadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; - - // NOTE: removing uploadOptions cause Android to timeout - specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); - }, UPLOAD_TIMEOUT); - - it("filetransfer.spec.38 should be able to upload a file using data: source uri", function (done) { - var fileURL = SERVER + "/upload"; - var specContext = this; + var fileURL = SERVER + '/upload_non_utf'; + var specContext = this; - var uploadWin = function (uploadResult) { + var uploadWin = function (uploadResult) { + verifyUpload(uploadResult, specContext); - verifyUpload(uploadResult,specContext); + var obj = null; + try { + obj = JSON.parse(uploadResult.response); + expect(obj.latin1Symbols).toBe(LATIN1_SYMBOLS); + } catch (e) { + expect(obj).not.toBeNull('returned data from server should be valid json'); + } - var obj = null; - try { - obj = JSON.parse(uploadResult.response); - expect(obj.files.file.size).toBe(DATA_URI_CONTENT_LENGTH); - } catch (e) { - expect(obj).not.toBeNull("returned data from server should be valid json"); - } + if (cordova.platformId === 'ios') { + expect(uploadResult.headers).toBeDefined('Expected headers to be defined.'); + expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.'); + } - if (cordova.platformId === "ios") { - expect(uploadResult.headers).toBeDefined("Expected headers to be defined."); - expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined."); - } + done(); + }; - done(); - }; + var uploadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - var dataUri = DATA_URI_PREFIX + DATA_URI_CONTENT; - // NOTE: removing uploadOptions cause Android to timeout - specContext.transfer.upload(dataUri, fileURL, uploadWin, function (err) { - console.error('err: ' + JSON.stringify(err)); - expect(err).not.toBeDefined(); - done(); - }, specContext.uploadOptions); - }, UPLOAD_TIMEOUT); + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); + }, + UPLOAD_TIMEOUT + ); + + it( + 'filetransfer.spec.38 should be able to upload a file using data: source uri', + function (done) { + var fileURL = SERVER + '/upload'; + var specContext = this; - it("filetransfer.spec.39 should be able to upload a file using data: source uri (non-multipart)", function (done) { - var fileURL = SERVER + "/upload"; + var uploadWin = function (uploadResult) { + verifyUpload(uploadResult, specContext); - var uploadWin = function (uploadResult) { + var obj = null; + try { + obj = JSON.parse(uploadResult.response); + expect(obj.files.file.size).toBe(DATA_URI_CONTENT_LENGTH); + } catch (e) { + expect(obj).not.toBeNull('returned data from server should be valid json'); + } - expect(uploadResult.responseCode).toBe(200); - expect(uploadResult.bytesSent).toBeGreaterThan(0); + if (cordova.platformId === 'ios') { + expect(uploadResult.headers).toBeDefined('Expected headers to be defined.'); + expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.'); + } - if (cordova.platformId === "ios") { - expect(uploadResult.headers).toBeDefined("Expected headers to be defined."); - expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined."); - } + done(); + }; - done(); - }; + var dataUri = DATA_URI_PREFIX + DATA_URI_CONTENT; + // NOTE: removing uploadOptions cause Android to timeout + specContext.transfer.upload( + dataUri, + fileURL, + uploadWin, + function (err) { + console.error('err: ' + JSON.stringify(err)); + expect(err).not.toBeDefined(); + done(); + }, + specContext.uploadOptions + ); + }, + UPLOAD_TIMEOUT + ); + + it( + 'filetransfer.spec.39 should be able to upload a file using data: source uri (non-multipart)', + function (done) { + var fileURL = SERVER + '/upload'; + + var uploadWin = function (uploadResult) { + expect(uploadResult.responseCode).toBe(200); + expect(uploadResult.bytesSent).toBeGreaterThan(0); + + if (cordova.platformId === 'ios') { + expect(uploadResult.headers).toBeDefined('Expected headers to be defined.'); + expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.'); + } - var uploadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; + done(); + }; - // Content-Type header disables multipart - this.uploadOptions.headers = { - "Content-Type": "image/png" - }; + var uploadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - var dataUri = DATA_URI_PREFIX + DATA_URI_CONTENT; - // NOTE: removing uploadOptions cause Android to timeout - this.transfer.upload(dataUri, fileURL, uploadWin, uploadFail, this.uploadOptions); - }, UPLOAD_TIMEOUT); + // Content-Type header disables multipart + this.uploadOptions.headers = { + 'Content-Type': 'image/png' + }; - it("filetransfer.spec.40 should not fail to upload a file using data: source uri when the data is empty", function (done) { - var fileURL = SERVER + "/upload"; + var dataUri = DATA_URI_PREFIX + DATA_URI_CONTENT; + // NOTE: removing uploadOptions cause Android to timeout + this.transfer.upload(dataUri, fileURL, uploadWin, uploadFail, this.uploadOptions); + }, + UPLOAD_TIMEOUT + ); - var dataUri = DATA_URI_PREFIX; + it( + 'filetransfer.spec.40 should not fail to upload a file using data: source uri when the data is empty', + function (done) { + var fileURL = SERVER + '/upload'; - var uploadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; + var dataUri = DATA_URI_PREFIX; - // NOTE: removing uploadOptions cause Android to timeout - this.transfer.upload(dataUri, fileURL, done, uploadFail, this.uploadOptions); - }, UPLOAD_TIMEOUT); + var uploadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - it("filetransfer.spec.41 should not fail to upload a file using data: source uri when the data is empty (non-multipart)", function (done) { - if (isIos) { - // iOS does not support uploads of an empty file with __chunkedMode=true__ and `multipartMode=false`: - // request body will be empty in this case instead of 0\n\n. - pending(); - } - var fileURL = SERVER + "/upload"; + // NOTE: removing uploadOptions cause Android to timeout + this.transfer.upload(dataUri, fileURL, done, uploadFail, this.uploadOptions); + }, + UPLOAD_TIMEOUT + ); + + it( + 'filetransfer.spec.41 should not fail to upload a file using data: source uri when the data is empty (non-multipart)', + function (done) { + if (isIos) { + // iOS does not support uploads of an empty file with __chunkedMode=true__ and `multipartMode=false`: + // request body will be empty in this case instead of 0\n\n. + pending(); + } + var fileURL = SERVER + '/upload'; - // Content-Type header disables multipart - this.uploadOptions.headers = { - "Content-Type": "image/png" - }; + // Content-Type header disables multipart + this.uploadOptions.headers = { + 'Content-Type': 'image/png' + }; - // turn off the onprogress handler - this.transfer.onprogress = function () { }; + // turn off the onprogress handler + this.transfer.onprogress = function () {}; - var dataUri = DATA_URI_PREFIX; + var dataUri = DATA_URI_PREFIX; - var uploadFail = function() { - unexpectedCallbacks.httpFail(); - done(); - }; + var uploadFail = function () { + unexpectedCallbacks.httpFail(); + done(); + }; - // NOTE: removing uploadOptions cause Android to timeout - this.transfer.upload(dataUri, fileURL, done, uploadFail, this.uploadOptions); - }, UPLOAD_TIMEOUT); + // NOTE: removing uploadOptions cause Android to timeout + this.transfer.upload(dataUri, fileURL, done, uploadFail, this.uploadOptions); + }, + UPLOAD_TIMEOUT + ); - describe("chunkedMode handling", function() { + describe('chunkedMode handling', function () { var testChunkedModeWin = function (uploadResult, specContext) { - var multipartModeEnabled = !(specContext.uploadOptions.headers && specContext.uploadOptions.headers["Content-Type"]); + var multipartModeEnabled = !( + specContext.uploadOptions.headers && specContext.uploadOptions.headers['Content-Type'] + ); var obj = null; try { obj = JSON.parse(uploadResult.response); if (specContext.uploadOptions.chunkedMode) { if (!isIos) { - expect(obj["content-length"]).not.toBeDefined("Expected Content-Length not to be defined"); + expect(obj['content-length']).not.toBeDefined('Expected Content-Length not to be defined'); } - expect(obj["transfer-encoding"].toLowerCase()).toEqual("chunked"); + expect(obj['transfer-encoding'].toLowerCase()).toEqual('chunked'); } else { - expect(obj["content-length"]).toBeDefined("Expected Content-Length to be defined"); - expect(obj["transfer-encoding"].toLowerCase()).not.toEqual("chunked"); + expect(obj['content-length']).toBeDefined('Expected Content-Length to be defined'); + expect(obj['transfer-encoding'].toLowerCase()).not.toEqual('chunked'); } if (multipartModeEnabled) { - expect(obj["content-type"].indexOf("multipart/form-data")).not.toBe(-1); + expect(obj['content-type'].indexOf('multipart/form-data')).not.toBe(-1); } else { - expect(obj["content-type"].indexOf("multipart/form-data")).toBe(-1); + expect(obj['content-type'].indexOf('multipart/form-data')).toBe(-1); } } catch (e) { - expect(obj).not.toBeNull("returned data from server should be valid json"); + expect(obj).not.toBeNull('returned data from server should be valid json'); } }; - var testChunkedModeBase = function(chunkedMode, multipart, done) { + var testChunkedModeBase = function (chunkedMode, multipart, done) { var retryCount = 0; - var fileURL = SERVER + "/upload_echo_headers"; + var fileURL = SERVER + '/upload_echo_headers'; var specContext = this; specContext.uploadOptions.chunkedMode = chunkedMode; if (!multipart) { // Content-Type header disables multipart specContext.uploadOptions.headers = { - "Content-Type": "text/plain" + 'Content-Type': 'text/plain' }; } - var uploadFail = function() { + var uploadFail = function () { if (++retryCount >= RETRY_COUNT) { unexpectedCallbacks.httpFail(); done(); @@ -1544,10 +1705,16 @@ exports.defineAutoTests = function () { console.log('retrying... ' + retryCount); setTimeout(function () { // NOTE: removing uploadOptions will cause Android to timeout - specContext.transfer.upload(specContext.localFilePath, fileURL, function (uploadResult) { - testChunkedModeWin(uploadResult, specContext); - done(); - }, uploadFail, specContext.uploadOptions); + specContext.transfer.upload( + specContext.localFilePath, + fileURL, + function (uploadResult) { + testChunkedModeWin(uploadResult, specContext); + done(); + }, + uploadFail, + specContext.uploadOptions + ); }, RETRY_INTERVAL); } }; @@ -1556,37 +1723,55 @@ exports.defineAutoTests = function () { this.transfer.onprogress = function () {}; // NOTE: removing uploadOptions cause Android to timeout - specContext.transfer.upload(specContext.localFilePath, fileURL, function (uploadResult) { - testChunkedModeWin(uploadResult, specContext); - done(); - }, uploadFail, specContext.uploadOptions); + specContext.transfer.upload( + specContext.localFilePath, + fileURL, + function (uploadResult) { + testChunkedModeWin(uploadResult, specContext); + done(); + }, + uploadFail, + specContext.uploadOptions + ); }; - it("filetransfer.spec.42 chunkedMode=false, multipart=false", function (done) { - - testChunkedModeBase.call(this, false, false, done); - }, UPLOAD_TIMEOUT * 11); - - it("filetransfer.spec.43 chunkedMode=true, multipart=false", function (done) { - - if (isWindows) { - pending(); - } - testChunkedModeBase.call(this, true, false, done); - }, UPLOAD_TIMEOUT * 11); - - it("filetransfer.spec.44 chunkedMode=false, multipart=true", function (done) { + it( + 'filetransfer.spec.42 chunkedMode=false, multipart=false', + function (done) { + testChunkedModeBase.call(this, false, false, done); + }, + UPLOAD_TIMEOUT * 11 + ); - testChunkedModeBase.call(this, false, true, done); - }, UPLOAD_TIMEOUT * 11); + it( + 'filetransfer.spec.43 chunkedMode=true, multipart=false', + function (done) { + if (isWindows) { + pending(); + } + testChunkedModeBase.call(this, true, false, done); + }, + UPLOAD_TIMEOUT * 11 + ); - it("filetransfer.spec.45 chunkedMode=true, multipart=true", function (done) { + it( + 'filetransfer.spec.44 chunkedMode=false, multipart=true', + function (done) { + testChunkedModeBase.call(this, false, true, done); + }, + UPLOAD_TIMEOUT * 11 + ); - if (isWindows) { - pending(); - } - testChunkedModeBase.call(this, true, true, done); - }, UPLOAD_TIMEOUT * 11); + it( + 'filetransfer.spec.45 chunkedMode=true, multipart=true', + function (done) { + if (isWindows) { + pending(); + } + testChunkedModeBase.call(this, true, true, done); + }, + UPLOAD_TIMEOUT * 11 + ); }); }); }); @@ -1598,29 +1783,28 @@ exports.defineAutoTests = function () { /******************************************************************************/ exports.defineManualTests = function (contentEl, createActionButton) { + 'use strict'; - "use strict"; + var imageURL = 'http://apache.org/images/feather-small.gif'; + var videoURL = 'http://techslides.com/demos/sample-videos/small.mp4'; - var imageURL = "http://apache.org/images/feather-small.gif"; - var videoURL = "http://techslides.com/demos/sample-videos/small.mp4"; - - function clearResults() { - var results = document.getElementById("info"); - results.innerHTML = ""; + function clearResults () { + var results = document.getElementById('info'); + results.innerHTML = ''; } - function downloadImg(source, urlFn, element, directory) { - var filename = source.substring(source.lastIndexOf("/") + 1); - filename = (directory || "") + filename; + function downloadImg (source, urlFn, element, directory) { + var filename = source.substring(source.lastIndexOf('/') + 1); + filename = (directory || '') + filename; - function download(fileSystem) { + function download (fileSystem) { var ft = new FileTransfer(); - console.log("Starting download"); + console.log('Starting download'); - var progress = document.getElementById("loadingStatus"); + var progress = document.getElementById('loadingStatus'); progress.value = 0; - ft.onprogress = function(progressEvent) { + ft.onprogress = function (progressEvent) { if (progressEvent.lengthComputable) { var currPercents = parseInt(100 * (progressEvent.loaded / progressEvent.total), 10); if (currPercents > progress.value) { @@ -1631,77 +1815,162 @@ exports.defineManualTests = function (contentEl, createActionButton) { } }; - ft.download(source, fileSystem.root.toURL() + filename, function (entry) { - console.log("Download complete"); - element.src = urlFn(entry); - console.log("Src URL is " + element.src); - console.log("Inserting element"); - document.getElementById("info").appendChild(element); - }, function (e) { console.log("ERROR: ft.download " + e.code); }); + ft.download( + source, + fileSystem.root.toURL() + filename, + function (entry) { + console.log('Download complete'); + element.src = urlFn(entry); + console.log('Src URL is ' + element.src); + console.log('Inserting element'); + document.getElementById('info').appendChild(element); + }, + function (e) { + console.log('ERROR: ft.download ' + e.code); + } + ); } - console.log("Requesting filesystem"); + console.log('Requesting filesystem'); clearResults(); - window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, function (fileSystem) { - console.log("Checking for existing file"); - if (typeof directory !== "undefined") { - console.log("Checking for existing directory."); - fileSystem.root.getDirectory(directory, {}, function (dirEntry) { - dirEntry.removeRecursively(function () { - download(fileSystem); - }, function () { console.log("ERROR: dirEntry.removeRecursively"); }); - }, function () { - download(fileSystem); - }); - } else { - fileSystem.root.getFile(filename, { create: false }, function (entry) { - console.log("Removing existing file"); - entry.remove(function () { - download(fileSystem); - }, function () { console.log("ERROR: entry.remove"); }); - }, function () { - download(fileSystem); - }); + window.requestFileSystem( + LocalFileSystem.TEMPORARY, + 0, + function (fileSystem) { + console.log('Checking for existing file'); + if (typeof directory !== 'undefined') { + console.log('Checking for existing directory.'); + fileSystem.root.getDirectory( + directory, + {}, + function (dirEntry) { + dirEntry.removeRecursively( + function () { + download(fileSystem); + }, + function () { + console.log('ERROR: dirEntry.removeRecursively'); + } + ); + }, + function () { + download(fileSystem); + } + ); + } else { + fileSystem.root.getFile( + filename, + { create: false }, + function (entry) { + console.log('Removing existing file'); + entry.remove( + function () { + download(fileSystem); + }, + function () { + console.log('ERROR: entry.remove'); + } + ); + }, + function () { + download(fileSystem); + } + ); + } + }, + function () { + console.log('ERROR: requestFileSystem'); } - }, function () { console.log("ERROR: requestFileSystem"); }); + ); } /******************************************************************************/ - var progress_tag = ""; - var file_transfer_tests = "

Image File Transfer Tests

" + - "

The following tests should display an image of the Apache feather in the status box

" + - "
" + - "
" + - "
" + - "

Video File Transfer Tests

" + - "

The following tests should display a video in the status box. The video should play when play is pressed

" + - "
" + - "
"; - - contentEl.innerHTML = "
" + "
" + progress_tag + - file_transfer_tests; - - createActionButton("Download and display img (cdvfile)", function () { - downloadImg(imageURL, function (entry) { return entry.toInternalURL(); }, new Image()); - }, "cdv_image"); - - createActionButton("Download and display img (native)", function () { - downloadImg(imageURL, function (entry) { return entry.toURL(); }, new Image()); - }, "native_image"); - - createActionButton("Download to a non-existent dir (should work)", function () { - downloadImg(imageURL, function (entry) { return entry.toURL(); }, new Image(), "/nonExistentDirTest/"); - }, "non-existent_dir"); - - createActionButton("Download and play video (cdvfile)", function () { - var videoElement = document.createElement("video"); - videoElement.controls = "controls"; - downloadImg(videoURL, function (entry) { return entry.toInternalURL(); }, videoElement); - }, "cdv_video"); - - createActionButton("Download and play video (native)", function () { - var videoElement = document.createElement("video"); - videoElement.controls = "controls"; - downloadImg(videoURL, function (entry) { return entry.toURL(); }, videoElement); - }, "native_video"); + var progress_tag = ''; + var file_transfer_tests = + '

Image File Transfer Tests

' + + '

The following tests should display an image of the Apache feather in the status box

' + + '
' + + '
' + + '
' + + '

Video File Transfer Tests

' + + '

The following tests should display a video in the status box. The video should play when play is pressed

' + + '
' + + '
'; + + contentEl.innerHTML = '
' + '
' + progress_tag + file_transfer_tests; + + createActionButton( + 'Download and display img (cdvfile)', + function () { + downloadImg( + imageURL, + function (entry) { + return entry.toInternalURL(); + }, + new Image() + ); + }, + 'cdv_image' + ); + + createActionButton( + 'Download and display img (native)', + function () { + downloadImg( + imageURL, + function (entry) { + return entry.toURL(); + }, + new Image() + ); + }, + 'native_image' + ); + + createActionButton( + 'Download to a non-existent dir (should work)', + function () { + downloadImg( + imageURL, + function (entry) { + return entry.toURL(); + }, + new Image(), + '/nonExistentDirTest/' + ); + }, + 'non-existent_dir' + ); + + createActionButton( + 'Download and play video (cdvfile)', + function () { + var videoElement = document.createElement('video'); + videoElement.controls = 'controls'; + downloadImg( + videoURL, + function (entry) { + return entry.toInternalURL(); + }, + videoElement + ); + }, + 'cdv_video' + ); + + createActionButton( + 'Download and play video (native)', + function () { + var videoElement = document.createElement('video'); + videoElement.controls = 'controls'; + downloadImg( + videoURL, + function (entry) { + return entry.toURL(); + }, + videoElement + ); + }, + 'native_video' + ); }; diff --git a/www/FileTransfer.js b/www/FileTransfer.js index 80cf91cb..99389717 100644 --- a/www/FileTransfer.js +++ b/www/FileTransfer.js @@ -17,16 +17,16 @@ * specific language governing permissions and limitations * under the License. * -*/ + */ /* global cordova, FileSystem */ -var argscheck = require('cordova/argscheck'), - exec = require('cordova/exec'), - FileTransferError = require('./FileTransferError'), - ProgressEvent = require('cordova-plugin-file.ProgressEvent'); +var argscheck = require('cordova/argscheck'); +var exec = require('cordova/exec'); +var FileTransferError = require('./FileTransferError'); +var ProgressEvent = require('cordova-plugin-file.ProgressEvent'); -function newProgressEvent(result) { +function newProgressEvent (result) { var pe = new ProgressEvent(); pe.lengthComputable = result.lengthComputable; pe.loaded = result.loaded; @@ -34,16 +34,15 @@ function newProgressEvent(result) { return pe; } -function getUrlCredentials(urlString) { - var credentialsPattern = /^https?\:\/\/(?:(?:(([^:@\/]*)(?::([^@\/]*))?)?@)?([^:\/?#]*)(?::(\d*))?).*$/, - credentials = credentialsPattern.exec(urlString); +function getUrlCredentials (urlString) { + var credentialsPattern = /^https?:\/\/(?:(?:(([^:@/]*)(?::([^@/]*))?)?@)?([^:/?#]*)(?::(\d*))?).*$/; + var credentials = credentialsPattern.exec(urlString); return credentials && credentials[1]; } -function getBasicAuthHeader(urlString) { - var header = null; - +function getBasicAuthHeader (urlString) { + var header = null; // This is changed due to MS Windows doesn't support credentials in http uris // so we detect them by regexp and strip off from result url @@ -52,12 +51,12 @@ function getBasicAuthHeader(urlString) { if (window.btoa) { var credentials = getUrlCredentials(urlString); if (credentials) { - var authHeader = "Authorization"; - var authHeaderValue = "Basic " + window.btoa(credentials); + var authHeader = 'Authorization'; + var authHeaderValue = 'Basic ' + window.btoa(credentials); header = { - name : authHeader, - value : authHeaderValue + name: authHeader, + value: authHeaderValue }; } } @@ -65,10 +64,10 @@ function getBasicAuthHeader(urlString) { return header; } -function convertHeadersToArray(headers) { +function convertHeadersToArray (headers) { var result = []; for (var header in headers) { - if (headers.hasOwnProperty(header)) { + if (Object.prototype.hasOwnProperty.call(headers, header)) { var headerValue = headers[header]; result.push({ name: header, @@ -85,22 +84,22 @@ var idCounter = 0; * FileTransfer uploads a file to a remote server. * @constructor */ -var FileTransfer = function() { +var FileTransfer = function () { this._id = ++idCounter; this.onprogress = null; // optional callback }; /** -* Given an absolute file path, uploads a file on the device to a remote server -* using a multipart HTTP request. -* @param filePath {String} Full path of the file on the device -* @param server {String} URL of the server to receive the file -* @param successCallback (Function} Callback to be invoked when upload has completed -* @param errorCallback {Function} Callback to be invoked upon error -* @param options {FileUploadOptions} Optional parameters such as file name and mimetype -* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false -*/ -FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + * Given an absolute file path, uploads a file on the device to a remote server + * using a multipart HTTP request. + * @param filePath {String} Full path of the file on the device + * @param server {String} URL of the server to receive the file + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + * @param options {FileUploadOptions} Optional parameters such as file name and mimetype + * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false + */ +FileTransfer.prototype.upload = function (filePath, server, successCallback, errorCallback, options, trustAllHosts) { argscheck.checkArgs('ssFFO*', 'FileTransfer.upload', arguments); // check for options var fileKey = null; @@ -124,36 +123,37 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro fileName = options.fileName; mimeType = options.mimeType; headers = options.headers; - httpMethod = options.httpMethod || "POST"; - if (httpMethod.toUpperCase() == "PUT"){ - httpMethod = "PUT"; + httpMethod = options.httpMethod || 'POST'; + if (httpMethod.toUpperCase() === 'PUT') { + httpMethod = 'PUT'; } else { - httpMethod = "POST"; + httpMethod = 'POST'; } - if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") { + if (options.chunkedMode !== null || typeof options.chunkedMode !== 'undefined') { chunkedMode = options.chunkedMode; } if (options.params) { params = options.params; - } - else { + } else { params = {}; } } - if (cordova.platformId === "windowsphone") { + if (cordova.platformId === 'windowsphone') { headers = headers && convertHeadersToArray(headers); params = params && convertHeadersToArray(params); } - var fail = errorCallback && function(e) { - var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body, e.exception); - errorCallback(error); - }; + var fail = + errorCallback && + function (e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body, e.exception); + errorCallback(error); + }; var self = this; - var win = function(result) { - if (typeof result.lengthComputable != "undefined") { + var win = function (result) { + if (typeof result.lengthComputable !== 'undefined') { if (self.onprogress) { self.onprogress(newProgressEvent(result)); } @@ -163,7 +163,19 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro } } }; - exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]); + exec(win, fail, 'FileTransfer', 'upload', [ + filePath, + server, + fileKey, + fileName, + mimeType, + params, + trustAllHosts, + chunkedMode, + headers, + this._id, + httpMethod + ]); }; /** @@ -175,7 +187,7 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false * @param options {FileDownloadOptions} Optional parameters such as headers */ -FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts, options) { +FileTransfer.prototype.download = function (source, target, successCallback, errorCallback, trustAllHosts, options) { argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments); var self = this; @@ -193,12 +205,12 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro headers = options.headers || null; } - if (cordova.platformId === "windowsphone" && headers) { + if (cordova.platformId === 'windowsphone' && headers) { headers = convertHeadersToArray(headers); } - var win = function(result) { - if (typeof result.lengthComputable != "undefined") { + var win = function (result) { + if (typeof result.lengthComputable !== 'undefined') { if (self.onprogress) { return self.onprogress(newProgressEvent(result)); } @@ -206,24 +218,27 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro var entry = null; if (result.isDirectory) { entry = new (require('cordova-plugin-file.DirectoryEntry'))(); - } - else if (result.isFile) { + } else if (result.isFile) { entry = new (require('cordova-plugin-file.FileEntry'))(); } entry.isDirectory = result.isDirectory; entry.isFile = result.isFile; entry.name = result.name; entry.fullPath = result.fullPath; - entry.filesystem = new FileSystem(result.filesystemName || (result.filesystem == window.PERSISTENT ? 'persistent' : 'temporary')); + entry.filesystem = new FileSystem( + result.filesystemName || (result.filesystem === window.PERSISTENT ? 'persistent' : 'temporary') + ); entry.nativeURL = result.nativeURL; successCallback(entry); } }; - var fail = errorCallback && function(e) { - var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body, e.exception); - errorCallback(error); - }; + var fail = + errorCallback && + function (e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body, e.exception); + errorCallback(error); + }; exec(win, fail, 'FileTransfer', 'download', [source, target, trustAllHosts, this._id, headers]); }; @@ -232,7 +247,7 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro * Aborts the ongoing file transfer on this object. The original error * callback for the file transfer will be called if necessary. */ -FileTransfer.prototype.abort = function() { +FileTransfer.prototype.abort = function () { exec(null, null, 'FileTransfer', 'abort', [this._id]); }; diff --git a/www/FileTransferError.js b/www/FileTransferError.js index 07ca8311..e3c15eb0 100644 --- a/www/FileTransferError.js +++ b/www/FileTransferError.js @@ -17,13 +17,13 @@ * specific language governing permissions and limitations * under the License. * -*/ + */ /** * FileTransferError * @constructor */ -var FileTransferError = function(code, source, target, status, body, exception) { +var FileTransferError = function (code, source, target, status, body, exception) { this.code = code || null; this.source = source || null; this.target = target || null; diff --git a/www/browser/FileTransfer.js b/www/browser/FileTransfer.js index 1cc66386..01e178e8 100644 --- a/www/browser/FileTransfer.js +++ b/www/browser/FileTransfer.js @@ -17,33 +17,32 @@ * specific language governing permissions and limitations * under the License. * -*/ + */ -/*global module, require*/ +/* global FileUploadResult */ -var argscheck = require('cordova/argscheck'), - FileTransferError = require('./FileTransferError'); +var argscheck = require('cordova/argscheck'); +var FileTransferError = require('./FileTransferError'); -function getParentPath(filePath) { +function getParentPath (filePath) { var pos = filePath.lastIndexOf('/'); return filePath.substring(0, pos + 1); } -function getFileName(filePath) { +function getFileName (filePath) { var pos = filePath.lastIndexOf('/'); return filePath.substring(pos + 1); } -function getUrlCredentials(urlString) { - var credentialsPattern = /^https?\:\/\/(?:(?:(([^:@\/]*)(?::([^@\/]*))?)?@)?([^:\/?#]*)(?::(\d*))?).*$/, - credentials = credentialsPattern.exec(urlString); +function getUrlCredentials (urlString) { + var credentialsPattern = /^https?:\/\/(?:(?:(([^:@/]*)(?::([^@/]*))?)?@)?([^:/?#]*)(?::(\d*))?).*$/; + var credentials = credentialsPattern.exec(urlString); return credentials && credentials[1]; } -function getBasicAuthHeader(urlString) { - var header = null; - +function getBasicAuthHeader (urlString) { + var header = null; // This is changed due to MS Windows doesn't support credentials in http uris // so we detect them by regexp and strip off from result url @@ -52,12 +51,12 @@ function getBasicAuthHeader(urlString) { if (window.btoa) { var credentials = getUrlCredentials(urlString); if (credentials) { - var authHeader = "Authorization"; - var authHeaderValue = "Basic " + window.btoa(credentials); + var authHeader = 'Authorization'; + var authHeaderValue = 'Basic ' + window.btoa(credentials); header = { - name : authHeader, - value : authHeaderValue + name: authHeader, + value: authHeaderValue }; } } @@ -65,8 +64,8 @@ function getBasicAuthHeader(urlString) { return header; } -function checkURL(url) { - return url.indexOf(' ') === -1 ? true : false; +function checkURL (url) { + return url.indexOf(' ') === -1; } var idCounter = 0; @@ -77,7 +76,7 @@ var transfers = {}; * FileTransfer uploads a file to a remote server. * @constructor */ -var FileTransfer = function() { +var FileTransfer = function () { this._id = ++idCounter; this.onprogress = null; // optional callback }; @@ -92,7 +91,7 @@ var FileTransfer = function() { * @param options {FileUploadOptions} Optional parameters such as file name and mimetype * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false */ -FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options) { +FileTransfer.prototype.upload = function (filePath, server, successCallback, errorCallback, options) { // check for arguments argscheck.checkArgs('ssFFO*', 'FileTransfer.upload', arguments); @@ -107,14 +106,14 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro options = options || {}; - var fileKey = options.fileKey || "file"; - var fileName = options.fileName || "image.jpg"; - var mimeType = options.mimeType || "image/jpeg"; + var fileKey = options.fileKey || 'file'; + var fileName = options.fileName || 'image.jpg'; + var mimeType = options.mimeType || 'image/jpeg'; var params = options.params || {}; var withCredentials = options.withCredentials || false; // var chunkedMode = !!options.chunkedMode; // Not supported var headers = options.headers || {}; - var httpMethod = options.httpMethod && options.httpMethod.toUpperCase() === "PUT" ? "PUT" : "POST"; + var httpMethod = options.httpMethod && options.httpMethod.toUpperCase() === 'PUT' ? 'PUT' : 'POST'; var basicAuthHeader = getBasicAuthHeader(server); if (basicAuthHeader) { @@ -123,93 +122,101 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro } var that = this; - var xhr = transfers[this._id] = new XMLHttpRequest(); + var xhr = (transfers[this._id] = new XMLHttpRequest()); xhr.withCredentials = withCredentials; - var fail = errorCallback && function(code, status, response) { - if (transfers[this._id]) { - delete transfers[this._id]; - } - var error = new FileTransferError(code, filePath, server, status, response); - if (errorCallback) { - errorCallback(error); - } - }; - - window.resolveLocalFileSystemURL(filePath, function(entry) { - entry.file(function(file) { - var reader = new FileReader(); - reader.onloadend = function() { - var blob = new Blob([this.result], {type: mimeType}); - - // Prepare form data to send to server - var fd = new FormData(); - fd.append(fileKey, blob, fileName); - for (var prop in params) { - if (params.hasOwnProperty(prop)) { - fd.append(prop, params[prop]); - } - } + var fail = + errorCallback && + function (code, status, response) { + if (transfers[this._id]) { + delete transfers[this._id]; + } + var error = new FileTransferError(code, filePath, server, status, response); + if (errorCallback) { + errorCallback(error); + } + }; - xhr.open(httpMethod, server); + window.resolveLocalFileSystemURL( + filePath, + function (entry) { + entry.file( + function (file) { + var reader = new FileReader(); + reader.onloadend = function () { + var blob = new Blob([this.result], { type: mimeType }); + + // Prepare form data to send to server + var fd = new FormData(); + fd.append(fileKey, blob, fileName); + for (var prop in params) { + if (Object.prototype.hasOwnProperty.call(params, prop)) { + fd.append(prop, params[prop]); + } + } - // Fill XHR headers - for (var header in headers) { - if (headers.hasOwnProperty(header)) { - xhr.setRequestHeader(header, headers[header]); - } - } + xhr.open(httpMethod, server); - xhr.onload = function() { - // 2xx codes are valid - if (this.status >= 200 && - this.status < 300) { - var result = new FileUploadResult(); // jshint ignore:line - result.bytesSent = blob.size; - result.responseCode = this.status; - result.response = this.response; - delete transfers[that._id]; - successCallback(result); - } else if (this.status === 404) { - fail(FileTransferError.INVALID_URL_ERR, this.status, this.response); - } else { - fail(FileTransferError.CONNECTION_ERR, this.status, this.response); - } - }; + // Fill XHR headers + for (var header in headers) { + if (Object.prototype.hasOwnProperty.call(headers, header)) { + xhr.setRequestHeader(header, headers[header]); + } + } + + xhr.onload = function () { + // 2xx codes are valid + if (this.status >= 200 && this.status < 300) { + var result = new FileUploadResult(); + result.bytesSent = blob.size; + result.responseCode = this.status; + result.response = this.response; + delete transfers[that._id]; + successCallback(result); + } else if (this.status === 404) { + fail(FileTransferError.INVALID_URL_ERR, this.status, this.response); + } else { + fail(FileTransferError.CONNECTION_ERR, this.status, this.response); + } + }; - xhr.ontimeout = function() { - fail(FileTransferError.CONNECTION_ERR, this.status, this.response); - }; + xhr.ontimeout = function () { + fail(FileTransferError.CONNECTION_ERR, this.status, this.response); + }; - xhr.onerror = function() { - fail(FileTransferError.CONNECTION_ERR, this.status, this.response); - }; + xhr.onerror = function () { + fail(FileTransferError.CONNECTION_ERR, this.status, this.response); + }; - xhr.onabort = function () { - fail(FileTransferError.ABORT_ERR, this.status, this.response); - }; + xhr.onabort = function () { + fail(FileTransferError.ABORT_ERR, this.status, this.response); + }; - xhr.upload.onprogress = function (e) { - if (that.onprogress) { - that.onprogress(e); - } - }; + xhr.upload.onprogress = function (e) { + if (that.onprogress) { + that.onprogress(e); + } + }; - xhr.send(fd); - // Special case when transfer already aborted, but XHR isn't sent. - // In this case XHR won't fire an abort event, so we need to check if transfers record - // isn't deleted by filetransfer.abort and if so, call XHR's abort method again - if (!transfers[that._id]) { - xhr.abort(); + xhr.send(fd); + // Special case when transfer already aborted, but XHR isn't sent. + // In this case XHR won't fire an abort event, so we need to check if transfers record + // isn't deleted by filetransfer.abort and if so, call XHR's abort method again + if (!transfers[that._id]) { + xhr.abort(); + } + }; + reader.readAsArrayBuffer(file); + }, + function () { + fail(FileTransferError.FILE_NOT_FOUND_ERR); } - }; - reader.readAsArrayBuffer(file); - }, function() { + ); + }, + function () { fail(FileTransferError.FILE_NOT_FOUND_ERR); - }); - }, function() { - fail(FileTransferError.FILE_NOT_FOUND_ERR); - }); + } + ); }; /** @@ -221,7 +228,7 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false * @param options {FileDownloadOptions} Optional parameters such as headers */ -FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts, options) { +FileTransfer.prototype.download = function (source, target, successCallback, errorCallback, trustAllHosts, options) { argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments); // Check if target URL doesn't contain spaces. If contains, it should be escaped first @@ -234,7 +241,7 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro } options = options || {}; - + var headers = options.headers || {}; var withCredentials = options.withCredentials || false; @@ -245,29 +252,30 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro } var that = this; - var xhr = transfers[this._id] = new XMLHttpRequest(); + var xhr = (transfers[this._id] = new XMLHttpRequest()); xhr.withCredentials = withCredentials; - var fail = errorCallback && function(code, status, response) { - if (transfers[that._id]) { - delete transfers[that._id]; - } - // In XHR GET reqests we're setting response type to Blob - // but in case of error we need to raise event with plain text response - if (response instanceof Blob) { - var reader = new FileReader(); - reader.readAsText(response); - reader.onloadend = function(e) { - var error = new FileTransferError(code, source, target, status, e.target.result); + var fail = + errorCallback && + function (code, status, response) { + if (transfers[that._id]) { + delete transfers[that._id]; + } + // In XHR GET reqests we're setting response type to Blob + // but in case of error we need to raise event with plain text response + if (response instanceof Blob) { + var reader = new FileReader(); + reader.readAsText(response); + reader.onloadend = function (e) { + var error = new FileTransferError(code, source, target, status, e.target.result); + errorCallback(error); + }; + } else { + var error = new FileTransferError(code, source, target, status, response); errorCallback(error); - }; - } else { - var error = new FileTransferError(code, source, target, status, response); - errorCallback(error); - } - }; + } + }; xhr.onload = function (e) { - var fileNotFound = function () { fail(FileTransferError.FILE_NOT_FOUND_ERR); }; @@ -275,27 +283,36 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro var req = e.target; // req.status === 0 is special case for local files with file:// URI scheme if ((req.status === 200 || req.status === 0) && req.response) { - window.resolveLocalFileSystemURL(getParentPath(target), function (dir) { - dir.getFile(getFileName(target), {create: true}, function writeFile(entry) { - entry.createWriter(function (fileWriter) { - fileWriter.onwriteend = function (evt) { - if (!evt.target.error) { - entry.filesystemName = entry.filesystem.name; - delete transfers[that._id]; - if (successCallback) { - successCallback(entry); - } - } else { - fail(FileTransferError.FILE_NOT_FOUND_ERR); - } - }; - fileWriter.onerror = function () { - fail(FileTransferError.FILE_NOT_FOUND_ERR); - }; - fileWriter.write(req.response); - }, fileNotFound); - }, fileNotFound); - }, fileNotFound); + window.resolveLocalFileSystemURL( + getParentPath(target), + function (dir) { + dir.getFile( + getFileName(target), + { create: true }, + function writeFile (entry) { + entry.createWriter(function (fileWriter) { + fileWriter.onwriteend = function (evt) { + if (!evt.target.error) { + entry.filesystemName = entry.filesystem.name; + delete transfers[that._id]; + if (successCallback) { + successCallback(entry); + } + } else { + fail(FileTransferError.FILE_NOT_FOUND_ERR); + } + }; + fileWriter.onerror = function () { + fail(FileTransferError.FILE_NOT_FOUND_ERR); + }; + fileWriter.write(req.response); + }, fileNotFound); + }, + fileNotFound + ); + }, + fileNotFound + ); } else if (req.status === 404) { fail(FileTransferError.INVALID_URL_ERR, req.status, req.response); } else { @@ -317,15 +334,15 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro fail(FileTransferError.ABORT_ERR, this.status, this.response); }; - xhr.open("GET", source, true); + xhr.open('GET', source, true); for (var header in headers) { - if (headers.hasOwnProperty(header)) { + if (Object.prototype.hasOwnProperty.call(headers, header)) { xhr.setRequestHeader(header, headers[header]); } } - xhr.responseType = "blob"; + xhr.responseType = 'blob'; xhr.send(); }; @@ -334,7 +351,7 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro * Aborts the ongoing file transfer on this object. The original error * callback for the file transfer will be called if necessary. */ -FileTransfer.prototype.abort = function() { +FileTransfer.prototype.abort = function () { if (this instanceof FileTransfer) { if (transfers[this._id]) { transfers[this._id].abort();