| /* |
| * |
| * 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 */ |
| |
| exports.defineAutoTests = function () { |
| |
| "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 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_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; |
| |
| // upload test server address |
| // NOTE: |
| // more info at https://github.com/apache/cordova-labs/tree/cordova-filetransfer |
| // 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 = ""; |
| |
| // flags |
| var isWindows = cordova.platformId === "windows8" || cordova.platformId === "windows"; |
| var isWindowsPhone81 = isWindows && WinJS.Utilities.isPhone; |
| var isWP8 = cordova.platformId === "windowsphone"; |
| var isBrowser = cordova.platformId === "browser"; |
| 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 () { |
| expect(FileTransferError).toBeDefined(); |
| }); |
| |
| it("should be constructable", function () { |
| var transferError = new FileTransferError(); |
| expect(transferError).toBeDefined(); |
| }); |
| |
| 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(); |
| expect(FileTransferError.ABORT_ERR).toBeDefined(); |
| expect(FileTransferError.NOT_MODIFIED_ERR).toBeDefined(); |
| |
| expect(FileTransferError.FILE_NOT_FOUND_ERR).toBe(1); |
| expect(FileTransferError.INVALID_URL_ERR).toBe(2); |
| expect(FileTransferError.CONNECTION_ERR).toBe(3); |
| expect(FileTransferError.ABORT_ERR).toBe(4); |
| expect(FileTransferError.NOT_MODIFIED_ERR).toBe(5); |
| }); |
| }); |
| |
| describe("FileUploadOptions", function () { |
| |
| it("should exist", function () { |
| expect(FileUploadOptions).toBeDefined(); |
| }); |
| |
| it("should be constructable", function () { |
| var transferOptions = new FileUploadOptions(); |
| expect(transferOptions).toBeDefined(); |
| }); |
| }); |
| |
| describe("FileTransfer", function () { |
| this.persistentRoot = null; |
| this.tempRoot = null; |
| |
| // named callbacks |
| var unexpectedCallbacks = { |
| httpFail: function () {}, |
| httpWin: function () {}, |
| fileSystemFail: function () {}, |
| fileSystemWin: function () {}, |
| fileOperationFail: function () {}, |
| fileOperationWin: function () {}, |
| }; |
| |
| var expectedCallbacks = { |
| unsupportedOperation: function (response) { |
| console.log("spec called unsupported functionality; response:", response); |
| }, |
| }; |
| |
| // helpers |
| var deleteFile = function (fileSystem, name, done) { |
| fileSystem.getFile(name, null, |
| function (fileEntry) { |
| fileEntry.remove( |
| function () { |
| done(); |
| }, |
| function () { |
| throw new Error("failed to delete: '" + name + "'"); |
| } |
| ); |
| }, |
| function () { |
| done(); |
| } |
| ); |
| }; |
| |
| var writeFile = function (fileSystem, name, content, success, done) { |
| var fileOperationFail = function() { |
| unexpectedCallbacks.fileOperationFail(); |
| done(); |
| }; |
| |
| fileSystem.getFile(name, { create: true }, |
| function (fileEntry) { |
| fileEntry.createWriter(function (writer) { |
| |
| writer.onwrite = function () { |
| success(fileEntry); |
| }; |
| |
| writer.onabort = function (evt) { |
| throw new Error("aborted creating test file '" + name + "': " + evt); |
| }; |
| |
| writer.error = function (evt) { |
| throw new Error("aborted creating test file '" + name + "': " + evt); |
| }; |
| |
| if (cordova.platformId === "browser") { |
| var blob = new Blob([content + "\n"], { type: "text/plain" }); |
| writer.write(blob); |
| } else { |
| writer.write(content + "\n"); |
| } |
| |
| }, fileOperationFail); |
| }, |
| function () { |
| throw new Error("could not create test file '" + name + "'"); |
| } |
| ); |
| }; |
| |
| // according to documentation, wp8 does not support onProgress: |
| // https://github.com/apache/cordova-plugin-file-transfer/blob/master/doc/index.md#supported-platforms |
| var wp8OnProgressHandler = function () {}; |
| |
| var defaultOnProgressHandler = function (event) { |
| if (event.lengthComputable) { |
| expect(event.loaded).toBeGreaterThan(1); |
| expect(event.total).toBeGreaterThan(0); |
| expect(event.total).not.toBeLessThan(event.loaded); |
| expect(event.lengthComputable).toBe(true, "lengthComputable"); |
| } else { |
| // In IE, when lengthComputable === false, event.total somehow is equal to 2^64 |
| if (isIE) { |
| expect(event.total).toBe(Math.pow(2, 64)); |
| } else { |
| // iOS returns -1, and other platforms return 0 |
| expect(event.total).toBeLessThan(1); |
| } |
| } |
| }; |
| |
| var getMalformedUrl = function () { |
| if (cordova.platformId === "android" || cordova.platformId === "amazon-fireos") { |
| // bad protocol causes a MalformedUrlException on Android |
| return "httpssss://example.com"; |
| } else { |
| // iOS doesn't care about protocol, space in hostname causes error |
| return "httpssss://exa mple.com"; |
| } |
| }; |
| |
| var setServerAddress = function (address) { |
| SERVER = address; |
| SERVER_WITH_CREDENTIALS = SERVER.replace('http://', 'http://cordova_user:cordova_password@'); |
| }; |
| |
| // NOTE: |
| // there are several beforeEach calls, one per async call; since calling done() |
| // signifies a completed async call, each async call needs its own done(), and |
| // therefore its own beforeEach |
| beforeEach(function (done) { |
| var specContext = this; |
| |
| window.requestFileSystem(LocalFileSystem.PERSISTENT, DEFAULT_FILESYSTEM_SIZE, |
| function (fileSystem) { |
| specContext.persistentRoot = fileSystem.root; |
| done(); |
| }, |
| function () { |
| throw new Error("Failed to initialize persistent file system."); |
| } |
| ); |
| }); |
| |
| beforeEach(function (done) { |
| var specContext = this; |
| |
| window.requestFileSystem(LocalFileSystem.TEMPORARY, DEFAULT_FILESYSTEM_SIZE, |
| function (fileSystem) { |
| specContext.tempRoot = fileSystem.root; |
| done(); |
| }, |
| function () { |
| throw new Error("Failed to initialize temporary file system."); |
| } |
| ); |
| }); |
| |
| // spy on all named callbacks |
| beforeEach(function() { |
| |
| // ignore the actual implementations of the unexpected callbacks |
| for (var callback in unexpectedCallbacks) { |
| if (unexpectedCallbacks.hasOwnProperty(callback)) { |
| spyOn(unexpectedCallbacks, callback); |
| } |
| } |
| |
| // but run the implementations of the expected callbacks |
| for (callback in expectedCallbacks) { // jshint ignore: line |
| if (expectedCallbacks.hasOwnProperty(callback)) { |
| spyOn(expectedCallbacks, callback).and.callThrough(); |
| } |
| } |
| }); |
| |
| // at the end, check that none of the unexpected callbacks got called, |
| // and act on the expected callbacks |
| afterEach(function() { |
| for (var callback in unexpectedCallbacks) { |
| if (unexpectedCallbacks.hasOwnProperty(callback)) { |
| expect(unexpectedCallbacks[callback]).not.toHaveBeenCalled(); |
| } |
| } |
| |
| if (expectedCallbacks.unsupportedOperation.calls.any()) { |
| pending(); |
| } |
| }); |
| |
| 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.send(null); |
| var parsedCfg = JSON.parse(xhr.responseText); |
| if (parsedCfg.serverAddress) { |
| setServerAddress(parsedCfg.serverAddress); |
| } |
| } 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.'); |
| fail(ex); |
| } |
| }); |
| |
| it("should initialise correctly", function() { |
| expect(this.persistentRoot).toBeDefined(); |
| expect(this.tempRoot).toBeDefined(); |
| }); |
| |
| it("should exist", function () { |
| expect(FileTransfer).toBeDefined(); |
| }); |
| |
| it("filetransfer.spec.1 should be constructable", function () { |
| var transfer = new FileTransfer(); |
| expect(transfer).toBeDefined(); |
| }); |
| |
| it("filetransfer.spec.2 should expose proper functions", function () { |
| |
| var transfer = new FileTransfer(); |
| |
| expect(transfer.upload).toBeDefined(); |
| expect(transfer.download).toBeDefined(); |
| |
| expect(transfer.upload).toEqual(jasmine.any(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() { |
| |
| this.transfer = new FileTransfer(); |
| |
| // assign onprogress handler |
| this.transfer.onprogress = isWP8 ? wp8OnProgressHandler : defaultOnProgressHandler; |
| |
| // spy on the onprogress handler, but still call through to it |
| spyOn(this.transfer, "onprogress").and.callThrough(); |
| |
| this.root = this.persistentRoot; |
| this.fileName = "testFile.txt"; |
| this.localFilePath = this.root.toURL() + this.fileName; |
| }); |
| |
| // NOTE: |
| // if download tests are failing, check the |
| // URL white list for the following URLs: |
| // - 'httpssss://example.com' |
| // - 'apache.org', with subdomains="true" |
| // - 'cordova-filetransfer.jitsu.com' |
| describe("download", function () { |
| |
| // helpers |
| var verifyDownload = function (fileEntry, specContext) { |
| expect(fileEntry.name).toBe(specContext.fileName); |
| }; |
| |
| // delete the downloaded file |
| afterEach(function (done) { |
| deleteFile(this.root, this.fileName, 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); |
| |
| // verify the FileEntry representing this file |
| entry.file(fileWin, fileSystemFail); |
| }; |
| |
| specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); |
| }, DOWNLOAD_TIMEOUT * 10); // to give Heroku server some time to wake up |
| |
| 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 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); |
| |
| // verify the FileEntry representing this file |
| entry.file(fileWin, fileSystemFail); |
| }; |
| |
| specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); |
| }, DOWNLOAD_TIMEOUT); |
| |
| 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; |
| |
| var downloadWin = function (entry) { |
| verifyDownload(entry, specContext); |
| done(); |
| }; |
| |
| var downloadFail = function() { |
| unexpectedCallbacks.httpFail(); |
| 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(); |
| }; |
| |
| this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail, null, |
| { |
| headers: { |
| "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) && cordova.platformId !== "blackberry10") { |
| if (cordova.platformId === "windowsphone") { |
| expect(fileURL).toMatch(/^x-wmapp0:/); |
| } |
| 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); |
| |
| it("filetransfer.spec.8 should download a file using https://", function (done) { |
| var fileURL = "https://www.apache.org/licenses/"; |
| var specContext = this; |
| |
| var downloadFail = function() { |
| unexpectedCallbacks.httpFail(); |
| done(); |
| }; |
| |
| var fileOperationFail = function() { |
| unexpectedCallbacks.fileOperationFail(); |
| done(); |
| }; |
| |
| var fileSystemFail = function() { |
| unexpectedCallbacks.fileSystemFail(); |
| done(); |
| }; |
| |
| var fileWin = function (file) { |
| |
| var reader = new FileReader(); |
| |
| reader.onerror = fileOperationFail; |
| reader.onload = function () { |
| expect(reader.result).toMatch(/The Apache Software Foundation/); |
| done(); |
| }; |
| |
| reader.readAsText(file); |
| }; |
| |
| var downloadWin = function (entry) { |
| verifyDownload(entry, specContext); |
| entry.file(fileWin, fileSystemFail); |
| }; |
| |
| specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); |
| }, isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT); |
| |
| 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 downloadWin = function () { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| 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; |
| |
| var fileSystemWin = function() { |
| unexpectedCallbacks.fileSystemWin(); |
| done(); |
| }; |
| |
| var downloadWin = function() { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| var downloadFail = function (error) { |
| |
| 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(); |
| |
| // check that there is no file |
| specContext.root.getFile(specContext.fileName, null, fileSystemWin, done); |
| }; |
| |
| // abort at the first onprogress event |
| specContext.transfer.onprogress = function (event) { |
| if (event.loaded > 0) { |
| specContext.transfer.abort(); |
| } |
| }; |
| |
| spyOn(specContext.transfer, "onprogress").and.callThrough(); |
| |
| specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); |
| }, isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT); |
| |
| 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(specContext.transfer.abort).not.toThrow(); // should be a no-op. |
| |
| var downloadWin = function() { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| var downloadFail = function (error) { |
| |
| expect(error.code).toBe(FileTransferError.ABORT_ERR); |
| |
| // delay calling done() to wait for the bogus abort() |
| setTimeout(done, GRACE_TIME_DELTA * 2); |
| }; |
| |
| specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail); |
| setTimeout(function() { |
| specContext.transfer.abort(); |
| }, ABORT_DELAY); |
| |
| // call abort() again, after a time greater than the grace period |
| setTimeout(function () { |
| expect(specContext.transfer.abort).not.toThrow(); |
| }, GRACE_TIME_DELTA); |
| }, DOWNLOAD_TIMEOUT); |
| |
| it("filetransfer.spec.12 should get http status on failure", function (done) { |
| var fileURL = SERVER + "/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); |
| |
| // wp8 does not make difference between 404 and unknown host |
| if (isWP8) { |
| expect(error.code).toBe(FileTransferError.CONNECTION_ERR); |
| } else { |
| expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR); |
| } |
| |
| done(); |
| }; |
| |
| var downloadWin = function() { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); |
| }, DOWNLOAD_TIMEOUT); |
| |
| it("filetransfer.spec.13 should get http body on failure", function (done) { |
| var fileURL = SERVER + "/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"); |
| |
| done(); |
| }; |
| |
| var downloadWin = function() { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); |
| }, DOWNLOAD_TIMEOUT); |
| |
| 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 |
| // <access origin=".*"/> 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.code).toBe(FileTransferError.INVALID_URL_ERR); |
| |
| done(); |
| }; |
| |
| var downloadWin = function() { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); |
| }); |
| |
| 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 downloadWin = function() { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| // turn off the onprogress handler |
| this.transfer.onprogress = function () {}; |
| |
| this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail); |
| }, isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT); |
| |
| it("filetransfer.spec.16 should handle bad file path", function (done) { |
| var fileURL = SERVER; |
| |
| var downloadWin = function() { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| this.transfer.download(fileURL, "c:\\54321", downloadWin, done); |
| }); |
| |
| it("filetransfer.spec.17 progress should work with gzip encoding", function (done) { |
| |
| // lengthComputable false on bb10 when downloading gzip |
| if (cordova.platformId === "blackberry10") { |
| pending(); |
| return; |
| } |
| |
| var fileURL = "http://www.apache.org/"; |
| var specContext = this; |
| |
| var downloadWin = function (entry) { |
| verifyDownload(entry, specContext); |
| done(); |
| }; |
| |
| var downloadFail = function () { |
| unexpectedCallbacks.httpFail(); |
| done(); |
| }; |
| |
| 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; |
| } |
| |
| var fileURL = SERVER + "/robots.txt"; |
| |
| var downloadWin = function (entry) { |
| |
| expect(entry.toNativeURL).toBeDefined(); |
| expect(entry.toNativeURL).toEqual(jasmine.any(Function)); |
| |
| var nativeURL = entry.toNativeURL(); |
| |
| expect(nativeURL).toBeTruthy(); |
| expect(nativeURL).toEqual(jasmine.any(String)); |
| |
| if (isWindows) { |
| expect(nativeURL.substring(0, 14)).toBe("ms-appdata:///"); |
| } else if (isWP8) { |
| expect(nativeURL.substring(0, 1)).toBe("/"); |
| } else { |
| expect(nativeURL.substring(0, 7)).toBe("file://"); |
| } |
| |
| done(); |
| }; |
| |
| var downloadFail = function() { |
| unexpectedCallbacks.httpFail(); |
| done(); |
| }; |
| |
| 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"; |
| var specContext = this; |
| |
| var unsupported = function (response) { |
| expectedCallbacks.unsupportedOperation(response); |
| done(); |
| }; |
| |
| var downloadWin = function (entry) { |
| verifyDownload(entry, specContext); |
| done(); |
| }; |
| |
| var internalFilePath; |
| if (specContext.root.toInternalURL) { |
| internalFilePath = specContext.root.toInternalURL() + specContext.fileName; |
| } else { |
| internalFilePath = specContext.localFilePath; |
| } |
| |
| var downloadFail = function() { |
| unexpectedCallbacks.httpFail(); |
| done(); |
| }; |
| |
| // 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.download(fileURL, localPath, downloadWin, downloadFail); |
| }, unsupported, "File", "_getLocalFilesystemPath", [internalFilePath]); |
| }); |
| |
| it("filetransfer.spec.33 should properly handle 304", function (done) { |
| if (isWP8) { |
| pending(); |
| return; |
| } |
| |
| 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(); |
| }; |
| |
| 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) { |
| if (isWP8) { |
| pending(); |
| return; |
| } |
| |
| var specContext = this; |
| |
| var fileOperationFail = function() { |
| unexpectedCallbacks.fileOperationFail(); |
| done(); |
| }; |
| |
| var fileSystemFail = function() { |
| unexpectedCallbacks.fileSystemFail(); |
| 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); |
| |
| specContext.persistentRoot.getFile(specContext.fileName, { create: false }, |
| function (entry) { |
| var fileWin = function (file) { |
| var reader = new FileReader(); |
| |
| reader.onerror = fileOperationFail; |
| reader.onloadend = function () { |
| |
| expect(reader.result).toBeTruthy(); |
| if (reader.result !== null) { |
| expect(reader.result.length).toBeGreaterThan(0); |
| } |
| |
| done(); |
| }; |
| |
| reader.readAsBinaryString(file); |
| }; |
| |
| entry.file(fileWin, fileSystemFail); |
| }, |
| function (err) { |
| expect("Could not open test file '" + specContext.fileName + "': " + JSON.stringify(err)).not.toBeDefined(); |
| done(); |
| } |
| ); |
| }; |
| |
| 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(); |
| } |
| |
| var fileURL = SERVER + '/download_non_utf'; |
| var specContext = this; |
| |
| var fileOperationFail = function() { |
| unexpectedCallbacks.fileOperationFail(); |
| done(); |
| }; |
| |
| var fileSystemFail = function() { |
| unexpectedCallbacks.fileSystemFail(); |
| done(); |
| }; |
| |
| var httpFail = 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"); |
| } |
| |
| expect(blob.size).toBeGreaterThan(0); |
| |
| var reader = new FileReader(); |
| |
| reader.onerror = fileOperationFail; |
| reader.onloadend = function () { |
| expect(reader.result.indexOf(LATIN1_SYMBOLS)).not.toBe(-1); |
| done(); |
| }; |
| |
| reader.readAsBinaryString(blob); |
| }; |
| |
| var downloadWin = function (entry) { |
| |
| verifyDownload(entry, specContext); |
| |
| // verify the FileEntry representing this file |
| entry.file(fileWin, fileSystemFail); |
| }; |
| |
| 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; |
| |
| // helpers |
| var verifyUpload = function (uploadResult, specContext) { |
| |
| expect(uploadResult.bytesSent).toBeGreaterThan(0); |
| expect(uploadResult.responseCode).toBe(200); |
| |
| var obj = null; |
| try { |
| obj = JSON.parse(uploadResult.response); |
| expect(obj.fields).toBeDefined(); |
| 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(specContext.transfer.onprogress).toHaveBeenCalled(); |
| }; |
| |
| beforeEach(function(done) { |
| var specContext = this; |
| |
| specContext.fileName = "fileToUpload.txt"; |
| specContext.fileContents = "upload test file"; |
| |
| specContext.uploadParams = {}; |
| specContext.uploadParams.value1 = "test"; |
| specContext.uploadParams.value2 = "param"; |
| |
| specContext.uploadOptions = new FileUploadOptions(); |
| specContext.uploadOptions.fileKey = "file"; |
| specContext.uploadOptions.fileName = specContext.fileName; |
| specContext.uploadOptions.mimeType = "text/plain"; |
| specContext.uploadOptions.params = specContext.uploadParams; |
| |
| var fileWin = function (entry) { |
| specContext.localFilePath = entry.toURL(); |
| done(); |
| }; |
| |
| // create a file to upload |
| writeFile(specContext.root, specContext.fileName, specContext.fileContents, fileWin, done); |
| }); |
| |
| // delete the uploaded file |
| afterEach(function (done) { |
| 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.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); |
| 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.21 should be stopped by abort()", function (done) { |
| var fileURL = SERVER + "/upload"; |
| var specContext = this; |
| |
| var uploadFail = function (e) { |
| expect(e.code).toBe(FileTransferError.ABORT_ERR); |
| |
| // delay calling done() to wait for the bogus abort() |
| setTimeout(done, GRACE_TIME_DELTA * 2); |
| }; |
| |
| var uploadWin = function() { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| var fileWin = function () { |
| |
| expect(specContext.transfer.abort).not.toThrow(); |
| |
| // NOTE: removing uploadOptions cause Android to timeout |
| specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); |
| setTimeout(function() { |
| specContext.transfer.abort(); |
| }, ABORT_DELAY); |
| |
| setTimeout(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 && !isWindowsPhone81) || 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 uploadWin = function() { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| 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) { |
| 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"); |
| done(); |
| }; |
| |
| var uploadWin = function() { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, {}); |
| }); |
| |
| 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 uploadWin = function() { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, {}); |
| }, UPLOAD_TIMEOUT); |
| |
| 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 uploadWin = function() { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| 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"; |
| |
| var uploadFail = function (error) { |
| expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list"); |
| done(); |
| }; |
| |
| var uploadWin = function() { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| 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(); |
| |
| var responseHtml = decodeURIComponent(uploadResult.response); |
| |
| expect(responseHtml).toMatch(/CustomHeader1[\s\S]*CustomValue1/i); |
| expect(responseHtml).toMatch(/CustomHeader2[\s\S]*CustomValue2[\s\S]*CustomValue3/i, "Should allow array values"); |
| |
| 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); |
| } |
| }; |
| |
| 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() { |
| unexpectedCallbacks.httpFail(); |
| done(); |
| }; |
| |
| var internalFilePath; |
| if (specContext.root.toInternalURL) { |
| internalFilePath = specContext.root.toInternalURL() + specContext.fileName; |
| } else { |
| internalFilePath = specContext.localFilePath; |
| } |
| |
| // 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; |
| |
| 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(); |
| }; |
| |
| 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(); |
| |
| 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(); |
| }; |
| |
| // 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; |
| |
| 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(); |
| }); |
| }; |
| |
| var uploadWin = function() { |
| unexpectedCallbacks.httpWin(); |
| done(); |
| }; |
| |
| var fileWin = function () { |
| |
| expect(specContext.transfer.abort).not.toThrow(); |
| |
| // abort at the first onprogress event |
| specContext.transfer.onprogress = function (event) { |
| if (event.loaded > 0) { |
| specContext.transfer.abort(); |
| } |
| }; |
| |
| // NOTE: removing uploadOptions cause Android to timeout |
| specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions); |
| }; |
| |
| writeFile(specContext.root, specContext.fileName, new Array(100000).join("aborttest!"), fileWin, done); |
| }, UPLOAD_TIMEOUT); |
| |
| 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 fileURL = SERVER + '/upload_non_utf'; |
| var specContext = this; |
| |
| var uploadWin = function (uploadResult) { |
| |
| 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"); |
| } |
| |
| 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.38 should be able to upload a file using data: source uri", function (done) { |
| var fileURL = SERVER + "/upload"; |
| var specContext = this; |
| |
| var uploadWin = function (uploadResult) { |
| |
| verifyUpload(uploadResult,specContext); |
| |
| 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."); |
| } |
| |
| 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."); |
| } |
| |
| done(); |
| }; |
| |
| var uploadFail = function() { |
| unexpectedCallbacks.httpFail(); |
| done(); |
| }; |
| |
| // Content-Type header disables multipart |
| this.uploadOptions.headers = { |
| "Content-Type": "image/png" |
| }; |
| |
| 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); |
| |
| 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; |
| |
| var uploadFail = function() { |
| unexpectedCallbacks.httpFail(); |
| done(); |
| }; |
| |
| // 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" |
| }; |
| |
| // turn off the onprogress handler |
| this.transfer.onprogress = function () { }; |
| |
| var dataUri = DATA_URI_PREFIX; |
| |
| var uploadFail = function() { |
| unexpectedCallbacks.httpFail(); |
| done(); |
| }; |
| |
| // NOTE: removing uploadOptions cause Android to timeout |
| this.transfer.upload(dataUri, fileURL, done, uploadFail, this.uploadOptions); |
| }, UPLOAD_TIMEOUT); |
| |
| describe("chunkedMode handling", function() { |
| var testChunkedModeWin = function (uploadResult, specContext) { |
| 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["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"); |
| } |
| |
| if (multipartModeEnabled) { |
| expect(obj["content-type"].indexOf("multipart/form-data")).not.toBe(-1); |
| } else { |
| expect(obj["content-type"].indexOf("multipart/form-data")).toBe(-1); |
| } |
| } catch (e) { |
| expect(obj).not.toBeNull("returned data from server should be valid json"); |
| } |
| }; |
| |
| var testChunkedModeBase = function(chunkedMode, multipart, done) { |
| var retryCount = 0; |
| 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" |
| }; |
| } |
| |
| 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 |
| specContext.transfer.upload(specContext.localFilePath, fileURL, function (uploadResult) { |
| testChunkedModeWin(uploadResult, specContext); |
| done(); |
| }, uploadFail, specContext.uploadOptions); |
| }, RETRY_INTERVAL); |
| } |
| }; |
| |
| // turn off the onprogress handler |
| 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); |
| }; |
| |
| 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) { |
| |
| testChunkedModeBase.call(this, false, 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); |
| }); |
| }); |
| }); |
| }); |
| }; |
| |
| /******************************************************************************/ |
| /******************************************************************************/ |
| /******************************************************************************/ |
| |
| exports.defineManualTests = function (contentEl, createActionButton) { |
| |
| "use strict"; |
| |
| 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 downloadImg(source, urlFn, element, directory) { |
| var filename = source.substring(source.lastIndexOf("/") + 1); |
| filename = (directory || "") + filename; |
| |
| function download(fileSystem) { |
| var ft = new FileTransfer(); |
| console.log("Starting download"); |
| |
| var progress = document.getElementById("loadingStatus"); |
| progress.value = 0; |
| |
| ft.onprogress = function(progressEvent) { |
| if (progressEvent.lengthComputable) { |
| var currPercents = parseInt(100 * (progressEvent.loaded / progressEvent.total), 10); |
| if (currPercents > progress.value) { |
| progress.value = currPercents; |
| } |
| } else { |
| progress.value = null; |
| } |
| }; |
| |
| 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"); |
| 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); |
| }); |
| } |
| }, function () { console.log("ERROR: requestFileSystem"); }); |
| } |
| |
| /******************************************************************************/ |
| |
| var progress_tag = "<progress id=\"loadingStatus\" value=\"0\" max=\"100\" style=\"width: 100%;\"></progress>"; |
| var file_transfer_tests = "<h2>Image File Transfer Tests</h2>" + |
| "<h3>The following tests should display an image of the Apache feather in the status box</h3>" + |
| "<div id=\"cdv_image\"></div>" + |
| "<div id=\"native_image\"></div>" + |
| "<div id=\"non-existent_dir\"></div>" + |
| "<h2>Video File Transfer Tests</h2>" + |
| "<h3>The following tests should display a video in the status box. The video should play when play is pressed</h3>" + |
| "<div id=\"cdv_video\"></div>" + |
| "<div id=\"native_video\"></div>"; |
| |
| contentEl.innerHTML = "<div id=\"info\"></div>" + "<br>" + 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"); |
| }; |