cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From purplecabb...@apache.org
Subject [06/25] updated to 2.7.0rc1 including updated mobile spec tests
Date Tue, 23 Apr 2013 00:34:31 GMT
http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/199321c2/windows8/test/autotest/tests/filetransfer.tests.js
----------------------------------------------------------------------
diff --git a/windows8/test/autotest/tests/filetransfer.tests.js b/windows8/test/autotest/tests/filetransfer.tests.js
index 1058a81..1759983 100644
--- a/windows8/test/autotest/tests/filetransfer.tests.js
+++ b/windows8/test/autotest/tests/filetransfer.tests.js
@@ -1,72 +1,753 @@
+/*
+ *
+ * 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.
+ *
+*/
+
 describe('FileTransfer', function() {
-    it("should exist and be constructable", 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() {
+        if (device.platform.match(/Android/i)) {
+            // 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";
+        }
+    };
+
+    // 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("should contain proper functions", function() {
+    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("FileTransferError constants should be defined", 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() {
-        it("should be able to download a file", function() {
-            var fail = jasmine.createSpy();
-            var remoteFile = "https://github.com/ajaxorg/cloud9/blob/master/server.js";
+
+        // NOTE: if download tests are failing, check the white list
+        //
+        //   <access origin="httpssss://example.com"/>
+        //   <access origin="apache.org" subdomains="true" />
+        //   <access origin="cordova-filetransfer.jitsu.com"/>
+
+        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);
+                expect(lastProgressEvent.loaded).toBeGreaterThan(1);
             });
-            var fileWin = function(fileEntry) {
+
+            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.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.download(remoteFile, fileEntry.fullPath, downloadWin);
+                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');
+
+            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.7 should be able to download a file using file:// (when hosted from file://)", function() {
+            var fail = createDoNotCallSpy('downloadFail');
+            var remoteFile = window.location.href.replace(/\?.*/, '').replace(/ /g, '%20');
+            var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1);
+            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;
             };
-            
-            // root is defined in the html page containing these tests
+            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() {
-                root.getFile(localFileName, {create: true, exclusive: false}, fileWin, fail);
+                var ft = new FileTransfer();
+                ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail);
             });
 
-            waitsFor(function() { return downloadWin.wasCalled; }, "downloadWin", Tests.TEST_TIMEOUT);
+            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/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);
+                var didNotExistSpy = jasmine.createSpy();
+                var existedSpy = createDoNotCallSpy('file existed after abort');
+                root.getFile(localFileName, null, existedSpy, didNotExistSpy);
+                waitsForAny(didNotExistSpy, existedSpy);
+            });
 
             runs(function() {
-                expect(downloadWin).toHaveBeenCalled();
-                expect(fail).not.toHaveBeenCalled();
+                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(300);
+            });
+
+            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).toEqual('You requested a 404\n');
+            });
+
+            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
+                // <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 " + 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().andCallFake(function(error) {
+                expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR);
+            });
+
+            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("should be able to upload a file", function () {
-            var fail = jasmine.createSpy();
-            var localFileName = "server.js";
-            var localFile = root.fullPath + "\\" + localFileName;
-            var Url = "http://localhost:5000/upload";
-            var uploadWin = jasmine.createSpy().andCallFake(function (entry) {
-                expect(entry.responseCode).toBe(200);
-            });
-            var fileWin = function (fileEntry) {
-                console.log("1========");
-                var ft = new FileTransfer();
-                ft.upload(localFile, Url, uploadWin,fail,null);
+    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) {
+                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, {});
             };
 
-            // root is defined in the html page containing these tests
-            runs(function () {
-                root.getFile(localFileName, { create: false }, fileWin, fail);
+            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.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("/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");
             });
 
-            waitsFor(function () { return uploadWin.wasCalled; }, "uploadWin", Tests.TEST_TIMEOUT);
+            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);
+            };
 
