diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..6ee021dd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,12 @@ +# How to contribute + +Hi, thanks for contributing to Crafty! We've got guidlines on + +- [How to build Crafty](https://github.com/craftyjs/Crafty/wiki/Building) +- [What workflow to use](https://github.com/craftyjs/Crafty/wiki/Workflow) + +## Quick summary + +- You'll need node, npm, and grunt-cli (installed globally) to work with Crafty's dev tools +- With those already installed, `npm install` from Crafty's directory will setup everything else you need +- Once you've made any changes, then `grunt check` will build and test your new version of Crafty. \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js index d60cb2fb..3cd633bb 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -76,8 +76,9 @@ module.exports = function (grunt) { }, jshint: { - files: ['Gruntfile.js', 'src/**/*.js'], + files: ['Gruntfile.js', 'src/**/*.js', 'tests/*.js'], options: { + trailing: true, globals: { } } @@ -85,24 +86,23 @@ module.exports = function (grunt) { qunit: { all: [ - 'tests/core.html', - 'tests/animation/animation.html', - 'tests/stage.html', - 'tests/events.html', - 'tests/math.html', - 'tests/isometric.html', - 'tests/loader.html', - 'tests/text.html', - 'tests/dom.html', - 'tests/tween.html', - 'tests/sound.html' + 'tests/index.html', + 'tests/animation/animation.html' ] - }, + }, jsvalidate: { - files: "crafty.js" + files: ['crafty.js', 'tests/*.js'] }, + connect: { + server: { + options: { + keepalive: true + } + } + } + }); // Load the plugin that provides the "uglify" task. @@ -110,6 +110,7 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-qunit'); grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks('grunt-jsvalidate'); grunt.loadNpmTasks('grunt-browserify'); grunt.loadNpmTasks('grunt-banner'); @@ -117,10 +118,10 @@ module.exports = function (grunt) { grunt.registerTask('version', 'Takes the version into src/version.js', function() { fs.writeFileSync('src/version.js', 'module.exports = "' + version + '";'); }); - + // Build development grunt.registerTask('build:dev', ['browserify:debug', 'usebanner']); - + // Build release grunt.registerTask('build:release', ['browserify:dist', 'usebanner']); @@ -139,5 +140,4 @@ module.exports = function (grunt) { // Run only tests grunt.registerTask('validate', ['qunit']); - }; diff --git a/README.md b/README.md index b4e1d6ae..c76687da 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Crafty.e("RightPoints, DOM, 2D, Text") ##Developing If you want to fix a bug, please submit a pull request against the development branch. Some guides to help you can be found [on the wiki](https://github.com/craftyjs/Crafty/wiki) - + If you would like to make larger contributions please catch us in the [forum](https://groups.google.com/forum/?fromgroups#!forum/craftyjs) and we will help you get started. Much appreciated :-) diff --git a/build/api-gen.coffee b/build/api-gen.coffee index 566a2a39..24286b15 100644 --- a/build/api-gen.coffee +++ b/build/api-gen.coffee @@ -38,7 +38,7 @@ processBlock = (block)-> if not Table.comps[block.comp] Table.comps[block.name] = {name:block.name, parts:[]} Table.comps[block.name].block = block - + # Having a component tag means it's part of a component page if block.comp if not Table.comps[block.comp] @@ -123,10 +123,10 @@ class DocBlock else return false - # Method for returning the documentation for this block + # Method for returning the documentation for this block getContent: ()-> if @isFunctionTag(@prevTag) then @code.push("") - return @code.join("\n") + triggerBlock(@triggers) + seeBlock(@see) + return @code.join("\n") + triggerBlock(@triggers) + seeBlock(@see) # parse js file parseJS = (path) -> @@ -140,7 +140,7 @@ parseJS = (path) -> block = new DocBlock() block.file = path block.line = ln - open = true + open = true # process if open block.processLine(line) @@ -153,18 +153,18 @@ parseJS = (path) -> if block.categories.length is 0 and block.comp is null console.log("No component or category for block at #{block.file}:#{block.line+1} (#{block.name})") data.push block - + cleanName = (name) -> name.replace(".", "-") triggerBlock = (triggers, noheader)-> - return '' if triggers?.length is 0 - if noheader then block = "
diff --git a/tests/animation/sprite-animation.js b/tests/animation/sprite-animation.js index c0c412cc..95731aab 100644 --- a/tests/animation/sprite-animation.js +++ b/tests/animation/sprite-animation.js @@ -42,7 +42,7 @@ test("Test .getReel() with no active reel", function(){ test("Test .reel() with no active reel", function() { var ret; var newAnimation = Crafty.e("SpriteAnimation"); - + ret = newAnimation.reel(); strictEqual(ret, null, "reel() returns null"); @@ -52,7 +52,7 @@ test("Test .reel() with no active reel", function() { test("Test .animate() with no active reel", function() { var newAnimation = Crafty.e("SpriteAnimation"); - + throws(function(){newAnimation.animate()}, /No reel is specified, and there is no currently active reel./, "Throws when calling .animate().") throws(function(){newAnimation.animate(3)}, /No reel is specified, and there is no currently active reel./, "Throws when calling .animate() with loop count.") @@ -121,7 +121,7 @@ test("Test .resetAnimation() with no active reel", function() { newAnimation.destroy(); }); - + test("Test .isPlaying() with no active reel", function() { @@ -162,19 +162,19 @@ test("Test reel switching functionality", function(){ equal(spriteAnimation._currentReelId, "short", "Correct _currentReelId after switching"); equal(spriteAnimation._currentReel.id, "short", "Correct _currentReel.id after switching"); var e =""; - + throws( function(){spriteAnimation.reel("wrong");}, /The specified reel wrong is undefined/, "Function should throw on bad reel"); - + equal(spriteAnimation._currentReelId, "short", "Correct _currentReelId after attempting to switch to bad reel"); - - + + }); test("Test using reel() with no arguments", function(){ spriteAnimation.reel("count"); var ret = spriteAnimation.reel(); equal(ret, "count", ".reel() returns the current id"); - + // Test setting reel id manually, since that's what reel() should return // Don't ever do this in actual code! spriteAnimation._currentReelId = null; @@ -183,7 +183,7 @@ test("Test using reel() with no arguments", function(){ // Reset currentReelId, since we messed it up! spriteAnimation.reel("count"); - + }); test("Test using .getReel() with no arguments", function(){ @@ -210,7 +210,7 @@ test("Test using .reel to set an animation using start and end values", function spriteAnimation.reel('short-test'); var reel = spriteAnimation.getReel('short-test'); equal(reel.id, "short-test", "Id of reel is set correctly."); - + equal(reel.duration, 3, "Reel has correct duration.") equal(reel.currentFrame, 0, "Reel starts with correct currentFrame of 0."); equal(reel.defaultLoops, 1, "Reel starts with correct default number of loops."); @@ -218,8 +218,8 @@ test("Test using .reel to set an animation using start and end values", function var frames = reel.frames; equal(frames.length, 3, "Reel has correct number of frames."); deepEqual(frames[0], [0, 0], "First frame is correct."); - deepEqual(frames[1], [64, 0], "Second frame is correct."); - deepEqual(frames[2], [128, 0], "Third frame is correct."); + deepEqual(frames[1], [1, 0], "Second frame is correct."); + deepEqual(frames[2], [2, 0], "Third frame is correct."); }) @@ -230,7 +230,7 @@ test("Test using .reel to set an animation using an array of frames", function() spriteAnimation.reel('short-test-2'); var reel = spriteAnimation.getReel('short-test-2'); equal(reel.id, "short-test-2", "Id of reel is set correctly."); - + equal(reel.duration, 3, "Reel has correct duration.") equal(reel.currentFrame, 0, "Reel starts with correct currentFrame of 0."); equal(reel.defaultLoops, 1, "Reel starts with correct default number of loops."); @@ -239,9 +239,9 @@ test("Test using .reel to set an animation using an array of frames", function() equal(frames.length, 3, "Reel has correct number of frames."); // This relies on the sprite being defined with a size of 64 deepEqual(frames[0], [0, 0], "First frame is correct."); - deepEqual(frames[1], [64, 0], "Second frame is correct."); - deepEqual(frames[2], [128, 0], "Third frame is correct."); - + deepEqual(frames[1], [1, 0], "Second frame is correct."); + deepEqual(frames[2], [2, 0], "Third frame is correct."); + }) module("Reel state"); @@ -357,6 +357,24 @@ test("Play an animation where sprites are displayed for more than one frame", fu } }); +test("Play an animation at twice the rate", function(){ + spriteAnimation.animationSpeed = 2; + spriteAnimation.animate('count'); + Crafty.timer.simulateFrames(3); + var activeReel = spriteAnimation.getReel(); + equal(activeReel.currentFrame, 6, "Frame 6 should be displayed after 3 ticks at double speed"); + spriteAnimation.animationSpeed = 1; +}) + +test("Play an animation at half the rate", function(){ + spriteAnimation.animationSpeed = 0.5; + spriteAnimation.animate('count'); + Crafty.timer.simulateFrames(6); + var activeReel = spriteAnimation.getReel(); + equal(activeReel.currentFrame, 3, "Frame 3 should be displayed after 6 ticks at half speed"); + spriteAnimation.animationSpeed = 1; +}) + test("Show the last frame after an animation ends", function() { spriteAnimation.animate('count'); Crafty.timer.simulateFrames(20); @@ -414,7 +432,7 @@ test("Play an animation with an infinite repeat count", function() { expected.push(0); expected.push(1); expected.push(2); - } + } deepEqual(eventFrames, expected, "Expected events matching the amount of frames that pass"); deepEqual(finishedAnimations, [], "Expected no animation to end"); diff --git a/tests/audio.js b/tests/audio.js new file mode 100644 index 00000000..29f09f76 --- /dev/null +++ b/tests/audio.js @@ -0,0 +1,107 @@ +module("Audio", { + setup: function() { + // prepare something for all following tests + }, + teardown: function() { + // clean up after each test + Crafty("*").destroy(); + } +}); + +//Set up some test fixtures +function MockAudio() { + var self = this; + this.endedListeners = []; + this.canPlayType = function() { + return true; + }; + this.addEventListener = function(event, listener) { + switch (event) { + case "ended": + this.endedListeners.push(listener); + break; + default: + throw new Exception("Not implemented"); + } + }; + this.removeEventListener = function(event, listener) { + switch (event) { + case "ended": + var ind = this.endedListeners.indexOf(listener); + if (ind) this.endedListeners.splice(ind, 1); + break; + default: + throw new Exception("Not implemented"); + } + }; + + function fireEnded() { + setTimeout(function() { + self.ended = true; + self.endedListeners.forEach(function(f) { + f.call(self); + }); + }, 0); + } + this.play = function() { + if (this.src) { + fireEnded(); + } + }; + this.pause = function() {}; + this.ended = false; +} + +function ChromeBuggedAudio() { + var self = this; + this.canPlayType = function() { + return true; + }; + this.addEventListener = function(event, listener) {}; + this.removeEventListener = function(event, listener) {}; + this.play = function() { + if (this.src) { + self.ended = true; + self.src = null; + ok(true, "Audio played"); + } + }; + this.pause = function() {}; + this.ended = false; +} + + +asyncTest("setChannels", function() { + // Test that setChannels doesn't break sound + expect(2); + window.Audio = MockAudio; + Crafty.support.audio = true; + Crafty.audio.setChannels(5); + Crafty.audio.add("mockSound", ["sound.ogg"]); + var a = Crafty.audio.play("mockSound", 1); + ok(typeof a === "object", "Type of a is object: " + a); + a.addEventListener("ended", function() { + ok(true, "Sound played"); + delete window.Audio; //reset Audio to platform default + Crafty.audio.channels = []; + start(); + }); +}); + +test("chromeBug", function() { + // Test that we don't exhaust our audio channels if Chrome bug 280417 + // eats our "ended" events + expect(10); + window.Audio = ChromeBuggedAudio; + Crafty.support.audio = true; + Crafty.audio.setChannels(1); + Crafty.support.audio = true; + Crafty.audio.add("mockSound", ["sound.ogg"]); + + var a; + for (var i = 0; i < 10; i++) { + a = Crafty.audio.play("mockSound", 1); // This will trigger an assertion + } + delete window.Audio; //reset Audio to platform default + Crafty.audio.channels = []; +}); \ No newline at end of file diff --git a/tests/camera.html b/tests/camera.html index 951472fc..96cb3baf 100644 --- a/tests/camera.html +++ b/tests/camera.html @@ -13,15 +13,15 @@
- +
- +
- +
@@ -38,9 +38,9 @@ } , imageEntities = '2D, DOM, Image' , hotspotEntities = '2D, DOM, Color, Mouse'; - - - + + + config.imagePath = 'http://placekitten.com/' + config.playgroundWidth + '/' + config.playgroundHeight Crafty.scene('main', function() { diff --git a/tests/core.html b/tests/core.html deleted file mode 100755 index 82428a83..00000000 --- a/tests/core.html +++ /dev/null @@ -1,666 +0,0 @@ - - - - - -
- - - - - - - - -
-