| /* |
| * |
| * 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'; |
| |
| // 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 === '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; |
| |
| // 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 + "'"); |
| } |
| ); |
| }; |
| |
| 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') { |
| // 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 (Object.prototype.hasOwnProperty.call(unexpectedCallbacks, callback)) { |
| spyOn(unexpectedCallbacks, callback); |
| } |
| } |
| |
| // but run the implementations of the expected callbacks |
| for (callback in expectedCallbacks) { |
| if (Object.prototype.hasOwnProperty.call(expectedCallbacks, 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 (Object.prototype.hasOwnProperty.call(unexpectedCallbacks, 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 = 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)) { |
| 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); |
| 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); |
| |
| 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) { |
| 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 { |
| 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) { |
| 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) { |
| 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 && !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 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' |
| ); |
| }; |