diff --git a/.gitignore b/.gitignore index 15812b0..4f7cdfc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ node_modules build +.grunt .sizecache.json *.log* +_SpecRunner.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ad0146..fff320b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,7 +39,7 @@ You should see a green message in the console: You can also run the tests in the browser. Start a test server from the project root: - $ grunt connect:tests + $ grunt specs This will automatically open the tests at http://127.0.0.1:9998/test/index.html in the default browser, with livereload enabled. diff --git a/Gruntfile.js b/Gruntfile.js index 3bc12c6..16b1fda 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -9,10 +9,23 @@ module.exports = function (grunt) { qunit: { all: ['test/index.html'] }, + jasmine: { + src: 'src/**/*.js', + options: { + vendor: [ + 'http://code.jquery.com/jquery-1.11.1.min.js', + 'vendor/jasmine-reporter.js' + ], + specs: 'spec/**/*.js', + // So we can view specs in a browser as well... + keepRunner: true + } + }, jshint: { files: [ 'Gruntfile.js', - 'jquery.cookie.js' + 'src/**/*.js' + // TODO 'spec/**/*.js' ], options: { jshintrc: true @@ -24,7 +37,7 @@ module.exports = function (grunt) { }, build: { files: { - 'build/jquery.cookie-<%= pkg.version %>.min.js': 'jquery.cookie.js' + 'build/jquery.cookie-<%= pkg.version %>.min.js': 'src/jquery.cookie.js' } } }, @@ -33,15 +46,15 @@ module.exports = function (grunt) { livereload: true }, files: [ - 'jquery.cookie.js', - 'test/tests.js' + 'src/**/*.js', + 'spec/**/*.js' ], - tasks: 'default' + tasks: ['default'] }, compare_size: { files: [ 'build/jquery.cookie-<%= pkg.version %>.min.js', - 'jquery.cookie.js' + 'src/jquery.cookie.js' ], options: { compress: { @@ -64,6 +77,14 @@ module.exports = function (grunt) { keepalive: true, livereload: true } + }, + specs: { + options: { + port: 3000, + open: 'http://127.0.0.1:3000/_SpecRunner.html', + keepalive: true, + livereload: true + } } }, 'saucelabs-qunit': { @@ -155,7 +176,99 @@ module.exports = function (grunt) { ] } } - } + }, + 'saucelabs-jasmine': { + all: { + options: { + urls: ['http://127.0.0.1:9999/_SpecRunner.html'], + tunnelTimeout: 5, + build: process.env.TRAVIS_JOB_ID, + concurrency: 3, + browsers: [ + // iOS + { + browserName: 'iphone', + platform: 'OS X 10.9', + version: '7.1' + }, + { + browserName: 'ipad', + platform: 'OS X 10.9', + version: '7.1' + }, + // Android + { + browserName: 'android', + platform: 'Linux', + version: '4.3' + }, + // OS X + { + browserName: 'safari', + platform: 'OS X 10.9', + version: '7' + }, + { + browserName: 'safari', + platform: 'OS X 10.8', + version: '6' + }, + { + browserName: 'firefox', + platform: 'OS X 10.9', + version: '28' + }, + // Windows + { + browserName: 'internet explorer', + platform: 'Windows 8.1', + version: '11' + }, + { + browserName: 'internet explorer', + platform: 'Windows 8', + version: '10' + }, + { + browserName: 'internet explorer', + platform: 'Windows 7', + version: '11' + }, + { + browserName: 'internet explorer', + platform: 'Windows 7', + version: '10' + }, + { + browserName: 'internet explorer', + platform: 'Windows 7', + version: '9' + }, + { + browserName: 'internet explorer', + platform: 'Windows 7', + version: '8' + }, + { + browserName: 'firefox', + platform: 'Windows 7', + version: '29' + }, + { + browserName: 'chrome', + platform: 'Windows 7', + version: '34' + }, + // Linux + { + browserName: 'firefox', + platform: 'Linux', + version: '29' + } + ] + } + } + }, }); // Loading dependencies @@ -165,7 +278,8 @@ module.exports = function (grunt) { } } - grunt.registerTask('default', ['jshint', 'qunit', 'uglify', 'compare_size']); - grunt.registerTask('saucelabs', ['connect:saucelabs', 'saucelabs-qunit']); - grunt.registerTask('ci', ['jshint', 'qunit', 'saucelabs']); + grunt.registerTask('default', ['jshint', 'qunit', 'jasmine', 'uglify', 'compare_size']); + grunt.registerTask('saucelabs', ['connect:saucelabs', 'saucelabs-qunit', 'saucelabs-jasmine']); + grunt.registerTask('ci', ['jshint', 'qunit', 'jasmine', 'saucelabs']); + grunt.registerTask('specs', ['jasmine', 'connect:specs']); }; diff --git a/bower.json b/bower.json index 2d8c25b..1b38e4d 100644 --- a/bower.json +++ b/bower.json @@ -2,7 +2,7 @@ "name": "jquery.cookie", "version": "1.4.1", "main": [ - "./jquery.cookie.js" + "./src/jquery.cookie.js" ], "dependencies": { "jquery": ">=1.2" diff --git a/component.json b/component.json index 58f79d6..0fad480 100644 --- a/component.json +++ b/component.json @@ -7,8 +7,8 @@ "dependencies": {}, "development": {}, "license": "MIT", - "main": "jquery.cookie.js", + "main": "src/jquery.cookie.js", "scripts": [ - "jquery.cookie.js" + "src/jquery.cookie.js" ] } diff --git a/package.json b/package.json index b15b7ad..569f11b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "jquery.cookie", "version": "1.4.1", "description": "A simple, lightweight jQuery plugin for reading, writing and deleting cookies.", - "main": "jquery.cookie.js", + "main": "src/jquery.cookie.js", "directories": { "test": "test" }, @@ -22,6 +22,7 @@ "grunt-contrib-jshint": "~0.10.0", "grunt-contrib-uglify": "~0.2.0", "grunt-contrib-qunit": "~0.2.0", + "grunt-contrib-jasmine": "~0.6.4", "grunt-contrib-watch": "~0.6.1", "grunt-compare-size": "~0.4.0", "grunt-saucelabs": "~7.0.0", @@ -29,11 +30,11 @@ "gzip-js": "~0.3.0" }, "volo": { - "url": "https://raw.github.com/carhartl/jquery-cookie/v{version}/jquery.cookie.js" + "url": "https://raw.github.com/carhartl/jquery-cookie/v{version}/src/jquery.cookie.js" }, "jspm": { "main": "jquery.cookie", - "files": ["jquery.cookie.js"], + "files": ["src/jquery.cookie.js"], "buildConfig": { "uglify": true } @@ -42,9 +43,9 @@ "dependencies": { "jquery": ">=1.2" }, - "main": "jquery.cookie.js", + "main": "src/jquery.cookie.js", "include": [ - "jquery.cookie.js" + "src/jquery.cookie.js" ] } } diff --git a/spec/delete_spec.js b/spec/delete_spec.js new file mode 100644 index 0000000..4e876a8 --- /dev/null +++ b/spec/delete_spec.js @@ -0,0 +1,5 @@ +describe("delete", function () { + it("test", function () { + expect(true).toBe(true); + }); +}); diff --git a/spec/read_spec.js b/spec/read_spec.js new file mode 100644 index 0000000..c147a02 --- /dev/null +++ b/spec/read_spec.js @@ -0,0 +1,5 @@ +describe("read", function () { + it("test", function () { + expect(true).toBe(true); + }); +}); diff --git a/spec/write_spec.js b/spec/write_spec.js new file mode 100644 index 0000000..c69180b --- /dev/null +++ b/spec/write_spec.js @@ -0,0 +1,5 @@ +describe("write", function () { + it("test", function () { + expect(true).toBe(true); + }); +}); diff --git a/jquery.cookie.js b/src/jquery.cookie.js similarity index 100% rename from jquery.cookie.js rename to src/jquery.cookie.js diff --git a/test/index.html b/test/index.html index 841310b..ade6830 100644 --- a/test/index.html +++ b/test/index.html @@ -6,7 +6,7 @@ - + diff --git a/test/malformed_cookie.html b/test/malformed_cookie.html index 17e8db8..74178d4 100644 --- a/test/malformed_cookie.html +++ b/test/malformed_cookie.html @@ -3,7 +3,7 @@ - + try { Object.defineProperty(document, "cookie", { get: function() { return "first=one; ; second=two"; } }); diff --git a/vendor/jasmine-reporter.js b/vendor/jasmine-reporter.js new file mode 100644 index 0000000..d2180da --- /dev/null +++ b/vendor/jasmine-reporter.js @@ -0,0 +1,396 @@ +/* + This file is part of the Jasmine JSReporter project from Ivan De Marino. + + Copyright (C) 2011-2014 Ivan De Marino + Copyright (C) 2014 Alex Treppass + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL IVAN DE MARINO BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +(function (jasmine) { + + if (!jasmine) { + throw new Error("[Jasmine JSReporter] 'Jasmine' library not found"); + } + + // ------------------------------------------------------------------------ + // Jasmine JSReporter for Jasmine 1.x + // ------------------------------------------------------------------------ + + /** + * Calculate elapsed time, in Seconds. + * @param startMs Start time in Milliseconds + * @param finishMs Finish time in Milliseconds + * @return Elapsed time in Seconds */ + function elapsedSec (startMs, finishMs) { + return (finishMs - startMs) / 1000; + } + + /** + * Round an amount to the given number of Digits. + * If no number of digits is given, than '2' is assumed. + * @param amount Amount to round + * @param numOfDecDigits Number of Digits to round to. Default value is '2'. + * @return Rounded amount */ + function round (amount, numOfDecDigits) { + numOfDecDigits = numOfDecDigits || 2; + return Math.round(amount * Math.pow(10, numOfDecDigits)) / Math.pow(10, numOfDecDigits); + } + + /** + * Create a new array which contains only the failed items. + * @param items Items which will be filtered + * @returns {Array} of failed items */ + function failures (items) { + var fs = [], i, v; + for (i = 0; i < items.length; i += 1) { + v = items[i]; + if (!v.passed_) { + fs.push(v); + } + } + return fs; + } + + /** + * Collect information about a Suite, recursively, and return a JSON result. + * @param suite The Jasmine Suite to get data from + */ + function getSuiteData (suite) { + var suiteData = { + description : suite.description, + durationSec : 0, + specs: [], + suites: [], + passed: true + }, + specs = suite.specs(), + suites = suite.suites(), + i, ilen; + + // Loop over all the Suite's Specs + for (i = 0, ilen = specs.length; i < ilen; ++i) { + suiteData.specs[i] = { + description : specs[i].description, + durationSec : specs[i].durationSec, + passed : specs[i].results().passedCount === specs[i].results().totalCount, + skipped : specs[i].results().skipped, + passedCount : specs[i].results().passedCount, + failedCount : specs[i].results().failedCount, + totalCount : specs[i].results().totalCount, + failures: failures(specs[i].results().getItems()) + }; + suiteData.passed = !suiteData.specs[i].passed ? false : suiteData.passed; + suiteData.durationSec += suiteData.specs[i].durationSec; + } + + // Loop over all the Suite's sub-Suites + for (i = 0, ilen = suites.length; i < ilen; ++i) { + suiteData.suites[i] = getSuiteData(suites[i]); //< recursive population + suiteData.passed = !suiteData.suites[i].passed ? false : suiteData.passed; + suiteData.durationSec += suiteData.suites[i].durationSec; + } + + // Rounding duration numbers to 3 decimal digits + suiteData.durationSec = round(suiteData.durationSec, 4); + + return suiteData; + } + + var JSReporter = function () { + }; + + JSReporter.prototype = { + reportRunnerStarting: function (runner) { + // Nothing to do + }, + + reportSpecStarting: function (spec) { + // Start timing this spec + spec.startedAt = new Date(); + }, + + reportSpecResults: function (spec) { + // Finish timing this spec and calculate duration/delta (in sec) + spec.finishedAt = new Date(); + // If the spec was skipped, reportSpecStarting is never called and spec.startedAt is undefined + spec.durationSec = spec.startedAt ? elapsedSec(spec.startedAt.getTime(), spec.finishedAt.getTime()) : 0; + }, + + reportSuiteResults: function (suite) { + // Nothing to do + }, + + reportRunnerResults: function (runner) { + var suites = runner.suites(), + i, j, ilen; + + // Attach results to the "jasmine" object to make those results easy to scrap/find + jasmine.runnerResults = { + suites: [], + durationSec : 0, + passed : true + }; + + // Loop over all the Suites + for (i = 0, ilen = suites.length, j = 0; i < ilen; ++i) { + if (suites[i].parentSuite === null) { + jasmine.runnerResults.suites[j] = getSuiteData(suites[i]); + // If 1 suite fails, the whole runner fails + jasmine.runnerResults.passed = !jasmine.runnerResults.suites[j].passed ? false : jasmine.runnerResults.passed; + // Add up all the durations + jasmine.runnerResults.durationSec += jasmine.runnerResults.suites[j].durationSec; + j++; + } + } + + // Decorate the 'jasmine' object with getters + jasmine.getJSReport = function () { + if (jasmine.runnerResults) { + return jasmine.runnerResults; + } + return null; + }; + jasmine.getJSReportAsString = function () { + return JSON.stringify(jasmine.getJSReport()); + }; + } + }; + + // export public + jasmine.JSReporter = JSReporter; + + + // ------------------------------------------------------------------------ + // Jasmine JSReporter for Jasmine 2.0 + // ------------------------------------------------------------------------ + + /* + Simple timer implementation + */ + var Timer = function () {}; + + Timer.prototype.start = function () { + this.startTime = new Date().getTime(); + return this; + }; + + Timer.prototype.elapsed = function () { + if (this.startTime == null) { + return -1; + } + return new Date().getTime() - this.startTime; + }; + + /* + Utility methods + */ + var _extend = function (obj1, obj2) { + for (var prop in obj2) { + obj1[prop] = obj2[prop]; + } + return obj1; + }; + var _clone = function (obj) { + if (obj !== Object(obj)) { + return obj; + } + return _extend({}, obj); + }; + + jasmine.JSReporter2 = function () { + this.specs = {}; + this.suites = {}; + this.rootSuites = []; + this.suiteStack = []; + + // export methods under jasmine namespace + jasmine.getJSReport = this.getJSReport; + jasmine.getJSReportAsString = this.getJSReportAsString; + }; + + var JSR = jasmine.JSReporter2.prototype; + + // Reporter API methods + // -------------------- + + JSR.suiteStarted = function (suite) { + suite = this._cacheSuite(suite); + // build up suite tree as we go + suite.specs = []; + suite.suites = []; + suite.passed = true; + suite.parentId = this.suiteStack.slice(this.suiteStack.length -1)[0]; + if (suite.parentId) { + this.suites[suite.parentId].suites.push(suite); + } else { + this.rootSuites.push(suite.id); + } + this.suiteStack.push(suite.id); + suite.timer = new Timer().start(); + }; + + JSR.suiteDone = function (suite) { + suite = this._cacheSuite(suite); + suite.duration = suite.timer.elapsed(); + suite.durationSec = suite.duration / 1000; + this.suiteStack.pop(); + + // maintain parent suite state + var parent = this.suites[suite.parentId]; + if (parent) { + parent.passed = parent.passed && suite.passed; + } + + // keep report representation clean + delete suite.timer; + delete suite.id; + delete suite.parentId; + delete suite.fullName; + }; + + JSR.specStarted = function (spec) { + spec = this._cacheSpec(spec); + spec.timer = new Timer().start(); + // build up suites->spec tree as we go + spec.suiteId = this.suiteStack.slice(this.suiteStack.length -1)[0]; + this.suites[spec.suiteId].specs.push(spec); + }; + + JSR.specDone = function (spec) { + spec = this._cacheSpec(spec); + + spec.duration = spec.timer.elapsed(); + spec.durationSec = spec.duration / 1000; + + spec.skipped = spec.status === 'pending'; + spec.passed = spec.skipped || spec.status === 'passed'; + + // totalCount and passedCount will be populated if/when jasmine#575 gets accepted + spec.totalCount = spec.totalExpectations || 0; + spec.passedCount = spec.passedExpectations ? spec.passedExpectations.length : 0; + + spec.failedCount = spec.failedExpectations.length; + spec.failures = []; + + for (var i = 0, j = spec.failedExpectations.length; i < j; i++) { + var fail = spec.failedExpectations[i]; + spec.failures.push({ + type: 'expect', + expected: fail.expected, + passed: false, + message: fail.message, + matcherName: fail.matcherName, + trace: { + stack: fail.stack + } + }); + } + + // maintain parent suite state + var parent = this.suites[spec.suiteId]; + if (spec.failed) { + parent.failingSpecs.push(spec); + } + parent.passed = parent.passed && spec.passed; + + // keep report representation clean + delete spec.timer; + delete spec.totalExpectations; + delete spec.passedExpectations; + delete spec.suiteId; + delete spec.fullName; + delete spec.id; + delete spec.status; + delete spec.failedExpectations; + }; + + JSR.jasmineDone = function () { + this._buildReport(); + }; + + JSR.getJSReport = function () { + if (jasmine.jsReport) { + return jasmine.jsReport; + } + }; + + JSR.getJSReportAsString = function () { + if (jasmine.jsReport) { + return JSON.stringify(jasmine.jsReport); + } + }; + + // Private methods + // --------------- + + JSR._haveSpec = function (spec) { + return this.specs[spec.id] != null; + }; + + JSR._cacheSpec = function (spec) { + var existing = this.specs[spec.id]; + if (existing == null) { + existing = this.specs[spec.id] = _clone(spec); + } else { + _extend(existing, spec); + } + return existing; + }; + + JSR._haveSuite = function (suite) { + return this.suites[suite.id] != null; + }; + + JSR._cacheSuite = function (suite) { + var existing = this.suites[suite.id]; + if (existing == null) { + existing = this.suites[suite.id] = _clone(suite); + } else { + _extend(existing, suite); + } + return existing; + }; + + JSR._buildReport = function () { + var overallDuration = 0; + var overallPassed = true; + var overallSuites = []; + + for (var i = 0, j = this.rootSuites.length; i < j; i++) { + var suite = this.suites[this.rootSuites[i]]; + overallDuration += suite.duration; + overallPassed = overallPassed && suite.passed; + overallSuites.push(suite); + } + + jasmine.jsReport = { + passed: overallPassed, + durationSec: overallDuration / 1000, + suites: overallSuites + }; + }; + +})(jasmine); + +jasmine.getEnv().addReporter(new jasmine.JSReporter2());