/* * * 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. * */ // 9 14 18 19 20 var _it = it; it = function (text, funk) { if (text.indexOf("filetransfer.spec.7") == 0) { return _it(text, funk); } else { console.log("Skipping Test : " + text); } } describe('FileTransfer', function() { // https://github.com/apache/cordova-labs/tree/cordova-filetransfer var server = "http://cordova-filetransfer.jitsu.com"; var server_with_credentials = "http://cordova_user:cordova_password@cordova-filetransfer.jitsu.com"; // deletes and re-creates the specified content var writeFile = function(fileName, fileContent, success, error) { deleteFile(fileName, function() { root.getFile(fileName, {create: true}, function(fileEntry) { fileEntry.createWriter(function (writer) { writer.onwrite = function(evt) { success(fileEntry); }; writer.onabort = function(evt) { error(evt); }; writer.error = function(evt) { error(evt); }; writer.write(fileContent + "\n"); }, error); }, error); }); }; var readFileEntry = function(entry, success, error) { entry.file(function(file) { var reader = new FileReader(); reader.onerror = error; reader.onload = function(e) { success(reader.result); }; reader.readAsText(file); }, error); }; var getMalformedUrl = function() { // iOS doesn't care about protocol, space in hostname causes error return "httpssss://exa mple.com"; }; // deletes file, if it exists, then invokes callback var deleteFile = function(fileName, callback) { callback = callback || function() {}; var spy = jasmine.createSpy().andCallFake(callback); root.getFile(fileName, null, // remove file system entry function(entry) { entry.remove(spy, spy); }, // doesn't exist spy); waitsFor(function() { return spy.wasCalled; }, Tests.TEST_TIMEOUT); }; it("filetransfer.spec.1 should exist and be constructable", function() { var ft = new FileTransfer(); expect(ft).toBeDefined(); }); it("filetransfer.spec.2 should contain proper functions", function() { var ft = new FileTransfer(); expect(typeof ft.upload).toBe('function'); expect(typeof ft.download).toBe('function'); }); describe('FileTransferError', function() { it("filetransfer.spec.3 FileTransferError constants should be defined", function() { expect(FileTransferError.FILE_NOT_FOUND_ERR).toBe(1); expect(FileTransferError.INVALID_URL_ERR).toBe(2); expect(FileTransferError.CONNECTION_ERR).toBe(3); }); }); describe('download method', function() { // NOTE: if download tests are failing, check the white list // // // // it("filetransfer.spec.4 should be able to download a file using http", function() { var fail = createDoNotCallSpy('downloadFail'); var remoteFile = server + "/robots.txt" var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1); var lastProgressEvent = null; var downloadWin = jasmine.createSpy().andCallFake(function(entry) { expect(entry.name).toBe(localFileName); console.log("lastProgressEvent = " + JSON.stringify(lastProgressEvent)); expect(lastProgressEvent.loaded).toBeGreaterThan(1); }); this.after(function() { deleteFile(localFileName); }); runs(function() { var ft = new FileTransfer(); ft.onprogress = function(e) { lastProgressEvent = e; }; ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, fail); }); waitsForAny(downloadWin, fail); }); it("filetransfer.spec.6 should get http status on basic auth failure", function() { var downloadWin = createDoNotCallSpy('downloadWin').andCallFake(function (res) { alert("it happened"); }); var remoteFile = server + "/download_basic_auth"; var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1); var downloadFail = jasmine.createSpy().andCallFake(function(error) { expect(error.http_status).toBe(401); expect(error.http_status).not.toBe(404, "Ensure " + remoteFile + " is in the white list"); }); this.after(function() { deleteFile(localFileName); }); runs(function() { var ft = new FileTransfer(); ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail); }); waitsForAny(downloadWin, downloadFail); }); it("filetransfer.spec.5 should be able to download a file using http basic auth", function () { var fail = createDoNotCallSpy('downloadFail'); var remoteFile = server_with_credentials + "/download_basic_auth" var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/') + 1); var lastProgressEvent = null; var downloadWin = jasmine.createSpy().andCallFake(function (entry) { expect(entry.name).toBe(localFileName); expect(lastProgressEvent.loaded).toBeGreaterThan(1); }); this.after(function () { deleteFile(localFileName); }); runs(function () { var ft = new FileTransfer(); ft.onprogress = function (e) { lastProgressEvent = e; }; ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, fail); }); waitsForAny(downloadWin, fail); }); it("filetransfer.spec.7 should be able to download a file using file:// (when hosted from file://)", function() { var fail = createDoNotCallSpy('downloadFail').andCallFake(function(err) { alert("err :: " + JSON.stringify(err)); }); var remoteFile = window.location.href.replace(/\?.*/, '').replace(/ /g, '%20').replace("x-wmapp0:","file://"); var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/') + 1); console.log("localFileName = " + localFileName); console.log("remoteFile = " + remoteFile); var lastProgressEvent = null; //if (!/^file/.exec(remoteFile)) { // expect(remoteFile).toMatch(/^file:/); // return; //} var downloadWin = jasmine.createSpy().andCallFake(function(entry) { expect(entry.name).toBe(localFileName); expect(lastProgressEvent.loaded).toBeGreaterThan(1); }); this.after(function() { deleteFile(localFileName); }); var ft = new FileTransfer(); ft.onprogress = function(e) { lastProgressEvent = e; console.log("onprogress :: " + JSON.stringify(e)); }; console.log("calling download : " + remoteFile + ", " + (root.fullPath + "/" + localFileName)); ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, fail); waitsForAny(downloadWin, fail); }); it("filetransfer.spec.8 should be able to download a file using https", function() { var remoteFile = "https://www.apache.org/licenses/"; var localFileName = 'httpstest.html'; var downloadFail = createDoNotCallSpy('downloadFail', 'Ensure ' + remoteFile + ' is in the white-list'); var fileFail = createDoNotCallSpy('fileFail'); var downloadWin = function(entry) { readFileEntry(entry, fileWin, fileFail); }; var fileWin = jasmine.createSpy().andCallFake(function(content) { expect(content).toMatch(/The Apache Software Foundation/); }); this.after(function() { deleteFile(localFileName); }); runs(function() { var ft = new FileTransfer(); ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail); }); waitsForAny(fileWin, downloadFail, fileFail); }); it("filetransfer.spec.9 should not leave partial file due to abort", function() { var downloadWin = createDoNotCallSpy('downloadWin'); var remoteFile = 'http://cordova.apache.org/downloads/logos_2.zip'; var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1); var startTime = +new Date(); var downloadFail = jasmine.createSpy().andCallFake(function (e) { console.log("downloadFail called : " + JSON.stringify(e)); expect(e.code).toBe(FileTransferError.ABORT_ERR); var didNotExistSpy = jasmine.createSpy(); var existedSpy = createDoNotCallSpy('file existed after abort'); root.getFile(localFileName, null, existedSpy, didNotExistSpy); waitsForAny(didNotExistSpy, existedSpy); }); runs(function() { var ft = new FileTransfer(); ft.onprogress = function(e) { if (e.loaded > 0) { ft.abort(); } }; ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail); }); waitsForAny(downloadWin, downloadFail); }); it("filetransfer.spec.10 should be stopped by abort() right away", function() { var downloadWin = createDoNotCallSpy('downloadWin'); var remoteFile = 'http://cordova.apache.org/downloads/BlueZedEx.mp3'; var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1); var startTime = +new Date(); var downloadFail = jasmine.createSpy().andCallFake(function(e) { expect(e.code).toBe(FileTransferError.ABORT_ERR); expect(new Date() - startTime).toBeLessThan(3000); }); this.after(function() { deleteFile(localFileName); }); runs(function() { var ft = new FileTransfer(); ft.abort(); // should be a no-op. ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail); ft.abort(); ft.abort(); // should be a no-op. }); waitsForAny(downloadWin, downloadFail); }); it("filetransfer.spec.11 should call the error callback on abort()", function() { var downloadWin = createDoNotCallSpy('downloadWin'); var downloadFail = jasmine.createSpy().andCallFake(function(e) { console.log("Abort called") }); var remoteFile = 'http://cordova.apache.org/downloads/BlueZedEx.mp3'; var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1); var startTime = +new Date(); this.after(function() { deleteFile(localFileName); }); runs(function() { var ft = new FileTransfer(); ft.abort(); // should be a no-op. ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail); ft.abort(); ft.abort(); // should be a no-op. }); waitsForAny(downloadFail); }); it("filetransfer.spec.12 should get http status on failure", function() { var downloadWin = createDoNotCallSpy('downloadWin'); var remoteFile = server + "/404"; var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1); var downloadFail = jasmine.createSpy().andCallFake(function(error) { expect(error.http_status).toBe(404); expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list"); }); this.after(function() { deleteFile(localFileName); }); runs(function() { var ft = new FileTransfer(); ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail); }); waitsForAny(downloadWin, downloadFail); }); it("filetransfer.spec.13 should get response body on failure", function() { var downloadWin = createDoNotCallSpy('downloadWin'); var remoteFile = server + "/404"; var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1); var downloadFail = jasmine.createSpy().andCallFake(function(error) { expect(error.body).toBeDefined(); expect(error.body).toMatch('You requested a 404'); }); this.after(function() { deleteFile(localFileName); }); runs(function() { var ft = new FileTransfer(); ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail); }); waitsForAny(downloadWin, downloadFail); }); it("filetransfer.spec.14 should handle malformed urls", function() { var downloadWin = createDoNotCallSpy('downloadWin'); var remoteFile = getMalformedUrl(); var localFileName = "download_malformed_url.txt"; var downloadFail = jasmine.createSpy().andCallFake(function(error) { // Note: Android needs the bad protocol to be added to the access list // won't match because ^https?:// is prepended to the regex // The bad protocol must begin with http to avoid automatic prefix expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list"); expect(error.code).toBe(FileTransferError.INVALID_URL_ERR); }); this.after(function() { deleteFile(localFileName); }); runs(function() { var ft = new FileTransfer(); ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail); }); waitsForAny(downloadWin, downloadFail); }); it("filetransfer.spec.15 should handle unknown host", function() { var downloadWin = createDoNotCallSpy('downloadWin'); var remoteFile = "http://foobar.apache.org/index.html"; var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1); var downloadFail = jasmine.createSpy().andCallFake(function(error) { expect(error.code).toBe(FileTransferError.CONNECTION_ERR); }); runs(function() { var ft = new FileTransfer(); ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail); }); waitsForAny(downloadWin, downloadFail); }); it("filetransfer.spec.16 should handle bad file path", function() { var downloadWin = createDoNotCallSpy('downloadWin'); var remoteFile = server; var badFilePath = "c:\\54321"; var downloadFail = jasmine.createSpy(); runs(function() { var ft = new FileTransfer(); ft.download(remoteFile, badFilePath, downloadWin, downloadFail); }); waitsForAny(downloadWin, downloadFail); }); it("filetransfer.spec.17 progress should work with gzip encoding", function() { var downloadFail = createDoNotCallSpy('downloadFail'); var remoteFile = "http://www.apache.org/"; var localFileName = "index.html"; var lastProgressEvent = null; var downloadWin = jasmine.createSpy().andCallFake(function(entry) { expect(entry.name).toBe(localFileName); expect(lastProgressEvent.loaded).toBeGreaterThan(1, 'loaded'); expect(lastProgressEvent.total).not.toBeLessThan(lastProgressEvent.loaded); expect(lastProgressEvent.lengthComputable).toBe(true, 'lengthComputable'); }); this.after(function() { deleteFile(localFileName); }); runs(function() { var ft = new FileTransfer(); ft.onprogress = function(e) { lastProgressEvent = e; }; ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail); }); waitsForAny(downloadWin, downloadFail); }); }); describe('upload method', function() { it("filetransfer.spec.18 should be able to upload a file", function() { var remoteFile = server + "/upload"; var localFileName = "upload.txt"; var fileContents = 'This file should upload'; var fileFail = createDoNotCallSpy('fileFail'); var uploadFail = createDoNotCallSpy('uploadFail', "Ensure " + remoteFile + " is in the white list"); var lastProgressEvent = null; var uploadWin = jasmine.createSpy().andCallFake(function (uploadResult) { console.log("uploadResult : " + JSON.stringify(uploadResult)); expect(uploadResult.bytesSent).toBeGreaterThan(0); expect(uploadResult.responseCode).toBe(200); expect(uploadResult.response).toMatch(/fields:\s*{\s*value1.*/); }); var fileWin = function(fileEntry) { ft = new FileTransfer(); var options = new FileUploadOptions(); options.fileKey = "file"; options.fileName = localFileName; options.mimeType = "text/plain"; var params = new Object(); params.value1 = "test"; params.value2 = "param"; options.params = params; ft.onprogress = function (e) { expect(e.lengthComputable).toBe(true); expect(e.total).toBeGreaterThan(0); expect(e.loaded).toBeGreaterThan(0); lastProgressEvent = e; }; // removing options cause Android to timeout ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, options); }; this.after(function() { deleteFile(localFileName); }); runs(function() { writeFile(localFileName, fileContents, fileWin, fileFail); }); waitsForAny(uploadWin, uploadFail, fileFail); runs(function() { expect(lastProgressEvent).not.toBeNull('expected progress events'); }); }); it("filetransfer.spec.19 should be able to upload a file with http basic auth", function() { var remoteFile = server_with_credentials + "/upload_basic_auth"; var localFileName = "upload.txt"; var fileContents = 'This file should upload'; var fileFail = createDoNotCallSpy('fileFail'); var uploadFail = createDoNotCallSpy('uploadFail', "Ensure " + remoteFile + " is in the white list"); var lastProgressEvent = null; var uploadWin = jasmine.createSpy().andCallFake(function(uploadResult) { expect(uploadResult.bytesSent).toBeGreaterThan(0); expect(uploadResult.responseCode).toBe(200); expect(uploadResult.response).toMatch(/fields:\s*{\s*value1.*/); }); var fileWin = function(fileEntry) { ft = new FileTransfer(); var options = new FileUploadOptions(); options.fileKey = "file"; options.fileName = localFileName; options.mimeType = "text/plain"; var params = new Object(); params.value1 = "test"; params.value2 = "param"; options.params = params; ft.onprogress = function(e) { expect(e.lengthComputable).toBe(true); expect(e.total).toBeGreaterThan(0); expect(e.loaded).toBeGreaterThan(0); lastProgressEvent = e; }; // removing options cause Android to timeout ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, options); }; this.after(function() { deleteFile(localFileName); }); runs(function() { writeFile(localFileName, fileContents, fileWin, fileFail); }); waitsForAny(uploadWin, uploadFail, fileFail); runs(function() { expect(lastProgressEvent).not.toBeNull('expected progress events'); }); }); it("filetransfer.spec.6 should get http status on basic auth failure", function() { var fileFail = createDoNotCallSpy('fileFail'); var uploadWin = createDoNotCallSpy('uploadWin'); var remoteFile = server + "/upload_basic_auth"; var localFileName = "upload_expect_fail.txt"; var uploadFail = jasmine.createSpy().andCallFake(function(error) { expect(error.http_status).toBe(401); expect(error.http_status).not.toBe(404, "Ensure " + remoteFile + " is in the white list"); }); var fileWin = function(fileEntry) { var ft = new FileTransfer(); var options = new FileUploadOptions(); options.fileKey="file"; options.fileName=fileEntry.name; options.mimeType="text/plain"; ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, options); }; this.after(function() { deleteFile(localFileName); }); runs(function() { writeFile(localFileName, "this file should fail to upload", fileWin, fileFail); }); waitsForAny(uploadWin, uploadFail, fileFail); }); it("filetransfer.spec.21 should be stopped by abort() right away.", function() { var remoteFile = server + "/upload"; var localFileName = "upload.txt"; var fileFail = createDoNotCallSpy('fileFail'); var uploadWin = createDoNotCallSpy('uploadWin', 'Should have been aborted'); var startTime; var uploadFail = jasmine.createSpy().andCallFake(function(e) { expect(e.code).toBe(FileTransferError.ABORT_ERR); expect(new Date() - startTime).toBeLessThan(300); }); var fileWin = function(fileEntry) { ft = new FileTransfer(); var options = new FileUploadOptions(); options.fileKey = "file"; options.fileName = localFileName; options.mimeType = "text/plain"; startTime = +new Date(); // removing options cause Android to timeout ft.abort(); // should be a no-op. ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, options); ft.abort(); ft.abort(); // should be a no-op. }; this.after(function() { deleteFile(localFileName); }); runs(function() { writeFile(localFileName, new Array(10000).join('aborttest!'), fileWin, fileFail); }); waitsForAny(uploadWin, uploadFail, fileFail); }); it("filetransfer.spec.12 should get http status on failure", function() { var fileFail = createDoNotCallSpy('fileFail'); var uploadWin = createDoNotCallSpy('uploadWin'); var remoteFile = server + "/403"; var localFileName = "upload_expect_fail.txt"; var uploadFail = jasmine.createSpy().andCallFake(function(error) { expect(error.http_status).toBe(403); expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list"); }); var fileWin = function(fileEntry) { var ft = new FileTransfer(); var options = new FileUploadOptions(); options.fileKey="file"; options.fileName=fileEntry.name; options.mimeType="text/plain"; ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, options); }; this.after(function() { deleteFile(localFileName); }); runs(function() { writeFile(localFileName, "this file should fail to upload", fileWin, fileFail); }); waitsForAny(uploadWin, uploadFail, fileFail); }); it("filetransfer.spec.14 should handle malformed urls", function() { var fileFail = createDoNotCallSpy('fileFail'); var uploadWin = createDoNotCallSpy('uploadWin'); var remoteFile = getMalformedUrl(); var localFileName = "malformed_url.txt"; var uploadFail = jasmine.createSpy().andCallFake(function(error) { expect(error.code).toBe(FileTransferError.INVALID_URL_ERR); expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list"); }); var fileWin = function(fileEntry) { var ft = new FileTransfer(); ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, {}); }; this.after(function() { deleteFile(localFileName); }); runs(function() { writeFile(localFileName, "Some content", fileWin, fileFail); }); waitsForAny(uploadWin, uploadFail, fileFail); }); it("filetransfer.spec.15 should handle unknown host", function() { var fileFail = createDoNotCallSpy('fileFail'); var uploadWin = createDoNotCallSpy('uploadWin'); var remoteFile = "http://foobar.apache.org/robots.txt"; var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1); var uploadFail = jasmine.createSpy().andCallFake(function(error) { expect(error.code).toBe(FileTransferError.CONNECTION_ERR); expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list"); }); var fileWin = function(fileEntry) { var ft = new FileTransfer(); ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, {}); }; this.after(function() { deleteFile(localFileName); }); runs(function() { writeFile(localFileName, "# allow all", fileWin, fileFail); }); waitsForAny(uploadWin, uploadFail, fileFail); }); it("filetransfer.spec.25 should handle missing file", function() { var uploadWin = createDoNotCallSpy('uploadWin'); var remoteFile = server + "/upload"; var localFileName = "does_not_exist.txt"; var uploadFail = jasmine.createSpy().andCallFake(function(error) { expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR); expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list"); }); runs(function() { var ft = new FileTransfer(); ft.upload(root.fullPath + "/" + localFileName, remoteFile, uploadWin, uploadFail); }); waitsForAny(uploadWin, uploadFail); }); it("filetransfer.spec.16 should handle bad file path", function() { var uploadWin = createDoNotCallSpy('uploadWin'); var remoteFile = server + "/upload"; var uploadFail = jasmine.createSpy().andCallFake(function(error) { expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list"); }); runs(function() { var ft = new FileTransfer(); ft.upload("/usr/local/bad/file/path.txt", remoteFile, uploadWin, uploadFail); }); waitsForAny(uploadWin, uploadFail); }); it("filetransfer.spec.27 should be able to set custom headers", function() { var remoteFile = "http://whatheaders.com"; var localFileName = "upload.txt"; var fileFail = function() {}; var uploadFail = createDoNotCallSpy('uploadFail', "Ensure " + remoteFile + " is in the white list and that Content-Length header is being set."); var uploadWin = jasmine.createSpy().andCallFake(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"); }); var fileWin = function(fileEntry) { ft = new FileTransfer(); var options = new FileUploadOptions(); options.fileKey = "file"; options.fileName = localFileName; options.mimeType = "text/plain"; var params = new Object(); params.value1 = "test"; params.value2 = "param"; options.params = params; options.headers = { "CustomHeader1": "CustomValue1", "CustomHeader2": ["CustomValue2", "CustomValue3"], }; // removing options cause Android to timeout ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, options); }; this.after(function() { deleteFile(localFileName); }); runs(function() { writeFile(localFileName, "this file should upload", fileWin, fileFail); }); waitsForAny(uploadWin, uploadFail); }); }); });