-            runs(function () {
-                expect(uploadWin).toHaveBeenCalled();
-                expect(fail).not.toHaveBeenCalled();
+            this.after(function() {
+                deleteFile(localFileName);
+            });
+            runs(function() {
+                writeFile(localFileName, "this file should upload", fileWin, fileFail);
             });
+
+            waitsForAny(uploadWin, uploadFail);
         });
     });
 });

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/199321c2/windows8/test/autotest/tests/geolocation.tests.js
----------------------------------------------------------------------
diff --git a/windows8/test/autotest/tests/geolocation.tests.js b/windows8/test/autotest/tests/geolocation.tests.js
index 6f447c5..6837f44 100644
--- a/windows8/test/autotest/tests/geolocation.tests.js
+++ b/windows8/test/autotest/tests/geolocation.tests.js
@@ -1,106 +1,140 @@
+/*
+ *
+ * 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.
+ *
+*/
+
 describe('Geolocation (navigator.geolocation)', function () {
-	it("should exist", function() {
+    it("geolocation.spec.1 should exist", function() {
         expect(navigator.geolocation).toBeDefined();
-	});
-
-	it("should contain a getCurrentPosition function", function() {
-		expect(typeof navigator.geolocation.getCurrentPosition).toBeDefined();
-		expect(typeof navigator.geolocation.getCurrentPosition == 'function').toBe(true);
-	});
-
-	it("should contain a watchPosition function", function() {
-		expect(typeof navigator.geolocation.watchPosition).toBeDefined();
-		expect(typeof navigator.geolocation.watchPosition == 'function').toBe(true);
-	});
-
-	it("should contain a clearWatch function", function() {
-		expect(typeof navigator.geolocation.clearWatch).toBeDefined();
-		expect(typeof navigator.geolocation.clearWatch == 'function').toBe(true);
-	});
-
-	it("getCurrentPosition success callback should be called with a Position object", function() {
-		var win = jasmine.createSpy().andCallFake(function(a) {
-                expect(a.coords).not.toBe(null);
-                expect(a.timestamp).not.toBe(null);
-            }),
-            fail = jasmine.createSpy();
-
-        runs(function () {
-            navigator.geolocation.getCurrentPosition(win, fail, {
-                maximumAge:300000 // 5 minutes maximum age of cached position
-            });
-        });
+    });
 
-        waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+    it("geolocation.spec.2 should contain a getCurrentPosition function", function() {
+        expect(typeof navigator.geolocation.getCurrentPosition).toBeDefined();
+        expect(typeof navigator.geolocation.getCurrentPosition == 'function').toBe(true);
+    });
 
-        runs(function () {
-            expect(fail).not.toHaveBeenCalled();
-        });
-	});
-
-	it("getCurrentPosition success callback should be called with a cached Position", function() {
-	    var win = jasmine.createSpy().andCallFake(function (a) {
-	        if (a instanceof Position) {
-	            expect(a instanceof Position).toBe(true);
-            } else {
-                expect(a.toString() === '[object Position]').toBe(true);
-            }
-               
-            }),
-            fail = jasmine.createSpy();
-
-        runs(function () {
-            navigator.geolocation.getCurrentPosition(win, fail, {
-                maximumAge:300000 // 5 minutes 
-            });
-        });
+    it("geolocation.spec.3 should contain a watchPosition function", function() {
+        expect(typeof navigator.geolocation.watchPosition).toBeDefined();
+        expect(typeof navigator.geolocation.watchPosition == 'function').toBe(true);
+    });
 
-        waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+    it("geolocation.spec.4 should contain a clearWatch function", function() {
+        expect(typeof navigator.geolocation.clearWatch).toBeDefined();
+        expect(typeof navigator.geolocation.clearWatch == 'function').toBe(true);
+    });
 
-        runs(function () {
-            expect(fail).not.toHaveBeenCalled();
+    describe('getCurrentPosition method', function() {
+        describe('error callback', function() {
+            it("geolocation.spec.5 should be called if we set timeout to 0 and maximumAge to a very small number", function() {
+                console.log("Here I am");
+                var win = jasmine.createSpy(),
+                    fail = jasmine.createSpy();
+
+                runs(function () {
+                    navigator.geolocation.getCurrentPosition(win, fail, {
+                        maximumAge: 0,
+                        timeout: 0
+                    });
+                });
+
+                waitsFor(function () { return fail.wasCalled; }, "fail never called", 250); //small timeout as this should fire very fast
+
+                runs(function () {
+                    expect(win).not.toHaveBeenCalled();
+                });
+            });
         });
-	});
-
-    it("getCurrentPosition error callback should be called if we set timeout to 0 and maximumAge to a very small number", function() {
-        var win = jasmine.createSpy(),
-            fail = jasmine.createSpy();
 
-        runs(function () {
-            navigator.geolocation.getCurrentPosition(win, fail, {
-                maximumAge: 0,
-                timeout: 0
+        describe('success callback', function() {
+            it("geolocation.spec.6 should be called with a Position object", function() {
+                var win = jasmine.createSpy().andCallFake(function(p) {
+                          expect(p.coords).toBeDefined();
+                          expect(p.timestamp).toBeDefined();
+                      }),
+                      fail = jasmine.createSpy();
+
+                runs(function () {
+                    navigator.geolocation.getCurrentPosition(win, fail, {
+                        maximumAge:300000 // 5 minutes maximum age of cached position
+                    });
+                });
+
+                waitsFor(function () { return win.wasCalled; }, "win never called", 20000);
+
+                runs(function () {
+                    expect(fail).not.toHaveBeenCalled();
+                });
             });
         });
+    });
 
-        waitsFor(function () { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+    describe('watchPosition method', function() {
+        describe('error callback', function() {
+            var errorWatch = null;
 
-        runs(function () {
-            expect(win).not.toHaveBeenCalled();
+            afterEach(function() {
+                navigator.geolocation.clearWatch(errorWatch);
+            });
+            it("geolocation.spec.5 should be called if we set timeout to 0 and maximumAge to a very small number", function() {
+                var win = jasmine.createSpy(),
+                    fail = jasmine.createSpy();
+
+                runs(function () {
+                    errorWatch = navigator.geolocation.watchPosition(win, fail, {
+                        maximumAge: 0,
+                        timeout: 0
+                    });
+                });
+
+                waitsFor(function () { return fail.wasCalled; }, "fail never called", 250); // small timeout as this should fire very quickly
+
+                runs(function () {
+                    expect(win).not.toHaveBeenCalled();
+                });
+            });
         });
-    });
 
-    // TODO: Need to test error callback... how?
-        // You could close your geolocation capability and expect the error code to be 3.(already tested in Win8 Implementation) 
-	// TODO: Need to test watchPosition success callback, test that makes sure clearPosition works (how to test that a timer is getting cleared?)
-    describe("Geolocation model", function () {
-        it("should be able to define a Position object with coords and timestamp properties", function() {
-            var pos = new Position({}, new Date());
-            expect(pos).toBeDefined();
-            expect(pos.coords).toBeDefined();
-            expect(pos.timestamp).toBeDefined();
-        });
+        describe('success callback', function() {
+            var successWatch = null;
 
-        it("should be able to define a Coordinates object with latitude, longitude, accuracy, altitude, heading, speed and altitudeAccuracy properties", function() {
-            var coords = new Coordinates(1,2,3,4,5,6,7);
-            expect(coords).toBeDefined();
-            expect(coords.latitude).toBeDefined();
-            expect(coords.longitude).toBeDefined();
-            expect(coords.accuracy).toBeDefined();
-            expect(coords.altitude).toBeDefined();
-            expect(coords.heading).toBeDefined();
-            expect(coords.speed).toBeDefined();
-            expect(coords.altitudeAccuracy).toBeDefined();
+            afterEach(function() {
+                navigator.geolocation.clearWatch(successWatch);
+            });
+            it("geolocation.spec.6 should be called with a Position object", function() {
+                var win = jasmine.createSpy().andCallFake(function(p) {
+                          expect(p.coords).toBeDefined();
+                          expect(p.timestamp).toBeDefined();
+                      }),
+                      fail = jasmine.createSpy();
+
+                runs(function () {
+                    successWatch = navigator.geolocation.watchPosition(win, fail, {
+                        maximumAge:(5 * 60 * 1000) // 5 minutes maximum age of cached position
+                    });
+                });
+
+                waitsFor(function () { return win.wasCalled; }, "win never called", 20000);
+
+                runs(function () {
+                    expect(fail).not.toHaveBeenCalled();
+                });
+            });
         });
     });
 });

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/199321c2/windows8/test/autotest/tests/globalization.tests.js
----------------------------------------------------------------------
diff --git a/windows8/test/autotest/tests/globalization.tests.js b/windows8/test/autotest/tests/globalization.tests.js
new file mode 100644
index 0000000..bc43b4a
--- /dev/null
+++ b/windows8/test/autotest/tests/globalization.tests.js
@@ -0,0 +1,808 @@
+describe('Globalization (navigator.globalization)', function () {
+    it("globalization.spec.1 should exist", function() {
+        expect(navigator.globalization).toBeDefined();
+    });
+    
+    describe("getLocaleName", function() {
+        it("globalization.spec.1 should exist", function() {
+            expect(typeof navigator.globalization.getLocaleName).toBeDefined();
+            expect(typeof navigator.globalization.getLocaleName == 'function').toBe(true);
+        });
+        it("globalization.spec.3 getLocaleName success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getLocaleName(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("dateToString", function() {
+        it("globalization.spec.1 should exist", function() {
+            expect(typeof navigator.globalization.dateToString).toBeDefined();
+            expect(typeof navigator.globalization.dateToString == 'function').toBe(true);
+        });
+        it("globalization.spec.5 dateToString using default options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.6 dateToString using formatLength=short and selector=date options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win, fail, {formatLength: 'short', selector: 'date'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.7 dateToString using formatLength=full and selector=date options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win, fail, {formatLength: 'full', selector: 'date'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.8 dateToString using formatLength=medium and selector=date and time(default) options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win, fail, {formatLength: 'medium'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.9 dateToString using formatLength=long and selector=date and time(default) options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win, fail, {formatLength: 'long'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.10 dateToString using formatLength=full and selector=date and time(default) options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win, fail, {formatLength: 'full'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("stringToDate", function() {
+        it("globalization.spec.1 should exist", function() {
+            expect(typeof navigator.globalization.stringToDate).toBeDefined();
+            expect(typeof navigator.globalization.stringToDate == 'function').toBe(true);
+        });
+        it("globalization.spec.12 stringToDate using default options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.year).toBeDefined();
+                    expect(typeof a.year).toBe('number');
+                    expect(a.year >= 0 && a.year <=9999).toBe(true);
+                    expect(a.month).toBeDefined();
+                    expect(typeof a.month).toBe('number');
+                    expect(a.month >= 0 && a.month <=11).toBe(true);
+                    expect(a.day).toBeDefined();
+                    expect(typeof a.day).toBe('number');
+                    expect(a.day >= 1 && a.day <=31).toBe(true);
+                    expect(a.hour).toBeDefined();
+                    expect(typeof a.hour).toBe('number');
+                    expect(a.hour >= 0 && a.hour <=23).toBe(true);
+                    expect(a.minute).toBeDefined();
+                    expect(typeof a.minute).toBe('number');
+                    expect(a.minute >= 0 && a.minute <=59).toBe(true);
+                    expect(a.second).toBeDefined();
+                    expect(typeof a.second).toBe('number');
+                    expect(a.second >= 0 && a.second <=59).toBe(true);
+                    expect(a.millisecond).toBeDefined();
+                    expect(typeof a.millisecond).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+                
+            var win2 = function(a) {
+                navigator.globalization.stringToDate(a.value, win, fail);                
+            };
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win2, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.13 stringToDate using formatLength=short and selector=date options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.year).toBeDefined();
+                    expect(typeof a.year).toBe('number');
+                    expect(a.year >= 0 && a.year <=9999).toBe(true);
+                    expect(a.month).toBeDefined();
+                    expect(typeof a.month).toBe('number');
+                    expect(a.month >= 0 && a.month <=11).toBe(true);
+                    expect(a.day).toBeDefined();
+                    expect(typeof a.day).toBe('number');
+                    expect(a.day >= 1 && a.day <=31).toBe(true);
+                    expect(a.hour).toBeDefined();
+                    expect(typeof a.hour).toBe('number');
+                    expect(a.hour >= 0 && a.hour <=23).toBe(true);
+                    expect(a.minute).toBeDefined();
+                    expect(typeof a.minute).toBe('number');
+                    expect(a.minute >= 0 && a.minute <=59).toBe(true);
+                    expect(a.second).toBeDefined();
+                    expect(typeof a.second).toBe('number');
+                    expect(a.second >= 0 && a.second <=59).toBe(true);
+                    expect(a.millisecond).toBeDefined();
+                    expect(typeof a.millisecond).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+                
+            var win2 = function(a) {
+                navigator.globalization.stringToDate(a.value, win, fail, {formatLength: 'short', selector: 'date'});
+            };
+
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win2, fail, {formatLength: 'short', selector: 'date'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.14 stringToDate using formatLength=full and selector=date options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.year).toBeDefined();
+                    expect(typeof a.year).toBe('number');
+                    expect(a.year >= 0 && a.year <=9999).toBe(true);
+                    expect(a.month).toBeDefined();
+                    expect(typeof a.month).toBe('number');
+                    expect(a.month >= 0 && a.month <=11).toBe(true);
+                    expect(a.day).toBeDefined();
+                    expect(typeof a.day).toBe('number');
+                    expect(a.day >= 1 && a.day <=31).toBe(true);
+                    expect(a.hour).toBeDefined();
+                    expect(typeof a.hour).toBe('number');
+                    expect(a.hour >= 0 && a.hour <=23).toBe(true);
+                    expect(a.minute).toBeDefined();
+                    expect(typeof a.minute).toBe('number');
+                    expect(a.minute >= 0 && a.minute <=59).toBe(true);
+                    expect(a.second).toBeDefined();
+                    expect(typeof a.second).toBe('number');
+                    expect(a.second >= 0 && a.second <=59).toBe(true);
+                    expect(a.millisecond).toBeDefined();
+                    expect(typeof a.millisecond).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+
+            var win2 = function(a) {
+                navigator.globalization.stringToDate(a.value, win, fail, {formatLength: 'full', selector: 'date'});
+            };
+            runs(function () {
+                navigator.globalization.dateToString(new Date(), win2, fail, {formatLength: 'full', selector: 'date'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.15 stringToDate using invalid date, error callback should be called with a GlobalizationError object", function() {
+            var win = jasmine.createSpy(),
+                fail = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.code).toBeDefined();
+                    expect(typeof a.code).toBe('number');
+                    expect(a.code === GlobalizationError.PARSING_ERROR).toBe(true);
+                    expect(a.message).toBeDefined();
+                    expect(typeof a.message).toBe('string');
+                    expect(a.message !== "").toBe(true);
+                });
+
+            runs(function () {
+                navigator.globalization.stringToDate('notADate', win, fail, {selector:'foobar'});
+            });
+
+            waitsFor(function () { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe("getDatePattern", function() {
+        it("globalization.spec.1 should exist", function() {
+            expect(typeof navigator.globalization.getDatePattern).toBeDefined();
+            expect(typeof navigator.globalization.getDatePattern == 'function').toBe(true);
+        });
+        it("globalization.spec.17 getDatePattern using default options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.pattern).toBeDefined();
+                    expect(typeof a.pattern).toBe('string');
+                    expect(a.pattern.length > 0).toBe(true);
+                    expect(a.timezone).toBeDefined();
+                    expect(typeof a.timezone).toBe('string');
+                    expect(a.timezone.length > 0).toBe(true);
+                    expect(a.utc_offset).toBeDefined();
+                    expect(typeof a.utc_offset).toBe('number');
+                    expect(a.dst_offset).toBeDefined();
+                    expect(typeof a.dst_offset).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getDatePattern(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.18 getDatePattern using formatLength=medium and selector=date options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.pattern).toBeDefined();
+                    expect(typeof a.pattern).toBe('string');
+                    expect(a.pattern.length > 0).toBe(true);
+                    expect(a.timezone).toBeDefined();
+                    expect(typeof a.timezone).toBe('string');
+                    expect(a.timezone.length > 0).toBe(true);
+                    expect(a.utc_offset).toBeDefined();
+                    expect(typeof a.utc_offset).toBe('number');
+                    expect(a.dst_offset).toBeDefined();
+                    expect(typeof a.dst_offset).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getDatePattern(win, fail, {formatLength: 'medium', selector: 'date'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });    
+
+    describe("getDateNames", function() {
+        it("globalization.spec.1 should exist", function() {
+            expect(typeof navigator.globalization.getDateNames).toBeDefined();
+            expect(typeof navigator.globalization.getDateNames == 'function').toBe(true);
+        });
+        it("globalization.spec.20 getDateNames using default options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(a.value instanceof Array).toBe(true);
+                    expect(a.value.length > 0).toBe(true);
+                    expect(typeof a.value[0]).toBe('string');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getDateNames(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.21 getDateNames using type=narrow and item=days options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(a.value instanceof Array).toBe(true);
+                    expect(a.value.length > 0).toBe(true);
+                    expect(typeof a.value[0]).toBe('string');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getDateNames(win, fail, {type: 'narrow', item: 'days'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.22 getDateNames using type=narrow and item=months options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(a.value instanceof Array).toBe(true);
+                    expect(a.value.length > 0).toBe(true);
+                    expect(typeof a.value[0]).toBe('string');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getDateNames(win, fail, {type: 'narrow', item: 'months'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.23 getDateNames using type=wide and item=days options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(a.value instanceof Array).toBe(true);
+                    expect(a.value.length > 0).toBe(true);
+                    expect(typeof a.value[0]).toBe('string');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getDateNames(win, fail, {item: 'days'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.24 getDateNames using type=wide and item=months options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(a.value instanceof Array).toBe(true);
+                    expect(a.value.length > 0).toBe(true);
+                    expect(typeof a.value[0]).toBe('string');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getDateNames(win, fail, {item: 'months'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("isDayLightSavingsTime", function() {
+        it("globalization.spec.1 should exist", function() {
+            expect(typeof navigator.globalization.isDayLightSavingsTime).toBeDefined();
+            expect(typeof navigator.globalization.isDayLightSavingsTime == 'function').toBe(true);
+        });
+        it("globalization.spec.26 isDayLightSavingsTime using default options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.dst).toBeDefined();
+                    expect(typeof a.dst).toBe('boolean');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.isDayLightSavingsTime(new Date(), win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("getFirstDayOfWeek", function() {
+        it("globalization.spec.1 should exist", function() {
+            expect(typeof navigator.globalization.getFirstDayOfWeek).toBeDefined();
+            expect(typeof navigator.globalization.getFirstDayOfWeek == 'function').toBe(true);
+        });
+        it("globalization.spec.28 getFirstDayOfWeek success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getFirstDayOfWeek(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("numberToString", function() {
+        it("globalization.spec.1 should exist", function() {
+            expect(typeof navigator.globalization.numberToString).toBeDefined();
+            expect(typeof navigator.globalization.numberToString == 'function').toBe(true);
+        });
+        it("globalization.spec.30 numberToString using default options, should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.numberToString(3.25, win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.31 numberToString using type=percent options, should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.numberToString(.25, win, fail, {type: 'percent'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.32 numberToString using type=currency options, should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('string');
+                    expect(a.value.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.numberToString(5.20, win, fail, {type: 'currency'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("stringToNumber", function() {
+        it("globalization.spec.1 should exist", function() {
+            expect(typeof navigator.globalization.stringToNumber).toBeDefined();
+            expect(typeof navigator.globalization.stringToNumber == 'function').toBe(true);
+        });
+        it("globalization.spec.34 stringToNumber using default options, should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('number');
+                    expect(a.value > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            var win2 = function(a) {
+                navigator.globalization.stringToNumber(a.value, win, fail);
+            };
+            
+            runs(function () {
+                navigator.globalization.numberToString(3.25, win2, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.35 stringToNumber using type=percent options, should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.value).toBeDefined();
+                    expect(typeof a.value).toBe('number');
+                    expect(a.value > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            var win2 = function(a) {
+                navigator.globalization.stringToNumber(a.value, win, fail, {type: 'percent'});
+            };
+            
+            runs(function () {
+                navigator.globalization.numberToString(.25, win2, fail, {type: 'percent'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("getNumberPattern", function() {
+        it("globalization.spec.1 should exist", function() {
+            expect(typeof navigator.globalization.getNumberPattern).toBeDefined();
+            expect(typeof navigator.globalization.getNumberPattern == 'function').toBe(true);
+        });
+        it("globalization.spec.37 getNumberPattern using default options, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.pattern).toBeDefined();
+                    expect(typeof a.pattern).toBe('string');
+                    expect(a.pattern.length > 0).toBe(true);
+                    expect(typeof a.symbol).toBe('string');
+                    expect(typeof a.fraction).toBe('number');
+                    expect(typeof a.rounding).toBe('number');
+                    expect(a.positive).toBeDefined();
+                    expect(typeof a.positive).toBe('string');
+                    expect(a.positive.length >= 0).toBe(true);
+                    expect(a.negative).toBeDefined();
+                    expect(typeof a.negative).toBe('string');
+                    expect(a.negative.length >= 0).toBe(true);
+                    expect(a.decimal).toBeDefined();
+                    expect(typeof a.decimal).toBe('string');
+                    expect(a.decimal.length > 0).toBe(true);
+                    expect(a.grouping).toBeDefined();
+                    expect(typeof a.grouping).toBe('string');
+                    expect(a.grouping.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getNumberPattern(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.38 getNumberPattern using type=percent, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.pattern).toBeDefined();
+                    expect(typeof a.pattern).toBe('string');
+                    expect(a.pattern.length > 0).toBe(true);
+                    expect(typeof a.symbol).toBe('string');
+                    expect(typeof a.fraction).toBe('number');
+                    expect(typeof a.rounding).toBe('number');
+                    expect(a.positive).toBeDefined();
+                    expect(typeof a.positive).toBe('string');
+                    expect(a.positive.length >= 0).toBe(true);
+                    expect(a.negative).toBeDefined();
+                    expect(typeof a.negative).toBe('string');
+                    expect(a.negative.length >= 0).toBe(true);
+                    expect(a.decimal).toBeDefined();
+                    expect(typeof a.decimal).toBe('string');
+                    expect(a.decimal.length > 0).toBe(true);
+                    expect(a.grouping).toBeDefined();
+                    expect(typeof a.grouping).toBe('string');
+                    expect(a.grouping.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getNumberPattern(win, fail, {type: 'percent'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("globalization.spec.39 getNumberPattern using type=currency, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.pattern).toBeDefined();
+                    expect(typeof a.pattern).toBe('string');
+                    expect(a.pattern.length > 0).toBe(true);
+                    expect(typeof a.symbol).toBe('string');
+                    expect(typeof a.fraction).toBe('number');
+                    expect(typeof a.rounding).toBe('number');
+                    expect(a.positive).toBeDefined();
+                    expect(typeof a.positive).toBe('string');
+                    expect(a.positive.length >= 0).toBe(true);
+                    expect(a.negative).toBeDefined();
+                    expect(typeof a.negative).toBe('string');
+                    expect(a.negative.length >= 0).toBe(true);
+                    expect(a.decimal).toBeDefined();
+                    expect(typeof a.decimal).toBe('string');
+                    expect(a.decimal.length > 0).toBe(true);
+                    expect(a.grouping).toBeDefined();
+                    expect(typeof a.grouping).toBe('string');
+                    expect(a.grouping.length > 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getNumberPattern(win, fail, {type: 'currency'});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    
+    describe("getCurrencyPattern", function() {
+        it("globalization.spec.1 should exist", function() {
+            expect(typeof navigator.globalization.getCurrencyPattern).toBeDefined();
+            expect(typeof navigator.globalization.getCurrencyPattern == 'function').toBe(true);
+        });
+        it("globalization.spec.41 getCurrencyPattern using EUR for currency, success callback should be called with a Properties object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(typeof a).toBe('object');
+                    expect(a.pattern).toBeDefined();
+                    expect(typeof a.pattern).toBe('string');
+                    expect(a.pattern.length > 0).toBe(true);
+                    expect(a.code).toBeDefined();
+                    expect(typeof a.code).toBe('string');
+                    expect(a.code.length > 0).toBe(true);
+                    expect(typeof a.fraction).toBe('number');
+                    expect(typeof a.rounding).toBe('number');                   
+                    expect(a.decimal).toBeDefined();
+                    expect(typeof a.decimal).toBe('string');
+                    expect(a.decimal.length >= 0).toBe(true);                    
+                    expect(a.grouping).toBeDefined();
+                    expect(typeof a.grouping).toBe('string');
+                    expect(a.grouping.length >= 0).toBe(true);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.globalization.getCurrencyPattern("EUR", win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+});


Mime
View raw message