├── README.md ├── backend └── nodejute │ ├── LICENSE │ ├── README.md │ ├── examples │ ├── README.md │ ├── clientSide │ │ ├── testToolbar.html │ │ ├── testToolbar.js │ │ └── toolbar.js │ ├── integration │ │ └── testYahoo.js │ └── serverSide │ │ ├── mySum.js │ │ ├── npm-debug.log │ │ └── testMySum.js │ ├── getConfig.js │ ├── jute │ ├── actions.js │ ├── actions │ │ ├── clearResults.js │ │ ├── clearTests.js │ │ ├── common.js │ │ ├── getTest.js │ │ ├── heartBeat.js │ │ ├── message.js │ │ ├── pop.js │ │ ├── prune.js │ │ ├── runTest.js │ │ ├── startPhantomjs.js │ │ ├── startSelenium.js │ │ ├── startSelenium2.js │ │ ├── status.js │ │ ├── testReport.js │ │ └── yuitest-coverage-report.jar │ ├── configure.js │ ├── daemon.js │ ├── jute_docs │ │ ├── capture.html │ │ ├── jute.js │ │ ├── jute_client.js │ │ └── run_tests.html │ ├── phantomJUTE.js │ ├── server.js │ └── yuitest-coverage.jar │ ├── jute_backend.js │ ├── jute_jasmine.js │ ├── jute_v8.js │ ├── package.json │ ├── submit_test.js │ ├── test │ ├── getConfig │ │ └── testGetConfig.js │ ├── jute │ │ ├── actions │ │ │ ├── clearResults │ │ │ │ └── testClearResults.js │ │ │ ├── clearTests │ │ │ │ └── testClearTests.js │ │ │ ├── common │ │ │ │ └── testCommon.js │ │ │ ├── getTest │ │ │ │ └── testGetTest.js │ │ │ ├── heartBeat │ │ │ │ └── testHeartBeat.js │ │ │ ├── pop │ │ │ │ └── testPop.js │ │ │ └── prune │ │ │ │ └── testPrune.js │ │ └── configure │ │ │ └── testConfigure.js │ ├── jute_backend │ │ └── testBackend.js │ └── mock │ │ ├── hub.js │ │ └── req.js │ └── testV8Script.js └── js ├── README └── jute.js /backend/nodejute/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Yahoo! Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use of this software in source and binary forms, 5 | with or without modification, are permitted provided that the following 6 | conditions are met: 7 | 8 | * Redistributions of source code must retain the above 9 | copyright notice, this list of conditions and the 10 | following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the 14 | following disclaimer in the documentation and/or other 15 | materials provided with the distribution. 16 | 17 | * Neither the name of Yahoo! Inc. nor the names of its 18 | contributors may be used to endorse or promote products 19 | derived from this software without specific prior 20 | written permission of Yahoo! Inc. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 23 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 25 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | 34 | -------------------------------------------------------------------------------- /backend/nodejute/README.md: -------------------------------------------------------------------------------- 1 | JUTE Server and Backends 2 | ======================== 3 | 4 | This is the code for the JUTE npm package - https://github.com/zzo/JUTE has the full JUTE scoop... 5 | 6 | Look in https://github.com/zzo/JUTE/ for full documentation. 7 | -------------------------------------------------------------------------------- /backend/nodejute/examples/README.md: -------------------------------------------------------------------------------- 1 | These 3 files show how it all fits together. 2 | 3 | testToolbar.html is the 'hub' - it loads YUI3 and the file being tested (toolbar.js) and the file with tests (testToolbar.js) 4 | 5 | toolbar.js so happens to use YUI3 in this example BUT it doesn't have to - it can use any (or none) JS Framework. Just make sure you HTML file loads whatever JS Framework you need for your tests to work. 6 | 7 | The tests themselves however MUST USE the YUI3 testing framework!! It's very nice and easy I promise you. 8 | 9 | Note in the HTML file the querystring '?coverage=1' tacked on to toolbar.js - this tells JUTE IF you want code coverage THIS is the file you want code coverage for. 10 | 11 | Also note in the HTML the 'log' div - IF you include this then you'll get a nice console while your unit tests run - it is NOT required. Just ensure IF you include this THEN add the 'yui3-skin-sam' class on your element. No big. 12 | 13 | That's about it for the HTML file. And nothing to say about the file you are testing - that does not change. 14 | 15 | In your test JS file (testToolbar.js in this example) this is important: 16 | 17 | 18 | YUI({ 19 | logInclude: { TestRunner: true }, 20 | gallery: 'gallery-2011.06.22-20-13' 21 | }).use('gallery-jute', 'toolbar', function(Y) { 22 | 23 | 24 | This loads up the client-side part of JUTE AND in this case pulls in the 'toolbar' module that we're testing. Note if your original JS is NOT a YUI3 module then you do not need this! 25 | 26 | Then the 'meat' of the file - I defined a test suite named 'toolbar' - THIS NAME IS IMPORTANT!! It will be translated into a directory name in your output directory! This directory will contain all test results and code coverage information for this suite - so name it sanely!! 27 | 28 | Then I define some tests and finally I call: 29 | 30 | 31 | Y.Test.Runner.add(suite); 32 | Y.UnitTest.go(); 33 | 34 | 35 | To kick the whole thing off. 36 | 37 | You can/should load up this HTML directly into your browser and your tests will run indendpendenly of JUTE. 38 | 39 | When you are ready to run within JUTE either run 'jute_submit_test' or run it directly via JUTE's WebUI. 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /backend/nodejute/examples/clientSide/testToolbar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /backend/nodejute/examples/clientSide/testToolbar.js: -------------------------------------------------------------------------------- 1 | YUI({ 2 | logInclude: { TestRunner: true }, 3 | }).use('test', 'toolbar', 'console', function(Y) { 4 | 5 | var suite = new Y.Test.Suite('toolbar'); 6 | suite.add(new Y.Test.Case({ 7 | name:'simple test', 8 | setUp: function() { 9 | this.tb = new Y.Toolbar(); 10 | }, 11 | testIsObject : function () { 12 | Y.log('testIsObject'); 13 | Y.Assert.isObject(this.tb); 14 | }, 15 | testMessage : function () { 16 | Y.log('testIsObject'); 17 | Y.Assert.areEqual(this.tb.message, "I am a toolbar!"); 18 | } 19 | 20 | })); 21 | 22 | Y.Test.Runner.add(suite); 23 | 24 | //initialize the console 25 | var yconsole = new Y.Console({ 26 | newestOnTop: false 27 | }); 28 | 29 | yconsole.render('#log'); 30 | Y.Test.Runner.run(); 31 | }); 32 | 33 | -------------------------------------------------------------------------------- /backend/nodejute/examples/clientSide/toolbar.js: -------------------------------------------------------------------------------- 1 | YUI().add('toolbar', function(Y) { 2 | Y.Toolbar = function Toolbar() { 3 | this.message = "I am a toolbar!"; 4 | function hidden(testme) { 5 | var ret = testme + ' TESTED'; 6 | return ret; 7 | } 8 | }; 9 | 10 | Y.Toolbar.prototype = { 11 | zop: function() { 12 | this.zop = 'ZOP'; 13 | } 14 | }; 15 | 16 | function testme(y) { 17 | return y * 55; 18 | } 19 | 20 | }, '1.0.0' ,{requires:['attribute', 'event-custom-base', 'common-utils']}); 21 | 22 | -------------------------------------------------------------------------------- /backend/nodejute/examples/integration/testYahoo.js: -------------------------------------------------------------------------------- 1 | //var YUI = require("yui3").YUI; 2 | YUI({ 3 | logInclude: { TestRunner: true }, 4 | }).use('test', function(Y) { 5 | 6 | var suite = new Y.Test.Suite('Yahoo'), 7 | soda = require('soda'); 8 | browser = soda.createClient({ 9 | url: 'http://yahoo.com', 10 | host: '99-184-251-18.lightspeed.sndgca.sbcglobal.net', 11 | browser: '*firefox' 12 | }); 13 | 14 | suite.add(new Y.Test.Case({ 15 | name:'Yahoo Search', 16 | testSearch: function() { 17 | var test = this; 18 | 19 | /* 20 | Y.log('running test search: ' + browser); 21 | Y.log('running test search: ' + browser.session); 22 | browser.session(function(err, sid) { 23 | Y.log('sess: ' + err); 24 | Y.log('sess sid: ' + sid); 25 | Y.log(browser.open); 26 | browser.open('/', function(err) { 27 | Y.log('open: ' + err); 28 | Y.log(browser.waitForPageToLoad); 29 | browser.waitForPageToLoad(10000, function(err) { 30 | Y.log('wait: ' + err); 31 | browser.testComplete(function(err) { 32 | Y.log('complete: ' + err); 33 | test.resume(function() { 34 | Y.log('resume: ' + err); 35 | Y.Assert.isNull(err); 36 | }); 37 | }); 38 | }); 39 | }); 40 | }); 41 | */ 42 | browser. 43 | chain. 44 | session(). 45 | open('/'). 46 | waitForPageToLoad(600000). 47 | typeKeys('name=p', 'ZZO Associates'). 48 | submit('name=sf1'). 49 | waitForPageToLoad(600000). 50 | getText('id=cquery', function(val) { 51 | console.log('TEXT: ' + val); 52 | }). 53 | verifyText('id=cquery', 'We have included zoo associates results - Show only ZZO Associates', function(error) { 54 | if (error) { 55 | console.log('TEST FAILED: ' + val); 56 | } 57 | }). 58 | testComplete(). 59 | end(function(error) { 60 | test.resume(function() { 61 | Y.Assert.isNull(error); 62 | }); 63 | }); 64 | 65 | test.wait(1000000); 66 | } 67 | })); 68 | 69 | suite.add(new Y.Test.Case({ 70 | name:'Yahoo Search Again', 71 | testSearch: function() { 72 | var test = this; 73 | browser. 74 | chain. 75 | open('/'). 76 | waitForPageToLoad(10000). 77 | typeKeys('name=p', 'Walrun'). 78 | submit('name=sf1'). 79 | waitForPageToLoad(10000). 80 | getText('id=cquery', function(val) { 81 | console.log('TEXT: ' + val); 82 | }). 83 | verifyText('id=cquery', 'We have included zoo associates results - Show only ZZO Associates', function(error) { 84 | if (error) { 85 | console.log('TEST FAILED: ' + val); 86 | } 87 | }). 88 | testComplete(). 89 | end(function(error) { 90 | test.resume(function() { 91 | Y.Assert.isNull(error); 92 | }); 93 | }); 94 | 95 | test.wait(10000000); 96 | } 97 | })); 98 | 99 | Y.Test.Runner.add(suite); 100 | Y.Test.Runner.run(); 101 | 102 | 103 | }); 104 | 105 | -------------------------------------------------------------------------------- /backend/nodejute/examples/serverSide/mySum.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mySum: function() { 3 | var result = 0, i = 0; 4 | if (typeof arguments[0] == 'object' && arguments[0].length) { 5 | for (;i < arguments[0].length; i++) { 6 | result += arguments[0][i]; 7 | } 8 | } else { 9 | for (;i < arguments.length; i++) { 10 | result += arguments[i]; 11 | } 12 | } 13 | return result; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /backend/nodejute/examples/serverSide/npm-debug.log: -------------------------------------------------------------------------------- 1 | info it worked if it ends with ok 2 | verbose cli [ 'node', '/usr/local/bin/npm', 'restart', 'jute' ] 3 | info using npm@1.0.22 4 | info using node@v0.4.5 5 | verbose config file /home/trostler/.npmrc 6 | verbose config file /usr/local/etc/npmrc 7 | ERR! Error: ENOENT, No such file or directory '/home/trostler/JUTE/backend/nodejute/node_modules/jute/package.json' 8 | ERR! Report this *entire* log at: 9 | ERR! 10 | ERR! or email it to: 11 | ERR! 12 | ERR! 13 | ERR! System Linux 2.6.38.8-2-vs2.3.0.37-rc17-blkio 14 | ERR! command "node" "/usr/local/bin/npm" "restart" "jute" 15 | ERR! cwd /home/trostler/JUTE/backend/nodejute/examples/serverSide 16 | ERR! node -v v0.4.5 17 | ERR! npm -v 1.0.22 18 | verbose exit [ 2, true ] 19 | -------------------------------------------------------------------------------- /backend/nodejute/examples/serverSide/testMySum.js: -------------------------------------------------------------------------------- 1 | YUI({ 2 | logInclude: { TestRunner: true }, 3 | }).use('test', function(Y) { 4 | 5 | var suite = new Y.Test.Suite('mySum'), 6 | mySum = require('./examples/serverSide/mySum', true).mySum; 7 | 8 | suite.add(new Y.Test.Case({ 9 | name:'simple sums', 10 | testTwoNumbers: function() { 11 | Y.Assert.areEqual(mySum(5, 5), 10); 12 | }, 13 | 14 | testArray: function() { 15 | Y.log('this is fun'); 16 | Y.Assert.areEqual(mySum([5, 5]), 10); 17 | } 18 | 19 | })); 20 | 21 | Y.Test.Runner.add(suite); 22 | Y.Test.Runner.run(); 23 | 24 | }); 25 | 26 | 27 | -------------------------------------------------------------------------------- /backend/nodejute/getConfig.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | module.exports = (function() { 37 | var fs = require('fs'), config; 38 | 39 | try { 40 | config = JSON.parse(fs.readFileSync('/tmp/jute.config', 'utf8')); 41 | } catch(e) { 42 | console.error('You must start the JUTE server: % npm start jute'); 43 | } 44 | 45 | return function() { return config }; 46 | 47 | })(); 48 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | module.exports = { 37 | 38 | Create: function(hub, common) { 39 | // Javascript is single threaded! We don't have to worry about concurrency! 40 | var glob = require("glob") 41 | , path = require('path') 42 | ; 43 | 44 | hub.on('loadActions', loadActions); 45 | 46 | function loadActions() { 47 | var base = path.join(__dirname, 'actions'); 48 | glob('*.js', { cwd: base }, function (err, actions) { 49 | // Suck in all available actions 50 | if (!err) { 51 | actions.forEach(function(action) { 52 | var act = require(path.join(base, action)); 53 | act.Create && act.Create(hub, common); 54 | }); 55 | hub.emit('actionsLoaded'); 56 | } else { 57 | hub.emit(hub.LOG, hub.ERROR, "Error loading actions: " + err); 58 | process.exit(1); 59 | } 60 | }); 61 | } 62 | } 63 | }; 64 | 65 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/clearResults.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | module.exports = { 38 | Create: function(hub) { 39 | var path = require('path'); 40 | 41 | // Events I care about 42 | hub.addListener('action:clear_results', clearResults); 43 | 44 | function clearResults(req, res) { 45 | var exec = require('child_process').exec; 46 | try { 47 | exec("/bin/rm -rf " + hub.config.outputDir + '/*'); 48 | } catch(e) { 49 | hub.emit(hub.LOG, hub.ERROR, 'Error clearing results: ' + e); 50 | } 51 | res.end('OK'); 52 | } 53 | } 54 | }; 55 | 56 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/clearTests.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | // Should just clear tests for THIS browser (or all tests if from cli?) 38 | module.exports = { 39 | Create: function(hub) { 40 | 41 | // Events I care about 42 | hub.addListener('action:clear_tests', clearTests); 43 | 44 | function clearTests(req, res) { 45 | hub.cache.tests_to_run = []; 46 | res.end('OK'); 47 | } 48 | } 49 | }; 50 | 51 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/common.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | module.exports = { 38 | Create: function(hub) { 39 | var cache = hub.cache, 40 | common = { 41 | browserName: function(req) { 42 | return [req.headers['user-agent'], req.connection.remoteAddress].join('---'); 43 | }, 44 | 45 | makeSaneNames: function(browser) { 46 | var names = browser.split('---'), 47 | filename = names[0], 48 | ip = names[1], 49 | pkgname 50 | ; 51 | 52 | // Get rid of funny chars 53 | filename = filename.replace(/[\/;]/g, ''); 54 | filename = filename.replace(/[^A-Za-z0-9-]/g, '_'); 55 | 56 | // Make a Hudson happy package name 57 | pkgname = filename.replace(/\./g, ''); 58 | pkgname = pkgname.replace(/_+/g, '.'); 59 | 60 | return [ filename, pkgname ]; 61 | }, 62 | 63 | dumpFile: function(vars, dataKey, filename, component) { 64 | var baseOutputDir = hub.config.outputDir, 65 | path = require('path'), 66 | dir = path.join(baseOutputDir, (common.makeSaneNames(component))[0]); 67 | data = vars[dataKey], 68 | fullFile = path.join(dir, filename), 69 | fs = require('fs') 70 | ; 71 | 72 | hub.emit(hub.LOG, hub.INFO, "Dumping " + fullFile); 73 | 74 | // Any one of these can toss cookies!!! 75 | try { 76 | // This will complain if dir already exists 77 | // And we KNOW we can already make dirs here 78 | fs.mkdirSync(dir, 0777); 79 | } catch(e) {} 80 | 81 | try { 82 | var fd = fs.openSync(fullFile, 'w') 83 | fs.writeSync(fd, data, 0, 'utf8'); 84 | fs.closeSync(fd) 85 | return [ fullFile, dir ]; 86 | } catch(e) { 87 | hub.emit(hub.LOG, hub.ERROR, "Error dumping file " + fullFile + ": " + e); 88 | } 89 | }, 90 | failedTests: function(filename) { 91 | var fs = require('fs'), file; 92 | 93 | try { 94 | file = fs.readFileSync(filename, 'utf8'); 95 | return file.match(/failures="[1-9]/); 96 | } catch(e) { 97 | hub.emit(hub.LOG, hub.ERROR, "Error checking for failed unit test: " + e); 98 | return true; 99 | } 100 | 101 | }, 102 | takeSeleniumSnapshot: function(test, component) { 103 | var soda = require('soda'), i 104 | , b = soda.createClient({ host: test.sel_host }) 105 | , filename = path.join(hub.config.outputDir, (common.makeSaneNames(component))[0], 'snapshot.png') 106 | ; 107 | 108 | if (!test.seleniumID) return; 109 | 110 | b.sid = test.seleniumID; 111 | 112 | b.chain.windowFocus().getEval("window.moveTo(1,0); window.resizeTo(screen.availWidth, screen.availHeight);").end(function(err) { 113 | if (!err) { 114 | b.command('captureScreenshotToString', [], function(err, body, res) { 115 | if (!err) { 116 | var msg; 117 | try { 118 | var bb = new Buffer(body, 'base64'), 119 | msg = "Dumped snapshot for " + test.url + ' to ' + filename + "\n"; 120 | fs.writeFileSync(filename, bb, 0, bb.length); 121 | common.addTestOutput(test, msg); 122 | hub.emit(hub.LOG, hub.INFO, msg); 123 | } catch(e) { 124 | msg = "Error dumping snapshot file " + filename + ": " + e + "\n"; 125 | common.addTestOutput(test, msg); 126 | hub.emit(hub.LOG, hub.ERROR, msg); 127 | } 128 | } 129 | hub.emit('action:doneDone', err, test); 130 | }); 131 | } else { 132 | hub.emit('action:doneDone', err, test); 133 | } 134 | }); 135 | }, 136 | addTestOutput: function(test, msg) { 137 | var lines = msg.split(/\n/), 138 | now = new Date(), 139 | output = '', 140 | format; 141 | 142 | if (!test) return; 143 | 144 | format = now.getFullYear() + '/' + (now.getMonth() + 1) + '/' + now.getDate() + ' ' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds(); 145 | lines.forEach(function(line) { 146 | output += '[' + format + '] ' + line + "\n"; 147 | }); 148 | test.output += output; 149 | 150 | if (test.sendOutput && cache.connections[test.browser]) { 151 | // selenium test 152 | cache.connections[test.browser].write(output); 153 | } 154 | if (test.requestKey && cache.connections[test.requestKey]) { 155 | // command line test 156 | cache.connections[test.requestKey].write(output); 157 | } 158 | }, 159 | badUnitTest: function(req, test) { 160 | // Dump a FAILED XML file 161 | // Use test file name as the NAME of this test (vs. component name from test itself) 162 | var parts = test.url.split('/'); 163 | var name = parts.pop(); 164 | name = name.replace(/\..*$/, ''); // get rid of suffix 165 | var names = common.makeSaneNames(common.browserName(req)); 166 | var err = 'Test Timed Out: Most likely a Javascript parsing error - try loading URL in your browser', 167 | err = err.replace('BROWSER', names[1]); 168 | err = err.replace('URL', test.url); 169 | var params = { results: err, name: name }; 170 | var msg = "Dumped error unit test file " + name + " / " + names[0] + " (from " + test.url + ")"; 171 | 172 | hub.emit(hub.LOG, hub.ERROR, msg); 173 | common.addTestOutput(test, msg); 174 | 175 | common.dumpFile(params, 'results', names[0] + '-test.xml', name); 176 | common.dumpFile({ output: test.output }, 'output', names[0] + '.txt', name); 177 | } 178 | }; 179 | 180 | return common; 181 | } 182 | }; 183 | 184 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/getTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | module.exports = { 38 | Create: function(hub, common) { 39 | // Javascript is single threaded! We don't have to worry about concurrency! 40 | var path = require('path'), 41 | cache = hub.cache 42 | ; 43 | 44 | // Events I care about 45 | hub.addListener('action:get_test', getTest); 46 | 47 | function getTest(req, res) { 48 | var browser = req.session.uuid, 49 | bName = common.browserName(req), 50 | now = new Date().getTime(), 51 | testURL; 52 | 53 | if (!cache.browsers[browser]) { 54 | cache.browsers[browser] = {}; 55 | } 56 | 57 | cache.browsers[browser].get_test = now; 58 | 59 | hub.emit(hub.LOG, hub.INFO, 'Getting test for ' + bName); 60 | 61 | for (var i = 0; i < cache.tests_to_run.length; i++) { 62 | var test = cache.tests_to_run[i]; 63 | if (test.browser == browser && test.running) { 64 | // um you're already running this test! 65 | // must be something wrong with it - pop it 66 | var error = 'Skipping bad test: ' + test.url + ': we thought it was running!'; 67 | hub.emit(hub.LOG, hub.ERROR, error); 68 | common.badUnitTest(req, test); 69 | cache.tests_to_run.splice(i, 1); 70 | i--; 71 | continue; 72 | } 73 | 74 | // This test is not for us 75 | if (test.browser && (test.browser != browser)) continue; 76 | 77 | if (!test.browser) { 78 | // A test pre-loaded w/o any browsers listening 79 | // this browser looks like a winner! 80 | test.browser = browser; 81 | } 82 | 83 | // Otherwise start running this test in capture mode!! 84 | common.addTestOutput(test, "To browser " + bName); 85 | test.running = now; 86 | cache.currentTest[browser] = test; 87 | testURL = test.url; 88 | break; 89 | } 90 | 91 | if (testURL) { 92 | res.writeHead(200, { 'Content-Type': 'application/json' }); 93 | res.end(JSON.stringify({ testLocation: testURL })); 94 | hub.emit(hub.LOG, hub.INFO, "Sent test url: " + testURL + ' to ' + bName); 95 | } else { 96 | delete cache.currentTest[browser]; 97 | // find all local tests 98 | var glob = require('glob'), 99 | prefix = hub.config.testDir, 100 | local_test_files = hub.config.testRegex, 101 | data = []; 102 | ; 103 | 104 | // No tests for me - end if we're a Selenium browser 105 | if (req.session.selenium) { 106 | // Selenium job all done!! 107 | hub.emit(req.session.uuid + 'finished'); 108 | } else { 109 | hub.emit('testsDone'); 110 | } 111 | 112 | // ONLY USE HTML FOR NOW UNTIL THE PAGE IS SMARTER... 113 | glob('**/*.html', { cwd: prefix }, function(err, matches_html) { 114 | matches_html.forEach(function(testFile) { 115 | data.push({ test_url: testFile }); 116 | }); 117 | res.writeHead(200, { 'Content-Type': 'application/json' }); 118 | res.end(JSON.stringify({ availableTests: data, config: hub.config })); 119 | }); 120 | } 121 | } 122 | } 123 | }; 124 | 125 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/heartBeat.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | module.exports = { 38 | Create: function(hub, common) { 39 | // Javascript is single threaded! We don't have to worry about concurrency! 40 | var path = require('path'), cache = hub.cache; 41 | 42 | // Events I care about 43 | hub.addListener('action:heart_beat', heartBeat); 44 | 45 | function heartBeat(req, res) { 46 | var id = req.session.uuid; 47 | 48 | // Update heartbeat time 49 | if (!cache.browsers[id]) { 50 | cache.browsers[id] = {}; 51 | } 52 | 53 | cache.browsers[id].heart_beat = new Date().getTime(); 54 | cache.browsers[id].name = common.browserName(req); 55 | 56 | hub.on('action:checkedResults', function(results) { 57 | results.current_status = { browsers: cache.browsers, tests_to_run: cache.tests_to_run }; 58 | results.config = hub.config; 59 | res.writeHead(200, { 'Content-Type': 'application/json' }); 60 | res.end(JSON.stringify(results)); 61 | }); 62 | hub.emit('action:checkResults', req, res); 63 | } 64 | } 65 | }; 66 | 67 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/message.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | module.exports = { 37 | Create: function(hub, common) { 38 | // A test just dumped to console.error 39 | var path = require('path'), 40 | cache = hub.cache; 41 | 42 | // Events I care about 43 | hub.addListener('action:message', message); 44 | 45 | function message(req, res) { 46 | var obj = req.body, 47 | msg = obj.msg, 48 | why = obj.why, 49 | browser = req.session.uuid, test; 50 | 51 | // Find this test 52 | for (var i = 0; i < cache.tests_to_run.length; i++) { 53 | test = cache.tests_to_run[i]; 54 | if (test.browser != browser) continue; 55 | if (test.running) { 56 | common.addTestOutput(test, why + ': ' + msg); 57 | hub.emit(hub.LOG, hub.LOG, 'MESSAGE ' + why + ' from ' + test.url + ': ' + msg); 58 | 59 | break; 60 | } 61 | } 62 | 63 | res.end('OK'); 64 | } 65 | } 66 | }; 67 | 68 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/pop.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | module.exports = { 37 | Create: function(hub) { 38 | // Javascript is single threaded! We don't have to worry about concurrency! 39 | var path = require('path'); 40 | 41 | // Events I care about 42 | hub.addListener('action:pop', pop); 43 | 44 | // THIS IS DANGEROUS!!! 45 | // Take off top test whatever it is 46 | function pop(req, res) { 47 | hub.cache.tests_to_run.shift(); 48 | hub.emit('action:status', req, res); 49 | } 50 | } 51 | }; 52 | 53 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/prune.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | module.exports = { 38 | Create: function(hub, common) { 39 | // Javascript is single threaded! We don't have to worry about concurrency! 40 | var TEST_TIME_THRESHOLD = 60000, // 60 seconds to wait before declaring test dead 41 | BROWSER_TIME_THRESHOLD = 20000, // Delete a captured browser after it has been gone for this long - 20 seconds 42 | path = require('path'), 43 | cache = hub.cache 44 | ; 45 | 46 | // Events I care about 47 | hub.addListener('startAction', prune); 48 | 49 | function prune(doing_what, req, res) { 50 | var redirect; 51 | 52 | if (doing_what != 'status') { 53 | prune_browsers(req); 54 | redirect = prune_tests(doing_what, req); 55 | if (redirect) { 56 | // we're done 57 | res.end(JSON.stringify({ redirect_run_tests: '/jute_docs/run_tests.html' })); 58 | } else { 59 | // keep going 60 | hub.emit('action:' + doing_what, req, res); 61 | } 62 | } else { 63 | hub.emit('action:status', req, res); 64 | } 65 | } 66 | 67 | function prune_tests(doing_what, req) { 68 | var now = new Date().getTime(), 69 | browser = req.session.uuid, test, 70 | timeStarted 71 | ; 72 | 73 | // We're already getting a test - let it slide... 74 | if (doing_what == 'get_test') return; 75 | 76 | // Only check my tests 77 | for (var i = 0; i < cache.tests_to_run.length; i++) { 78 | test = cache.tests_to_run[i]; 79 | if (test.browser != browser) continue; 80 | timeStarted = test.running; 81 | if (timeStarted) { 82 | if (now - timeStarted > TEST_TIME_THRESHOLD) { 83 | // This test has been running for too long!! 84 | var msg = "Test running for too long - killing it"; 85 | 86 | hub.emit(hub.LOG, hub.ERROR, msg); 87 | common.addTestOutput(test, msg); 88 | cache.tests_to_run.splice(i, 1); 89 | common.badUnitTest(req, test); 90 | 91 | // redirect me outta here 92 | return 1; 93 | 94 | } 95 | } 96 | } 97 | if (cache.browsers[browser] && cache.browsers[browser].get_test && (now - cache.browsers[browser].get_test > TEST_TIME_THRESHOLD)) { 98 | // A link test taking too long - these are NOT in cache.tests_to_run 99 | hub.emit(hub.LOG, hub.ERROR, "Test running for too long - killing it"); 100 | 101 | // redirect me outta here 102 | return 1; 103 | } 104 | } 105 | 106 | function prune_browsers(req) { 107 | var me = req.session.uuid, 108 | util = require('util'); 109 | 110 | // only check other browsers 111 | for (browser in cache.browsers) { 112 | var now = new Date().getTime(), 113 | b_time = cache.browsers[browser].heart_beat; 114 | 115 | if (browser == me) continue; 116 | 117 | if (now - b_time > BROWSER_TIME_THRESHOLD) { 118 | hub.emit(hub.LOG, hub.ERROR, "We lost browser " + cache.browsers[browser].name); 119 | delete cache.browsers[browser]; 120 | // take it out of ay tests it's supposed to be running 121 | for (var i = 0; i < cache.tests_to_run.length; i++) { 122 | var test = cache.tests_to_run[i]; 123 | if (test.browser == browser) { 124 | // blow this test out! 125 | hub.emit(hub.LOG, hub.ERROR, "Deleting this test that was part of lost browser: " + util.inspect(test)); 126 | cache.tests_to_run.splice(i, 1); 127 | i--; // fake a perl 'redo'!! Otherwise we might skip over something! 128 | common.badUnitTest(req, test); 129 | } 130 | } 131 | } 132 | } 133 | } 134 | } 135 | }; 136 | 137 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/runTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | module.exports = { 38 | Create: function(hub, common) { 39 | // Javascript is single threaded! We don't have to worry about concurrency! 40 | var path = require('path'), 41 | cache = hub.cache; 42 | 43 | // Events I care about 44 | hub.addListener('action:run_test', runTest); 45 | 46 | function runTest(req, res) { 47 | var uuid = require('node-uuid'), 48 | path = require('path'), 49 | fs = require('fs'), 50 | obj = req.body, 51 | util = require('util'), 52 | tests, multipleFromUI = false, 53 | capture = false, 54 | exec = require('child_process').exec, 55 | errors = [] 56 | ; 57 | 58 | hub.emit(hub.LOG, hub.INFO, 'OBJ: ' + util.inspect(obj)); 59 | if (obj.test) { 60 | multipleFromUI = true; 61 | // 'run multiple' from UI 62 | if (typeof obj.test == 'object') { 63 | tests = obj.test 64 | } else { 65 | tests = [ obj.test ]; 66 | } 67 | } else if (obj.tests) { 68 | // From CLI 69 | tests = obj.tests.split(/\s+/); 70 | } 71 | 72 | if (!tests) { 73 | res.writeHead(302, { Location: "/jute_docs/run_tests.html" }); 74 | res.end("/jute_docs/run_tests.html"); 75 | return; 76 | } 77 | 78 | // FIRST make sure all these alleged test files exist 79 | for (var i = 0; i < tests.length; i++) { 80 | var realFullFile = path.join(hub.config.testDir, tests[i].replace(/\?.*/, '')); 81 | if (realFullFile.match(/\.html$/)) { 82 | capture = true; 83 | } 84 | 85 | try { 86 | fs.statSync(realFullFile); 87 | } catch (e) { 88 | errors.push(realFullFile); 89 | } 90 | } 91 | 92 | if (errors.length > 0) { 93 | res.writeHead(404); 94 | res.end("Cannot find test files: " + errors.join(', ')); 95 | return; 96 | } 97 | 98 | if (!Object.keys(cache.browsers).length && !obj.sel_host && !obj.phantomjs && capture && !obj.load) { 99 | res.writeHead(412); 100 | res.end("There are no currently captured browsers!"); 101 | return; 102 | } 103 | 104 | var pushed = false, v8Tests = '', 105 | requestKey = uuid(), seleniumIDs = []; 106 | 107 | // Generate Selenium IDs 108 | if (obj.sel_host || obj.phantomjs) { 109 | var seleniums = parseInt(obj.seleniums, 10) || parseInt(obj.parallel, 10) || 1; 110 | for (var i = 0; i < seleniums; i++) { 111 | seleniumIDs.push(uuid()); 112 | } 113 | } 114 | 115 | for (var i = 0; i < tests.length; i++) { 116 | var test = tests[i], 117 | test_obj = { 118 | running: 0, 119 | url: path.join('/', hub.config.testDirWeb, test), 120 | output: '', 121 | requestKey: requestKey, 122 | sendOutput: obj.send_output, 123 | retry: parseInt(obj.retry, 10) || 0 124 | }; 125 | 126 | if (test.match(/\.js/)) { 127 | // V8 test! 128 | pushed = true; 129 | exec('JUTE_DEUBG=1 ' + path.join(__dirname, '..', '..', 'jute_v8.js') + ' ' + test + ' 2>&1', function(error, stdout, stderr) { 130 | if (error) { 131 | v8Tests += 'V8 ERROR: ' + error; 132 | } else { 133 | v8Tests += stdout; 134 | } 135 | }); 136 | } else if (obj.sel_host) { 137 | // A Selenium Test! 138 | 139 | // keep this around 140 | test_obj.sel_host = obj.sel_host; 141 | 142 | if (obj.send_output) { 143 | test_obj.sendOutput = 1; 144 | } 145 | if (obj.snapshot) { 146 | test_obj.snapshot = 1; 147 | } 148 | 149 | // Only pass these tests out to selenium hosts started by this 150 | // this is how we keep track 151 | // hand this off to next SeleniumID 152 | console.log('choosing selenium id: ' + (i % seleniumIDs.length)); 153 | test_obj.browser = seleniumIDs[i % seleniumIDs.length]; 154 | 155 | common.addTestOutput(test_obj, 'Selenium test'); 156 | 157 | cache.tests_to_run.push(test_obj); 158 | pushed = true; 159 | } else if (obj.phantomjs) { 160 | if (obj.send_output) { 161 | test_obj.sendOutput = 1; 162 | } 163 | if (obj.snapshot) { 164 | test_obj.snapshot = 1; 165 | } 166 | 167 | // Only pass these tests out to phantomjs instances started by this 168 | // this is how we keep track 169 | // hand this off to next SeleniumID 170 | console.log('choosing phantomjs id: ' + (i % seleniumIDs.length)); 171 | test_obj.browser = seleniumIDs[i % seleniumIDs.length]; 172 | 173 | common.addTestOutput(test_obj, 'PhantomJS test'); 174 | 175 | cache.tests_to_run.push(test_obj); 176 | pushed = true; 177 | } else { 178 | if (multipleFromUI) { 179 | // Only run these tests in THIS browser from the UI 180 | 181 | pushed = true; 182 | for (var browser in cache.browsers) { 183 | (function(b) { 184 | hub.emit(hub.LOG, hub.INFO, 'Adding this test to zob: ' + b); 185 | var obj = JSON.parse(JSON.stringify(test_obj)); 186 | obj.browser = b; 187 | common.addTestOutput(obj, 'Capture test'); 188 | cache.tests_to_run.push(obj); 189 | }(browser)); 190 | } 191 | } else { 192 | // Send to each test to each captured browser 193 | if (!obj.load) { 194 | pushed = true; 195 | for (var browser in cache.browsers) { 196 | (function(b) { 197 | hub.emit(hub.LOG, hub.INFO, 'Adding this test to zob: ' + b); 198 | var obj = JSON.parse(JSON.stringify(test_obj)); 199 | obj.browser = b; 200 | common.addTestOutput(obj, 'Capture test'); 201 | cache.tests_to_run.push(obj); 202 | }(browser)); 203 | } 204 | } else { 205 | common.addTestOutput(test_obj, 'Loading this test for any browser'); 206 | cache.tests_to_run.push(test_obj); 207 | } 208 | } 209 | } 210 | 211 | common.addTestOutput(test_obj, util.inspect(test_obj)); 212 | } 213 | 214 | if (pushed) { 215 | if (obj.sel_host) { 216 | // Start up for a Selenium browser & Listen for results 217 | var totalError = ''; 218 | hub.on('action:seleniumDone', function(err, selID) { 219 | seleniumIDs.pop(); // a selenium browser finished - we don't really care which one 220 | // as we're just waiting for all to finish 221 | var done = !seleniumIDs.length; 222 | if (done) hub.removeListener('action:seleniumDone', arguments.callee); 223 | 224 | if (err) { 225 | hub.emit(hub.LOG, hub.ERROR, 'ERROR running Selenium tests (' + selID + '): ' + err); 226 | totalError += err; 227 | if (done) { 228 | res.end(totalError); 229 | } 230 | } else { 231 | if (done) { 232 | hub.once('action:checkedResults', function(results) { 233 | res.end('Final Selenium Results: ' + JSON.stringify(results)); 234 | }); 235 | hub.emit('action:checkResults'); 236 | } 237 | } 238 | }); 239 | 240 | seleniumIDs.forEach(function(selID) { 241 | if (obj.sel2) { 242 | hub.emit('action:selenium2Start', selID, req, res); 243 | } else { 244 | hub.emit('action:seleniumStart', selID, req, res); 245 | } 246 | }); 247 | } else if (obj.phantomjs) { 248 | // Start up for a Selenium browser & Listen for results 249 | var totalError = ''; 250 | hub.on('action:phantomjsDone', function(err, selID) { 251 | seleniumIDs.pop(); // a selenium browser finished - we don't really care which one 252 | // as we're just waiting for all to finish 253 | var done = !seleniumIDs.length; 254 | if (done) hub.removeListener('action:phantomjsDone', arguments.callee); 255 | 256 | if (err) { 257 | hub.emit(hub.LOG, hub.ERROR, 'ERROR running PhantomJS tests (' + selID + '): ' + err); 258 | totalError += err; 259 | if (done) { 260 | res.end(totalError); 261 | } 262 | } else { 263 | if (done) { 264 | hub.once('action:checkedResults', function(results) { 265 | res.end('Final PhantomJS Results: ' + JSON.stringify(results)); 266 | }); 267 | hub.emit('action:checkResults'); 268 | } 269 | } 270 | }); 271 | 272 | seleniumIDs.forEach(function(selID) { 273 | hub.emit('action:phantomjsStart', selID, req, res); 274 | }); 275 | } else { 276 | // UI wants to run multiple tests - redirect to it! 277 | if (multipleFromUI) { 278 | // Now tell browser to run the tests! 279 | res.writeHead(302, { Location: "/jute_docs/run_tests.html" }); 280 | res.end("/jute_docs/run_tests.html"); 281 | } else { 282 | // Command line client 283 | if (v8Tests) { 284 | res.write(v8Tests); 285 | } 286 | if (obj.wait) { 287 | cache.connections[requestKey] = res; // our link back to the requesting client for status messages 288 | hub.once('testsDone', function() { 289 | delete cache.connections[requestKey]; // our link back to the requesting client for status messages 290 | res.end('all done!'); 291 | }); 292 | } else if (capture) { 293 | res.end('Added ' + (obj.test || obj.tests) + ' to capture/load tests'); 294 | } 295 | } 296 | } 297 | } else { 298 | if (obj.load) { 299 | res.write('All tests loading and waiting for a browser'); 300 | if (obj.wait) { 301 | cache.connections[requestKey] = res; // our link back to the requesting client for status messages 302 | hub.once('testsDone', function() { 303 | delete cache.connections[requestKey]; // our link back to the requesting client for status messages 304 | res.end('all done from load!'); 305 | }); 306 | } else { 307 | res.end(''); 308 | } 309 | } else { 310 | hub.emit(hub.LOG, hub.ERROR, "No browsers listening!"); 311 | res.statusCode = 412; // Ye Olde Failed Precondition 312 | res.end('No browsers listening!! Test(s) not added!'); 313 | } 314 | } 315 | } 316 | } 317 | }; 318 | 319 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/startPhantomjs.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | module.exports = { 38 | Create: function(hub, common) { 39 | // Javascript is single threaded! We don't have to worry about concurrency! 40 | var path = require('path'), 41 | fs = require('fs'), 42 | child = require('child_process'), 43 | cache = hub.cache 44 | ; 45 | 46 | 47 | // Events I care about 48 | hub.addListener('action:phantomjsStart', startPhantomjs); 49 | 50 | function startPhantomjs(selID, req, res) { 51 | var cb, phantom, body = req.body, 52 | phantomjs = hub.config.phantomjs, 53 | url = 'http://' + (hub.config.host ? hub.config.host + ':' + hub.config.port : req.headers.host) + '/?selenium=' + selID 54 | ; 55 | 56 | try { 57 | hub.emit(hub.LOG, hub.INFO, phantomjs + ' ' + path.join(__dirname, '..', "phantomJUTE.js") + ' ' + url); 58 | phantom = child.spawn(phantomjs, [ path.join(__dirname, '..', "phantomJUTE.js"), url, hub.config.outputDir]); 59 | phantom.stdout.on('data', function(data) { 60 | if (data === 'snapshot') { 61 | hub.emit(hub.LOG, hub.INFO, "SNAPSHOT!"); 62 | } 63 | hub.emit(hub.LOG, hub.INFO, "PhantomJS sez: " + data); 64 | common.addTestOutput(cache.currentTest[selID], 'PhantomJS console: ' + data); 65 | }); 66 | phantom.stderr.on('data', function(data) { 67 | hub.emit(hub.LOG, hub.ERROR, "PhantomJS stderr: " + data); 68 | common.addTestOutput(cache.currentTest[selID], 'PhantomJS error: ' + data); 69 | }); 70 | phantom.on('exit', function() { 71 | if (!phantom.done) { 72 | hub.emit(hub.LOG, hub.ERROR, "PhantomJS exited unexpectedly"); 73 | cb('PhantomJS executable exited unexpectedly'); 74 | } 75 | }); 76 | 77 | } catch(e) { 78 | hub.emit('action:phantomjsDone', 'Cannot start up phantomjs at ' + phantomjs + ': ' + e, selID); 79 | return; 80 | } 81 | 82 | // Give Selenium 1000 minutes to finish - should be good - 16 hours baby! 83 | req.socket.setTimeout(6000000, function() { 84 | hub.emit(hub.LOG, hub.ERROR, 'Phantomjs taking too long - giving up'); 85 | cb('took too long!'); 86 | }); 87 | 88 | cache.connections[selID] = res; // our link back to the requesting client for status messages 89 | 90 | // called when all PhantomJS tests are complete for this instance 91 | // && keep track of requesting client for debug messages back... 92 | // Callback for when the phantomjs process is done 93 | cb = function(err) { 94 | hub.emit(hub.LOG, hub.INFO, 'Phantomjs done!'); 95 | phantom.done = true; 96 | phantom.kill(); 97 | delete cache.connections[selID]; // done with status updates 98 | hub.emit('action:phantomjsDone', err, selID); 99 | }; 100 | hub.once(selID + 'finished', cb); 101 | } 102 | } 103 | }; 104 | 105 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/startSelenium.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | module.exports = { 38 | Create: function(hub) { 39 | // Javascript is single threaded! We don't have to worry about concurrency! 40 | var path = require('path'), 41 | fs = require('fs'), 42 | cache = hub.cache 43 | ; 44 | 45 | // Events I care about 46 | hub.addListener('action:seleniumStart', startSelenium); 47 | 48 | function startSelenium(selID, req, res) { 49 | var soda = require('soda'), cb, 50 | body = req.body, 51 | browser 52 | ; 53 | 54 | try { 55 | browser = soda.createClient({ 56 | url: 'http://' + (hub.config.host ? hub.config.host + ':' + hub.config.port : req.headers.host), 57 | host: body.sel_host, 58 | browser: body.sel_browser 59 | }) 60 | } catch(e) { 61 | hub.emit('action:seleniumDone', 'Cannot connect to Selenium server at ' + body.sel_host + ': ' + e, selID); 62 | return; 63 | } 64 | 65 | // Give Selenium 1000 minutes to finish - should be good - 16 hours baby! 66 | req.socket.setTimeout(60000000, function() { 67 | hub.emit(hub.LOG, hub.ERROR, 'Selenium taking too long - giving up'); 68 | cb(); 69 | }); 70 | 71 | cache.connections[selID] = res; // our link back to the requesting client for status messages 72 | 73 | // called when all Selenium tests are complete for this browser 74 | // && keep track of requesting client for debug messages back... 75 | // Callback for when the Selenium session is done 76 | cb = function(err) { 77 | if (!err) { 78 | browser.chain.testComplete().end(function(err) { 79 | delete cache.connections[selID]; // done with status updates 80 | hub.emit('action:seleniumDone', err, selID); 81 | }); 82 | } else { 83 | hub.emit('action:seleniumDone', err, selID); 84 | } 85 | }; 86 | cb = hub.once(selID + 'finished', cb); 87 | 88 | browser. 89 | chain. 90 | session(). 91 | open('/?selenium=' + selID). 92 | waitForPageToLoad(10000). 93 | end(function(err) { 94 | if (err) { 95 | var msg = 'Error starting/waiting for Selenium page to load: ' + err; 96 | hub.emit('seleniumTestsFinished', err); 97 | } else { 98 | hub.emit(hub.LOG, hub.INFO, "Selenium up and running: " + browser.sid); 99 | // If this is one of the tests that are going to run in the 100 | // Selenium session, tag it with the Selenium token 101 | cache.tests_to_run.forEach(function(test) { 102 | if (test.browser === selID) { 103 | test.seleniumID = browser.sid; 104 | } 105 | }); 106 | 107 | } 108 | }); 109 | } 110 | } 111 | 112 | }; 113 | 114 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/startSelenium2.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | module.exports = { 38 | Create: function(hub, common) { 39 | // Javascript is single threaded! We don't have to worry about concurrency! 40 | var path = require('path'), 41 | fs = require('fs'), 42 | cache = hub.cache 43 | ; 44 | 45 | // Events I care about 46 | hub.addListener('action:selenium2Start', startSelenium); 47 | 48 | function startSelenium(selID, req, res) { 49 | var webdriverio = require("webdriverio"), cb, 50 | body = req.body, browser 51 | , url = 'http://' + (hub.config.host ? hub.config.host + ':' + hub.config.port : req.headers.host) 52 | ; 53 | 54 | try { 55 | browser = webdriverio.remote({ 56 | host: body.sel_host, 57 | port: body.sel_port || 4444, 58 | desiredCapabilities: { browserName: body.sel_browser || 'firefox' } 59 | }); 60 | 61 | } catch(e) { 62 | hub.emit('action:seleniumDone', 'Cannot connect to Selenium server at ' + body.sel_host + ': ' + e, selID); 63 | return; 64 | } 65 | 66 | // Give Selenium 1000 minutes to finish - should be good - 16 hours baby! 67 | req.socket.setTimeout(60000000, function() { 68 | hub.emit(hub.LOG, hub.ERROR, 'Selenium taking too long - giving up'); 69 | cb(); 70 | }); 71 | 72 | cache.connections[selID] = res; // our link back to the requesting client for status messages 73 | 74 | // called when all Selenium tests are complete for this browser 75 | // && keep track of requesting client for debug messages back... 76 | // Callback for when the Selenium session is done 77 | cb = function(err) { 78 | hub.removeAllListeners(selID + 'snapshot'); 79 | if (!err) { 80 | browser.end(function() { 81 | delete cache.connections[selID]; // done with status updates 82 | hub.emit('action:seleniumDone', err, selID); 83 | }); 84 | } else { 85 | hub.emit('action:seleniumDone', err, selID); 86 | } 87 | }; 88 | cb = hub.once(selID + 'finished', cb); 89 | 90 | hub.on(selID + 'snapshot', function(test, component) { 91 | browser.screenshot(function(screenshot) { 92 | var path = require('path') 93 | , filename = path.join(hub.config.outputDir, (common.makeSaneNames(component))[0], 'snapshot.png'); 94 | 95 | try { 96 | var bb = new Buffer(screenshot.value, 'base64'), 97 | msg = "Dumped snapshot for " + test.url + ' to ' + filename + "\n"; 98 | fs.writeFileSync(filename, bb, 0, bb.length); 99 | common.addTestOutput(test, msg); 100 | hub.emit(hub.LOG, hub.INFO, msg); 101 | } catch(e) { 102 | msg = "Error dumping snapshot file " + filename + ": " + e + "\n"; 103 | common.addTestOutput(test, msg); 104 | hub.emit(hub.LOG, hub.ERROR, msg); 105 | } 106 | 107 | hub.emit('action:doneDone', null, test); 108 | }); 109 | }); 110 | 111 | browser 112 | .init(function() { 113 | // If this is one of the tests that are going to run in the 114 | // Selenium session, tag it with the Selenium token 115 | cache.tests_to_run.forEach(function(test) { 116 | if (test.browser === selID) { 117 | test.seleniumID = browser.sessionId; 118 | test.sel2 = true; 119 | } 120 | }); 121 | }) 122 | .url(url + '/?selenium=' + selID); 123 | } 124 | } 125 | 126 | }; 127 | 128 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/status.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | module.exports = { 38 | Create: function(hub, common) { 39 | // Javascript is single threaded! We don't have to worry about concurrency! 40 | var path = require('path'), 41 | cache = hub.cache; 42 | 43 | // Events I care about 44 | hub.addListener('action:checkResults', checkResults); 45 | 46 | hub.addListener('action:status', function(req, res) { 47 | hub.once('action:checkedResults', function(results) { 48 | results.current_status = { browsers: cache.browsers, tests_to_run: cache.tests_to_run }; 49 | results.config = hub.config; 50 | res.end(JSON.stringify(results)); 51 | }); 52 | hub.emit('action:checkResults'); 53 | }); 54 | 55 | function checkResults() { 56 | var baseDir = hub.config.outputDir, 57 | fs = require('fs'), 58 | path = require('path'), 59 | ret = { current_results: {} }; 60 | 61 | try { 62 | // Find & parse all results 63 | var components = fs.readdirSync(baseDir); 64 | 65 | if (components.length) { 66 | components.forEach(function(component) { 67 | doComp(ret, component, function() { 68 | if (Object.keys(ret.current_results).length == components.length) { 69 | hub.emit('action:checkedResults', ret); 70 | } 71 | }); 72 | }); 73 | } else { 74 | hub.emit('action:checkedResults', ret); 75 | } 76 | } catch(e) { 77 | hub.emit(hub.LOG, hub.ERROR, 'Error getting current results from ' + baseDir + ': ' + e); 78 | hub.emit('action:checkedResults', ret); 79 | } 80 | } 81 | 82 | function doComp(ret, component, cb) { 83 | var testFiles, testResults = [], compDir, 84 | glob = require('glob'), 85 | fs = require('fs'), 86 | baseDir = hub.config.outputDir; 87 | 88 | component = path.basename(component); 89 | compDir = path.join(baseDir, component); 90 | 91 | // Find all the various output files 92 | glob('**/*.txt', { cwd: compDir }, function(err, debugFiles) { 93 | glob('**/*.png', { cwd: compDir }, function(err, snapshotFiles) { 94 | glob('**/*.xml', { cwd: compDir }, function(err, testFiles) { 95 | // Determined if failed or not 96 | if (!err) { 97 | if (testFiles && testFiles.length) { 98 | testFiles.forEach(function(testFile) { 99 | if (common.failedTests(path.join(compDir, testFile))) { 100 | testResults.push({ name: testFile, failed: 1 }); 101 | } else { 102 | testResults.push({ name: testFile, failed: 0 }); 103 | } 104 | }); 105 | } 106 | 107 | try { 108 | var coverage = fs.existsSync(path.join(baseDir, component, 'lcov-report')); 109 | ret.current_results[component] = {}; 110 | ret.current_results[component].test_results = testResults; 111 | ret.current_results[component].coverage = coverage; 112 | ret.current_results[component].debugFiles = debugFiles.map(function(f) { return f; }); 113 | ret.current_results[component].snapshotFiles = snapshotFiles.map(function(f) { return f; }); 114 | } catch(e) { 115 | hub.emit(hub.LOG, hub.ERROR, 'Error checking for coverage path: ' + e); 116 | } 117 | } else { 118 | hub.emit(hub.LOG, hub.ERROR, 'Error getting current output files: ' + err); 119 | } 120 | cb(); 121 | }); 122 | }); 123 | }); 124 | } 125 | } 126 | }; 127 | 128 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/testReport.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | module.exports = { 38 | Create: function(hub, common) { 39 | // Javascript is single threaded! We don't have to worry about concurrency! 40 | var path = require('path'), 41 | cache = hub.cache; 42 | 43 | // Events I care about 44 | hub.addListener('action:test_report', testReport); 45 | hub.addListener('testOutputDone', testOutputDone); 46 | 47 | function testReport(req, res) { 48 | var obj = req.body, 49 | succeeded = true, 50 | names = common.makeSaneNames(common.browserName(req)), 51 | filename = names[0], 52 | pkgname = names[1], 53 | output = '', 54 | exec = require('child_process').exec 55 | ; 56 | 57 | // obj = { results: , name: , coverage: } 58 | obj.results = obj.results.replace(/testsuite name="/g, 'testsuite name="' + pkgname + '.'); 59 | 60 | if (obj.name) { 61 | names = common.dumpFile(obj, 'results', filename + '-test.xml', obj.name); 62 | if (common.failedTests(names[0])) { 63 | succeeded = false; 64 | } 65 | hub.emit(hub.LOG, hub.INFO, "Test Report for " + obj.name); 66 | output += "Test Report for " + obj.name + "\n"; 67 | output += 'It: ' + (succeeded ? 'succeeded' : 'failed') + "\n"; 68 | 69 | } 70 | 71 | if (obj.log) { 72 | var log = JSON.parse(obj.log); 73 | output += "------------ START CONSOLE OUTPUT ------------\n"; 74 | log.forEach(function(msg) { 75 | output += msg.msg + "\n"; 76 | }); 77 | output += "------------ END CONSOLE OUTPUT ------------\n"; 78 | } 79 | 80 | if (obj.coverage && obj.coverage !== 'null') { 81 | try { 82 | var cover_obj = JSON.parse(obj.coverage); 83 | for (file in cover_obj) { 84 | var new_file = path.join(hub.config.outputDir, obj.name, 'lcov-report', file); 85 | cover_obj[new_file] = cover_obj[file]; 86 | delete cover_obj[file]; 87 | } 88 | obj.coverage = JSON.stringify(cover_obj); 89 | var namez = common.dumpFile(obj, 'coverage', 'cover.json', obj.name); 90 | exec(hub.config.java + ' -jar ' + path.join(__dirname, "yuitest-coverage-report.jar") + " -o " + namez[1] + " --format lcov " + namez[0], 91 | function(error, stdout, stderr) { 92 | var msg; 93 | if (error) { 94 | msg = "Error generating cooverage Report for " + obj.name + ': ' + error + "\n"; 95 | output += msg; 96 | hub.emit(hub.LOG, hub.ERROR, msg); 97 | } else { 98 | msg = "Generated cooverage Report for " + obj.name + "\n"; 99 | hub.emit(hub.LOG, hub.INFO, msg); 100 | output += msg; 101 | 102 | /// deteremine coverage 103 | for (file in cover_obj) { 104 | cover = cover_obj[file]; 105 | total_lines = cover.coveredLines; 106 | total_functions = cover.coveredFunctions; 107 | 108 | if (total_lines) { 109 | line_coverage = Math.round((cover.calledLines / total_lines) * 100); 110 | output += 'Line coverage for ' + path.basename(file) + ': ' + line_coverage + '%\n'; 111 | } 112 | 113 | if (total_functions) { 114 | func_coverage = Math.round((cover.calledFunctions / total_functions) * 100); 115 | output += 'Function coverage for ' + path.basename(file) + ': ' + func_coverage + '%\n'; 116 | } 117 | } 118 | ///// determine coverage 119 | } 120 | 121 | hub.emit('testOutputDone', req, res, succeeded, output); 122 | } 123 | ); 124 | 125 | } catch(e) { 126 | hub.emit(hub.LOG, hub.ERROR, "Error generating coverage report: " + e); 127 | output += "Error generating coverage report: " + e; 128 | hub.emit('testOutputDone', req, res, succeeded, output); 129 | } 130 | } else { 131 | hub.emit('testOutputDone', req, res, succeeded, output); 132 | } 133 | } 134 | 135 | function testOutputDone(req, res, succeeded, output) { 136 | // Take this test out of circulation 137 | var totalTests = cache.tests_to_run.length, 138 | names = common.makeSaneNames(common.browserName(req)), 139 | now = new Date().getTime(), 140 | obj = req.body; 141 | 142 | for (var i = 0; i < totalTests; i++) { 143 | var test = cache.tests_to_run[i]; 144 | 145 | if (test.browser == req.session.uuid) { 146 | // This is the test that just finished 147 | hub.once('action:doneDone', function(err, test) { 148 | if (err) { 149 | hub.emit(hub.LOG, hub.ERROR, err); 150 | } else { 151 | hub.emit(hub.LOG, hub.INFO, 'Test finished: ' + test.url); 152 | } 153 | common.dumpFile({ output: test.output }, 'output', path.basename(names[0], 'xml') + '.txt', obj.name); 154 | res.writeHead(200, { 'Content-Type': 'text/plain' }); 155 | res.end('OK'); 156 | }); 157 | 158 | // Clear this test out 159 | common.addTestOutput(test, output); 160 | common.addTestOutput(test, obj.name + " finished - it " + 161 | (succeeded ? 'SUCCEEDED' : 'FAILED') + ' - it took ' + 162 | (now - test.running) + "ms\n"); 163 | 164 | if (test.retry && !succeeded) { 165 | common.addTestOutput(test, 'Test failed - trying it ' + test.retry + ' more times'); 166 | common.addTestOutput(test, '--------------------------------------------------------------------------------------'); 167 | test.retry--; 168 | test.running = 0; 169 | } else { 170 | cache.tests_to_run.splice(i, 1); 171 | } 172 | 173 | // Take a snapshot & wait or we're done - always if 'snapshot' is set otherwise 174 | // only if a test fails 175 | if ((!succeeded || test.snapshot) && req.session.selenium) { 176 | hub.emit(hub.LOG, hub.INFO, 'Taking a Selenium snapshot of: ' + test.url); 177 | if (test.sel2) { 178 | hub.emit(test.browser + 'snapshot', test, obj.name); 179 | } else { 180 | common.takeSeleniumSnapshot(test, obj.name); 181 | } 182 | } else { 183 | hub.emit('action:doneDone', null, test); 184 | } 185 | break; 186 | } 187 | } 188 | 189 | if (!totalTests) { 190 | // A single browser test 191 | output += obj.name + " finished - it " + (succeeded ? 'SUCCEEDED' : 'FAILED') + "\n"; 192 | common.dumpFile({ output: output }, 'output', path.basename(names[0], 'xml') + '.txt', obj.name); 193 | res.end('OK'); 194 | } 195 | 196 | } 197 | } 198 | }; 199 | 200 | -------------------------------------------------------------------------------- /backend/nodejute/jute/actions/yuitest-coverage-report.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzo/JUTE/77527d0a377077038b2b81aabe8f6b8d2a37f90a/backend/nodejute/jute/actions/yuitest-coverage-report.jar -------------------------------------------------------------------------------- /backend/nodejute/jute/configure.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | module.exports = { 38 | Create: function(hub) { 39 | path = require('path'); 40 | 41 | // Events I care about 42 | hub.addListener('configure', configure); 43 | 44 | function configure() { 45 | 46 | var config = { 47 | port: 5883, 48 | docRoot: '/var/www', 49 | testDir: 'test/', 50 | outputDir: 'output/', 51 | java: '', 52 | logFormat: '', 53 | logFile: '/tmp/jute.log', 54 | testRegex: '.html$', 55 | inject: 1, 56 | phantomjs: '/usr/local/bin/phantomjs', 57 | host: '' 58 | }, 59 | exec = require('child_process').exec, 60 | fs = require('fs'); 61 | 62 | hub.config = config; 63 | 64 | // Suck in NPM config variables 65 | for (var key in config) { 66 | var val = process.env['npm_package_config_' + key]; 67 | if (val) { 68 | config[key] = val; 69 | } 70 | } 71 | 72 | try { 73 | config.logFD = fs.openSync(config.logFile, "w"); 74 | } catch(e) { 75 | console.error('Cannot open logilfe: ' + e); 76 | hub.emit('configureError', { name: 'logFile', value: config.logFile, error: e } ); 77 | return; 78 | } 79 | 80 | try { 81 | var stat = fs.statSync(config.docRoot); 82 | if (!stat.isDirectory()) { 83 | throw 'foobie'; 84 | } 85 | } catch(e) { 86 | hub.emit(hub.LOG, hub.ERROR, "** " + config.docRoot + " does not exist or is not a directory!! **"); 87 | hub.emit(hub.LOG, hub.ERROR, "Set it properly: npm config set jute:docRoot "); 88 | hub.emit('configureError', { name: 'docRoot', value: config.docRoot, error: e } ); 89 | return; 90 | } 91 | 92 | // Web paths and full paths... 93 | config.outputDirWeb = config.outputDir; 94 | config.outputDir = path.join(config.docRoot, config.outputDir); 95 | try { 96 | fs.statSync(config.outputDir); 97 | } catch(e) { 98 | try { 99 | fs.mkdirSync(config.outputDir, 0777) 100 | } catch(e2) { 101 | hub.emit(hub.LOG, hub.ERROR, "Cannot make output dir: " + e2); 102 | hub.emit('configureError', { name: 'outputDir', value: config.outputDir, error: e2 } ); 103 | return; 104 | } 105 | } 106 | 107 | config.testDirWeb = config.testDir; 108 | if (!config.testDirWeb.match(/\/$/)) { 109 | config.testDirWeb += '/'; 110 | } 111 | 112 | config.testDir = path.join(config.docRoot, config.testDir); 113 | 114 | try { 115 | fs.statSync(config.testDir); 116 | } catch(e) { 117 | try { 118 | fs.mkdirSync(config.testDir, 0777) 119 | } catch(e2) { 120 | hub.emit(hub.LOG, hub.ERROR, "Cannot make test dir: " + e2); 121 | hub.emit('configureError', { name: 'testDir', value: config.testDir, error: e2 } ); 122 | return; 123 | } 124 | } 125 | 126 | // Find Java executable 127 | if (!config.java) { 128 | exec('which java', function (error, stdout, stderr) { 129 | if (!error) { 130 | config.java = stdout.trim(); 131 | } 132 | try { 133 | var stat = fs.statSync(config.java); 134 | if (!stat.isFile()) { 135 | throw 'foobie'; 136 | } 137 | } catch(e) { 138 | hub.emit(hub.LOG, hub.ERROR, '** Cannot find "java" executable in PATH **'); 139 | hub.emit(hub.LOG, hub.ERROR, 'Set the "java" configuration variable (% npm config set jute:java )'); 140 | hub.emit(hub.LOG, hub.ERROR, 'Or add the "java" executable to your PATH'); 141 | hub.emit('configureError', { name: 'java', value: config.java, error: e } ); 142 | return; 143 | } 144 | }); 145 | } else { 146 | try { 147 | var stat = fs.statSync(config.java); 148 | if (!stat.isFile()) { 149 | throw 'foobie'; 150 | } 151 | } catch(e) { 152 | hub.emit(hub.LOG, hub.ERROR, '** Cannot find "java" executable **'); 153 | hub.emit(hub.LOG, hub.ERROR, 'NPM java config variable improperly set: ' + config.java); 154 | hub.emit('configureError', { name: 'java', value: config.java, error: e } ); 155 | return; 156 | } 157 | } 158 | 159 | // Make sure output directory is writable for grins... 160 | var testDir = path.join(config.outputDir, 'foo'); 161 | try { 162 | fs.mkdirSync(testDir, 0777); 163 | } catch(e) { 164 | hub.emit(hub.LOG, hub.ERROR, "** Output directory '" + config.outputDir + "' not writable or does not exist!! **"); 165 | hub.emit(hub.LOG, hub.ERROR, "Note outputDir is RELATIVE to docRoot!!"); 166 | hub.emit(hub.LOG, hub.ERROR, "Change output dir using: % npm conifg set jute:outputDir "); 167 | hub.emit(hub.LOG, hub.ERROR, "Or make " + config.outputDir + ' writable by user/group running jute'); 168 | hub.emit('configureError', { name: 'outputDir', value: config.outputDir, error: e } ); 169 | return; 170 | } 171 | 172 | try { 173 | fs.rmdirSync(testDir); 174 | } catch(e) {} 175 | 176 | // All is cool - stash config & move on 177 | hub.config = config; 178 | hub.emit('configureDone', config); 179 | } 180 | } 181 | }; 182 | 183 | -------------------------------------------------------------------------------- /backend/nodejute/jute/daemon.js: -------------------------------------------------------------------------------- 1 | #!env node 2 | 3 | var opt = require( "optimist"), 4 | fs = require('fs'), 5 | args = opt 6 | .usage('Usage: $0 [ --start ] [ --stop ] [ --restart ] [ --kill ]') 7 | .alias('s', 'start') 8 | .alias('p', 'stop') 9 | .alias('r', 'restart') 10 | .alias('k', 'kill') 11 | .describe('start', 'Start JUTE') 12 | .describe('stop', 'Stop JUTE') 13 | .argv, 14 | pidfile = process.env['npm_package_config_pidfile'] || '/tmp/jute.pid' 15 | user = process.env['npm_package_config_asuser'] || '' 16 | ; 17 | 18 | var daemon = require("daemonize2").setup({ 19 | main: "../jute_backend.js" 20 | , name: "jute" 21 | , pidfile: pidfile 22 | , user: user 23 | }); 24 | 25 | if (args.start) { 26 | daemon.start().once("started", function() { 27 | process.exit(); 28 | }); 29 | } else if (args.stop) { 30 | daemon.stop(); 31 | } else if (args.restart) { 32 | if (daemon.status()) { 33 | daemon.stop().once("stopped", function() { 34 | daemon.start().once("started", function() { 35 | process.exit(); 36 | }); 37 | }); 38 | } else { 39 | daemon.start().once("started", function() { 40 | process.exit(); 41 | }); 42 | } 43 | } else if (args.kill) { 44 | daemon.kill(); 45 | } else { 46 | console.error("You must specify 'start' or 'stop'!"); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /backend/nodejute/jute/jute_docs/capture.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | JUTE - Javascript Unit Test Environment 8 | 103 | 104 | 105 | 106 |
107 | 108 |

JUTE

109 |
110 | 111 | 112 | 113 |
114 | 115 |
116 |
117 | 118 |
119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 134 |
135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /backend/nodejute/jute/jute_docs/jute.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | YUI().add('jute', function(Y) { 37 | var UT = Y.namespace('UnitTest'), button, load_button, kick_button, clear_button, clear_results; 38 | 39 | UT.heartBeat = function() { 40 | var cfg = { 41 | method: 'GET', 42 | on: { 43 | success: function (transactionid, response) { 44 | var obj, current_status, current_results, status_output, result_output, i, 45 | test, color, errorCount = 0; 46 | try { 47 | obj = Y.JSON.parse(response.responseText); 48 | if (typeof(obj.redirect_run_tests) === 'string') { 49 | // kick the run_tests iframe 50 | Y.one('#run_tests').setAttribute('src', obj.redirect_run_tests); 51 | } else { 52 | current_status = obj.current_status; 53 | current_results = obj.current_results; 54 | 55 | // Make test/browser output pretty 56 | status_output = '

Status

  • Browsers
    • '; 57 | Y.each(current_status.browsers, 58 | function(value, key, object) { 59 | status_output += '
    • ' + value.name + '
    • '; 60 | status_output += '
      • heart beat ' + value.heart_beat + '
      • '; 61 | status_output += '
      • get test ' + value.get_test + '
      '; 62 | } 63 | ); 64 | status_output += '
    '; 65 | 66 | status_output += '
  • Tests
    • '; 67 | for (i = 0; i < current_status.tests_to_run.length; i = i + 1) { 68 | test = current_status.tests_to_run[i]; 69 | color = ''; 70 | if (test.running > 0) { 71 | color = 'style="background-color:yellow"'; 72 | } 73 | status_output += '
    • Test
      • '; 74 | status_output += '
      • Test: ' + test.url + '
      • '; 75 | status_output += '
      • Browser: ' + test.browser + '
      • '; 76 | status_output += '
      • Running: ' + test.running + '
      '; 77 | } 78 | status_output += '
    '; 79 | status_output += '
'; 80 | Y.one('#list').setContent(status_output); 81 | 82 | // Make result output pretty 83 | result_output = '

Results

    '; 84 | if (current_results) { 85 | if (current_results['lcov-report']) { 86 | result_output += '
  • Total Coverage
  • '; 87 | } 88 | Y.each(current_results, 89 | function(value, key, object) { 90 | var file, color, found = 0, show; 91 | 92 | if (key === 'lcov.info' || key === 'lcov-report') { 93 | return; 94 | } 95 | 96 | result_output += '
  • ' + key + '
    • '; 97 | if (value.test_results) { 98 | for (i = 0; i < value.test_results.length; i = i + 1) { 99 | file = value.test_results[i].name; 100 | color = value.test_results[i].failed ? 'red' : 'green'; 101 | show = file; 102 | show = show.replace(/-test.xml$/, ''); 103 | result_output += '
    • ' + show + '
    • '; 104 | if (value.test_results[i].failed && !found) { 105 | found = 1; 106 | errorCount +=1; 107 | } 108 | } 109 | } 110 | if (value.snapshotFiles) { 111 | for (i = 0; i < value.snapshotFiles.length; i = i + 1) { 112 | file = value.snapshotFiles[i]; 113 | result_output += '
    • SNAPSHOT ' + file + '
    • '; 114 | } 115 | } 116 | if (value.debugFiles) { 117 | for (i = 0; i < value.debugFiles.length; i = i + 1) { 118 | file = value.debugFiles[i]; 119 | result_output += '
    • DEBUG ' + file + '
    • '; 120 | } 121 | } 122 | 123 | if (value.coverage == 1) { 124 | result_output += '
    • Coverage Report
    • '; 125 | } 126 | result_output += '
    '; 127 | } 128 | ); 129 | } 130 | result_output += '
'; 131 | 132 | Y.one('#results').setContent(result_output); 133 | UT.current_status = Y.JSON.stringify(current_status); 134 | Y.one('#count').setContent('

' + errorCount + ' test failure(s) found.

'); 135 | } 136 | } catch (e) { 137 | console.log(e); 138 | Y.one('#list').setContent('
' + response.responseText + '
'); 139 | } 140 | }, 141 | end: function(transactionid) { 142 | Y.later(5000, {}, UT.heartBeat); 143 | } 144 | } 145 | }; 146 | 147 | Y.io('/jute/_heart_beat', cfg); 148 | 149 | }; 150 | 151 | UT.content_set = false; 152 | UT.waitLoop = function() { 153 | var cfg = { 154 | method: 'GET', 155 | data: "d=" + new Date().getTime(), 156 | on: { 157 | success: function (transactionid, response) { 158 | var data, qs, connect, i, content_node, content, test; 159 | try { 160 | data = Y.JSON.parse(response.responseText); 161 | if (data.testLocation) { 162 | // get the current URL 163 | qs = window.location.search.substring(1); 164 | //get the parameters & mash them with testLocation 165 | connect = '?'; 166 | if (data.testLocation.match(/\?/)) { 167 | connect = '&'; 168 | } 169 | if (qs) { 170 | data.testLocation = data.testLocation + connect + qs; 171 | } 172 | window.location.href = data.testLocation; 173 | } else if (!UT.content_set && data.availableTests) { 174 | content_node = Y.one('#content'); 175 | content = '
'; 176 | content += '

Available Tests

'; 177 | content += ''; 178 | for (i = 0; i < data.availableTests.length; i = i + 1) { 179 | test = data.availableTests[i]; 180 | content += ''; 181 | content += ''; 182 | content += ''; 183 | content += ''; 184 | content += ''; 185 | } 186 | content += '
Test File (located at ' + data.config.testDir + ') Run Without Coverage Run With Coverage
' + test.test_url + ' RunRun
'; 187 | content += ''; 188 | content_node.setContent(content); 189 | Y.one('#run_all_no_cov').on('click', function() { 190 | Y.all('.no_cov_cbox').set('checked', Y.one('#run_all_no_cov').get('checked')); 191 | }); 192 | Y.one('#run_all_cov').on('click', function() { 193 | Y.all('.cov_cbox').set('checked', Y.one('#run_all_cov').get('checked')); 194 | }); 195 | UT.content_set = true; 196 | } 197 | } 198 | catch(e) { 199 | // Nothing to run prolly or server went away 200 | console.log(e); 201 | } 202 | }, 203 | end: function(transactionid) { 204 | Y.later(5000, {}, UT.waitLoop); 205 | }, 206 | failure: function() { 207 | // this of course will never happen... 208 | } 209 | } 210 | }; 211 | 212 | Y.io('/jute/_get_test', cfg); 213 | }; 214 | 215 | button = Y.one('#get_coverage_button'); 216 | if (button) { 217 | button.on('click', function() { 218 | // open iframe in same domain to grab coverage output 219 | console.log("current status: " + UT.current_status); 220 | var current_status = Y.JSON.parse(UT.current_status), i, obj; 221 | for (i = 0; i < current_status.tests_to_run.length; i = i + 1) { 222 | obj = current_status.tests_to_run[i]; 223 | if (navigator.userAgent === obj.browser && obj.running > 0) { 224 | Y.one('#grabber').setAttribute('src', obj.remote_grabber); 225 | Y.later(5000, {}, function() { Y.one('#run_tests').setAttribute('src', '/jute_docs/run_tests.html'); }); 226 | } 227 | } 228 | }); 229 | } 230 | 231 | load_button = Y.one('#load_button'); 232 | if (load_button) { 233 | load_button.on('click', function() { 234 | var load_me = Y.one('#load_file').get('value'); 235 | Y.io('/jute/_run_test', 236 | { 237 | method: 'POST', 238 | data: 'test=' + escape(load_me) 239 | } 240 | ); 241 | }); 242 | } 243 | 244 | kick_button = Y.one('#kick_frame'); 245 | if (kick_button) { 246 | kick_button.on('click', function() { 247 | Y.one('#run_tests').setAttribute('src', '/jute_docs/run_tests.html'); 248 | }); 249 | } 250 | 251 | clear_button = Y.one('#clear_tests'); 252 | if (clear_button) { 253 | clear_button.on('click', function() { 254 | Y.io('/jute/_clear_tests', 255 | { 256 | method: 'GET', 257 | on: { 258 | success: function() { 259 | Y.one('#run_tests').setAttribute('src', '/jute_docs/run_tests.html'); 260 | }, 261 | failure: function() { 262 | Y.one('#run_tests').setAttribute('src', '/jute_docs/run_tests.html'); 263 | } 264 | } 265 | } 266 | ); 267 | }); 268 | } 269 | 270 | clear_results = Y.one('#clear_results'); 271 | if (clear_results) { 272 | clear_results.on('click', function() { 273 | Y.io('/jute/_clear_results'); 274 | }); 275 | } 276 | }, '1.0', { requires: [ 'node', 'io-base', 'json' ] }); 277 | 278 | -------------------------------------------------------------------------------- /backend/nodejute/jute/jute_docs/jute_client.js: -------------------------------------------------------------------------------- 1 | YUI().add('jute', function(Y) { 2 | var totalLog = []; 3 | Y.Test.Runner.subscribe(Y.Test.Runner.COMPLETE_EVENT, 4 | function(data) { 5 | var cover_out = Y.Test.Runner.getCoverage(Y.Coverage.Format.JSON), 6 | report_data = Y.Test.Format.JUnitXML(data.results); 7 | 8 | Y.Global.fire('testDone', data); 9 | if (typeof(console) == 'object') { 10 | console.log("JUTE TestDone " + data.results.name); 11 | } 12 | Y.io('/jute/_test_report', 13 | { 14 | method: 'PUT', 15 | data: 'results=' + escape(report_data) + '&name=' + escape(data.results.name) + "&coverage=" + escape(cover_out) + "&log=" + escape(Y.JSON.stringify(totalLog)), 16 | headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 17 | on: { 18 | end: function(tid, args) { 19 | if (!window.location.toString().match("_one_shot")) { 20 | window.location.href = '/jute_docs/run_tests.html'; 21 | } 22 | } 23 | } 24 | } 25 | ); 26 | } 27 | ); 28 | 29 | var pushBack = function(where, what, why) { 30 | Y.io('/jute/_' + where, 31 | { 32 | method: 'PUT', 33 | data: 'msg=' + escape(what) + '&why=' + escape(why), 34 | headers: { 'Content-Type': 'application/x-www-form-urlencoded' } 35 | } 36 | ); 37 | }; 38 | 39 | Y.Global.on('yui:log', function(log) { 40 | totalLog.push(log); 41 | pushBack('message', log.msg, 'Y.Global.log'); 42 | }); 43 | 44 | Y.on('yui:log', function(log) { 45 | totalLog.push(log); 46 | pushBack('message', log.msg, 'Y.log'); 47 | }); 48 | 49 | // Immediately push console message in case we never hear from this dude again 50 | if (typeof(console) == 'object') { 51 | var oCL = console.log, 52 | oCE = console.error; 53 | 54 | console.log = function(msg) { 55 | totalLog.push({ msg: 'console.log: ' + msg }); 56 | oCL.apply(console, arguments); 57 | pushBack('message', msg, 'console.log'); 58 | } 59 | 60 | console.error = function(msg) { 61 | totalLog.push({ msg: 'console.error: ' + msg }); 62 | oCE.apply(console, arguments); 63 | pushBack('message', msg, 'console.error'); 64 | } 65 | } 66 | 67 | // A helpful function - setup console & run tests 68 | Y.namespace('UnitTest').go = function() { 69 | 70 | //initialize the console 71 | var yconsole = new Y.Console({ 72 | newestOnTop: false 73 | }); 74 | yconsole.render('#log'); 75 | Y.Test.Runner.run(); 76 | }; 77 | 78 | }, '1.0', { requires: [ 'test', 'io-base', 'console', 'json-stringify' ] }); 79 | 80 | -------------------------------------------------------------------------------- /backend/nodejute/jute/jute_docs/run_tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 37 | 38 | 39 | 40 | 41 |
Don't mind me - waiting around to run tests...
42 | 43 | 44 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /backend/nodejute/jute/phantomJUTE.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wait until the test condition is true or a timeout occurs. Useful for waiting 3 | * on a server response or for a ui change (fadeIn, etc.) to occur. 4 | * 5 | * @param testFx javascript condition that evaluates to a boolean, 6 | * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or 7 | * as a callback function. 8 | * @param onReady what to do when testFx condition is fulfilled, 9 | * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or 10 | * as a callback function. 11 | * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used. 12 | */ 13 | function waitFor(testFx, onReady, timeOutMillis) { 14 | var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3000000, //< Default Max Timout is 3s 15 | start = new Date().getTime(), 16 | condition = false, 17 | interval = setInterval(function() { 18 | if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) { 19 | // If not time-out yet and condition not yet fulfilled 20 | condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code 21 | } else { 22 | if(!condition) { 23 | // If condition still not fulfilled (timeout but condition is 'false') 24 | console.log("'waitFor()' timeout"); 25 | phantom.exit(1); 26 | } else { 27 | // Condition fulfilled (timeout and/or condition is 'true') 28 | console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms."); 29 | typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled 30 | clearInterval(interval); //< Stop this interval 31 | } 32 | } 33 | }, 1000); //< repeat check every 1000ms 34 | }; 35 | 36 | 37 | var page = new WebPage(), outputDir = phantom.args[1]; 38 | 39 | page.onAlert = function(msg) { console.log("ALERT: " + msg); }; 40 | page.onConsoleMessage = function (msg) { 41 | var matches = msg.match(/JUTE TestDone (.+)/); 42 | if (matches != null) { 43 | setTimeout(function() { 44 | page.render(outputDir + '/' + matches[1] + '/phantomjs_snap.png'); 45 | }, 100); 46 | } else { 47 | console.log(msg); 48 | } 49 | }; 50 | 51 | //page.onResourceRequested = function (msg) { console.log("Request: " + msg.url); }; 52 | //page.onResourceReceived = function (msg) { console.log("Received: " + msg.url); }; 53 | 54 | // Open to JUTE & wait - JUTE will kill us when we're done 55 | page.open(phantom.args[0], function (status) { 56 | // Check for page load success 57 | if (status !== "success") { 58 | console.log("Unable to access network"); 59 | } else { 60 | setInterval(function() {}, 200000); 61 | } 62 | }); 63 | 64 | -------------------------------------------------------------------------------- /backend/nodejute/jute/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Yahoo! Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use of this software in source and binary forms, 6 | with or without modification, are permitted provided that the following 7 | conditions are met: 8 | 9 | * Redistributions of source code must retain the above 10 | copyright notice, this list of conditions and the 11 | following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the 15 | following disclaimer in the documentation and/or other 16 | materials provided with the distribution. 17 | 18 | * Neither the name of Yahoo! Inc. nor the names of its 19 | contributors may be used to endorse or promote products 20 | derived from this software without specific prior 21 | written permission of Yahoo! Inc. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 24 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | module.exports = { 38 | Create: function(hub) { 39 | var path = require('path'), 40 | fs = require('fs'); 41 | 42 | // Events I care about 43 | hub.addListener('startServer', startServer); 44 | 45 | // Get this ready to go 46 | var juteClient = fs.readFileSync(path.join(__dirname, 'jute_docs', 'jute_client.js'), 'utf8'); 47 | 48 | function startServer() { 49 | 50 | var connect = require('connect'), 51 | sessions = require('cookie-sessions'), 52 | os = require('os'), 53 | uuid = require('node-uuid'); 54 | 55 | hub.emit(hub.LOG, hub.INFO, "Running as " + process.getuid() + '/' + process.getgid()); 56 | hub.emit(hub.LOG, hub.INFO, "Connect at http://" + (hub.config.host || os.hostname()) + ':' + hub.config.port + '/'); 57 | 58 | connect( 59 | sessions({secret: 'jute rox', timeout: 365 * 1000 * 60 * 60 * 24 }) 60 | , connect.favicon() 61 | , connect.query() 62 | , connect.bodyParser() 63 | , function(req, res, next) { 64 | // Make sure we have a session UUID 65 | // maybe even from a selenium host 66 | var sess = req.session; 67 | if (!sess) sess = req.session = {}; 68 | if (!sess.uuid) { 69 | sess.uuid = req.query.selenium || uuid(); 70 | sess.selenium = req.query.selenium ? true: false; 71 | } 72 | 73 | next(); 74 | } 75 | , connect.logger(hub.config.logFormat) 76 | , connect.router(function(app){ 77 | app.get('/jute_docs/:file', function(req, res, next){ 78 | // Just one of JUTE's static files 79 | sendFullFile(path.join(__dirname, req.url), req, res, next); 80 | }); 81 | app.all(/\/jute\/_([^\?]+)/, function(req, res, next){ 82 | // A JUTE action - start with 'prune' and go from there 83 | hub.emit('startAction', req.params[0], req, res); 84 | }); 85 | app.get('/', function(req, res, next){ 86 | // Serve from '/' 87 | res.writeHead(301, { Location: '/jute_docs/capture.html' }); 88 | res.end(); 89 | }); 90 | app.get(/jute\.js$/, function(req, res, next){ // FOR LEGACY JUTE TESTS THAT EXPLICITY INCLUDE JUTE.JS 91 | res.end(juteClient); 92 | }); 93 | }) 94 | , function(req, res, next) { 95 | // Anything else is a regular file - a test or a file being tested 96 | sendFullFile(path.join(hub.config.docRoot, req.url), req, res, next); 97 | } 98 | ).listen(hub.config.port); 99 | 100 | // Let anyone know the server has been started 101 | hub.emit('serverStarted'); 102 | } 103 | 104 | /* 105 | * Check if static file needs to be coverage'd before sending it 106 | */ 107 | function sendFullFile(path, req, res, next) { 108 | 109 | var p = require('path'), 110 | exec = require('child_process').exec, 111 | url = req.url; 112 | 113 | path = path.replace(/\?.*/,''); // get rid of any query string 114 | url = url.replace(/\?.*/,''); // get rid of any query string 115 | url = url.replace(hub.config.testDirWeb,''); // get rid of any query string 116 | 117 | try { fs.statSync(path); } catch(e) { res.writeHeader(404); res.end('Cannot find: ' + path ); return; } // 404 this bad boy 118 | 119 | // Do coverage for this file IF: 120 | // 1. coverage requested 121 | // 2a. referrer header does not exist 122 | // 2b. OR referrer header ODES NOT HAVE 'do_coverage=0' in its query string 123 | if (req.query.coverage && req.headers.referer && !req.headers.referer.match('do_coverage=0')) { 124 | // Coverage this bad boy! 125 | var tempFile = p.join('/tmp', p.basename(path)); 126 | hub.emit(hub.LOG, hub.INFO, "Generating coverage file " + tempFile + " for " + path); 127 | exec(hub.config.java + ' -jar ' + p.join(__dirname, "yuitest-coverage.jar") + " -o " + tempFile + " " + path, function(err) { 128 | if (err) { 129 | hub.emit(hub.LOG, 'error', "Error coverage'ing " + path + ": " + err); 130 | hub.emit(hub.LOG, 'error', "Sending plain file instead"); 131 | _doSend(path, req, res, next); 132 | } else { 133 | _doSend(tempFile, req, res, next); 134 | // DO NOT delete coverage'd file for debugging 135 | } 136 | }); 137 | } else { 138 | _doSend(path, req, res, next); 139 | } 140 | } 141 | /* 142 | * Sucked mostly from connection/middleware/static 143 | * just send a static file 144 | */ 145 | function _doSend(path, req, res, next) { 146 | 147 | var mime = require('mime'), 148 | efun = "\ 149 | if (typeof(YUI) == 'object') {\ 150 | YUI().use('io-base', 'json-stringify', function(Y) {\ 151 | var output = a, from='try/catch';\ 152 | if (typeof(b) != 'undefined') {\ 153 | if (typeof(b) == 'object') {\ 154 | output += ' ' + Y.JSON.stringify(b);\ 155 | } else {\ 156 | output += ' ' + b;\ 157 | }\ 158 | if (typeof(c) == 'object') {\ 159 | output += ' ' + Y.JSON.stringify(b);\ 160 | } else {\ 161 | output += ' ' + c;\ 162 | }\ 163 | from = 'onerror';\ 164 | }\ 165 | output = from + ': ' + output;\ 166 | Y.io('/jute/_message',\ 167 | {\ 168 | method: 'PUT',\ 169 | data: 'msg=' + escape(output) + '&why=script.error',\ 170 | headers: { 'Content-Type': 'application/x-www-form-urlencoded' }\ 171 | }\ 172 | );\ 173 | });}"; 174 | 175 | fs.stat(path, function(err, stat) { 176 | var type, charset, 177 | cutils = require('connect/lib/utils'); 178 | 179 | if (err) { 180 | return 'ENOENT' == err.code ? next() : next(err); 181 | // ignore directories 182 | } else if (stat.isDirectory()) { 183 | next(); 184 | } 185 | 186 | type = mime.lookup(path); 187 | 188 | // res.setHeader('Content-Length', stat.size); 189 | res.setHeader('Last-Modified', new Date().toUTCString()); 190 | 191 | // header fields 192 | if (!res.getHeader('content-type')) { 193 | charset = mime.charsets.lookup(type); 194 | res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : '')); 195 | } 196 | 197 | console.log(path + ' type: ' + type); 198 | 199 | if (type.match(/javascript/)) { 200 | var file = fs.readFileSync(path, 'utf8'), 201 | add = "}catch(a){" + efun + "}"; 202 | 203 | file = "try{" + file + add; 204 | // res.setHeader('Content-Length', stat.size + add.length + 4); 205 | 206 | console.log('put try/catch block around: ' + path); 207 | 208 | // dynamically inject JUTE? 209 | if (parseInt(hub.config.inject, 10)) { 210 | regex = /\)\s*\.\s*use\s*\(([^)]+)/; 211 | 212 | var matches = regex.exec(file); 213 | if (matches) { 214 | if (matches[1].match('test')) { 215 | 216 | hub.emit(hub.LOG, hub.INFO, "Dynamically injecting JUTE client into " + path); 217 | 218 | // res.setHeader('Content-Length', res.getHeader('Content-Length') + juteClient.length); 219 | res.write(juteClient); 220 | file = file.replace('test', 'jute'); // Need to smarter some day 221 | } 222 | } 223 | } 224 | res.end(file); 225 | } else if (type.match(/html/i)) { 226 | var file = fs.readFileSync(path, 'utf8'), 227 | err = ''; 228 | 229 | // res.setHeader('Content-Length', res.getHeader('Content-Length') + err.length); 230 | res.write(err); 231 | res.end(file); 232 | } else { 233 | fs.createReadStream(path).pipe(res); 234 | } 235 | }); 236 | } 237 | } 238 | }; 239 | 240 | 241 | -------------------------------------------------------------------------------- /backend/nodejute/jute/yuitest-coverage.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzo/JUTE/77527d0a377077038b2b81aabe8f6b8d2a37f90a/backend/nodejute/jute/yuitest-coverage.jar -------------------------------------------------------------------------------- /backend/nodejute/jute_backend.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | Copyright (c) 2011, Yahoo! Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use of this software in source and binary forms, 7 | with or without modification, are permitted provided that the following 8 | conditions are met: 9 | 10 | * Redistributions of source code must retain the above 11 | copyright notice, this list of conditions and the 12 | following disclaimer. 13 | 14 | * Redistributions in binary form must reproduce the above 15 | copyright notice, this list of conditions and the 16 | following disclaimer in the documentation and/or other 17 | materials provided with the distribution. 18 | 19 | * Neither the name of Yahoo! Inc. nor the names of its 20 | contributors may be used to endorse or promote products 21 | derived from this software without specific prior 22 | written permission of Yahoo! Inc. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 25 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 27 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 28 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 30 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | // npm npm start jute -g 2&>1 > LOGFILE & 38 | var util = require('util'), 39 | fs = require("fs"), 40 | events = require("events"), 41 | configure = require('./jute/configure'), 42 | server = require('./jute/server'), 43 | actions = require('./jute/actions'), 44 | common = require('./jute/actions/common'), 45 | eventHubF = function() { events.EventEmitter.call(this); this.LOG = 'log'; this.ERROR = 'error'; this.INFO = 'info'; } 46 | ; 47 | 48 | /** 49 | * Create our event hub 50 | */ 51 | util.inherits(eventHubF, events.EventEmitter); 52 | var eventHub = new eventHubF(); 53 | 54 | // Prime cache 55 | eventHub.cache = { browsers: {}, tests_to_run: [], connections: {}, currentTest: {} }; 56 | 57 | // Some app-wide helpers 58 | eventHub.addListener(eventHub.LOG, function(sev, str) { 59 | var output; 60 | if (sev === eventHub.ERROR) { 61 | output = 'ERROR: ' + str + "\n"; 62 | } else { 63 | output = str + "\n"; 64 | } 65 | 66 | if (eventHub.config.logFD) { 67 | fs.writeSync(eventHub.config.logFD, output); 68 | } else { 69 | console.error(str); 70 | } 71 | }); 72 | 73 | configure.Create(eventHub); 74 | actions.Create(eventHub, common.Create(eventHub)); 75 | server.Create(eventHub); 76 | 77 | // Get Party Started 78 | eventHub.on('configureError', function(obj) { 79 | eventHub.emit(eventHub.LOG, eventHub.ERROR, "Configuration error for " + obj.name + ': ' + obj.error); 80 | eventHub.emit(eventHub.LOG, eventHub.ERROR, "Fix and restart jute!"); 81 | process.exit(0); 82 | }); 83 | 84 | eventHub.on('configureDone', function() { 85 | // Note config gets stashed in eventHub (eventHub.config) 86 | eventHub.on('actionsLoaded', function() { 87 | 88 | console.log('Looking for unit tests in: ' + eventHub.config.testDir); 89 | console.log('Ouptut going to: ' + eventHub.config.outputDir); 90 | 91 | // Dump the config file for jute_v8 and submit_tests 92 | console.log('DMPING: ' + JSON.stringify(eventHub.config)); 93 | fs.writeFileSync('/tmp/jute.config', JSON.stringify(eventHub.config)); 94 | 95 | // Fire up server 96 | eventHub.emit('startServer'); 97 | }); 98 | eventHub.emit('loadActions'); 99 | }); 100 | eventHub.emit('configure'); 101 | 102 | process.on('exit', function() { fs.unlinkSync(process.env['npm_package_config_pidfile']); }); 103 | -------------------------------------------------------------------------------- /backend/nodejute/jute_jasmine.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* 4 | Copyright (c) 2012, ZZO Associates 5 | All rights reserved. 6 | 7 | Redistribution and use of this software in source and binary forms, 8 | with or without modification, are permitted provided that the following 9 | conditions are met: 10 | 11 | * Redistributions of source code must retain the above 12 | copyright notice, this list of conditions and the 13 | following disclaimer. 14 | 15 | * Redistributions in binary form must reproduce the above 16 | copyright notice, this list of conditions and the 17 | following disclaimer in the documentation and/or other 18 | materials provided with the distribution. 19 | 20 | * Neither the name of Yahoo! Inc. nor the names of its 21 | contributors may be used to endorse or promote products 22 | derived from this software without specific prior 23 | written permission of Yahoo! Inc. 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 26 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 28 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | */ 37 | 38 | var fs = require('fs') 39 | ,m = require('module') 40 | ,PATH = require('path') 41 | ,util = require('util') 42 | ,exec = require('child_process').exec 43 | ,config = (require('./getConfig'))() 44 | ,DEBUG = function() { if (process.env.JUTE_DEBUG==1) { console.log(Array.prototype.join.call(arguments, ' ')); } } 45 | ,coverageReportJar = PATH.join(__dirname, 'jute', 'actions', 'yuitest-coverage-report.jar') 46 | ,coverageJar = PATH.join(__dirname, 'jute', 'yuitest-coverage.jar') 47 | ,TEST_FILE 48 | ,SAVE_DIR 49 | ,DO_COVERAGE 50 | ,FILES_FOR_COVERAGE = {} 51 | ,testOutput = '' 52 | ,jasmine = require('jasmine-node/lib/jasmine-node/index.js') 53 | ,originalLoader 54 | ; 55 | 56 | 57 | if (!config) { 58 | console.error('You must % npm start jute!'); 59 | process.exit(0); 60 | } 61 | 62 | if (!process.argv[2]) { 63 | console.log('You must specify a test to run! This file is relative to testDir'); 64 | process.exit(1); 65 | } 66 | 67 | TEST_FILE = PATH.join(config.testDir, process.argv[2]); 68 | DO_COVERAGE = TEST_FILE.match(/do_coverage=1/) || process.argv[3]; 69 | TEST_FILE = TEST_FILE.replace(/\?.*/,''); // get rid of any query string 70 | SAVE_DIR = PATH.join(config.outputDir, PATH.basename(TEST_FILE, '.js'), PATH.sep) 71 | 72 | console.log('Testing ' + TEST_FILE + ' with' + (DO_COVERAGE ? '' : 'out') + ' code coverage'); 73 | 74 | // Recursively instrument all requested coverage files 75 | function getOneCoverage(files, index) { 76 | if (files[index]) { 77 | var reqMatch = /require\s*\(\s*['"]([^'"]+)['"]\s*,\s*true\s*\)/g 78 | , covers = reqMatch.exec(files[index]) 79 | ; 80 | 81 | generateCoverage(covers[1], getOneCoverage, files, ++index); 82 | } else { 83 | // All done 84 | doit(); 85 | } 86 | } 87 | 88 | // START PARTY!!! 89 | if (TEST_FILE.match(/\.js$/)) { 90 | // JS 91 | 92 | if (DO_COVERAGE) { 93 | try { 94 | var tf = fs.readFileSync(TEST_FILE, 'utf8') 95 | , reqMatch = /require\s*\(\s*['"]([^'"]+)['"]\s*,\s*true\s*\)/g 96 | , cc = tf.match(reqMatch) 97 | ; 98 | 99 | if (cc) { 100 | console.log('getting coverage'); 101 | getOneCoverage(cc, 0); 102 | } else { 103 | console.log('Warning: requested coverage but found no modules to cover'); 104 | doit(); 105 | } 106 | } catch(e) { 107 | console.error("Error reading " + TEST_FILE + ": " + e); 108 | process.exit(1); 109 | } 110 | 111 | originalLoader = m._load; 112 | m._load = hookedLoader; 113 | } else { 114 | doit(); 115 | } 116 | } 117 | 118 | function doit() { 119 | console.log('doing it'); 120 | var junitreport = { 121 | report: true 122 | , savePath : PATH.join(config.outputDir, PATH.basename(TEST_FILE, '.js'), PATH.sep) 123 | , useDotNotation: true 124 | , consolidate: true 125 | }; 126 | 127 | jasmine.executeSpecsInFolder(config.testDir 128 | , onComplete 129 | , process.env.JUTE_DEBUG == 1 130 | , false 131 | , false 132 | , false 133 | , new RegExp(PATH.basename(TEST_FILE, '.js')) 134 | , junitreport 135 | ); 136 | } 137 | 138 | /** 139 | * This gets called when the unit tests are finished 140 | */ 141 | function onComplete(runner, log) { 142 | var dirname = SAVE_DIR 143 | , cover_out_file = PATH.join(dirname, 'cover.json') 144 | , cover 145 | , total_lines 146 | , total_functions 147 | , line_coverage = 0 148 | , func_coverage = 0 149 | ; 150 | 151 | if (typeof _yuitest_coverage == 'object') { 152 | 153 | try { 154 | fs.writeFileSync(cover_out_file, JSON.stringify(_yuitest_coverage)); 155 | } catch(e) { 156 | console.error("Error writing coverage file " + cover_out_file + ": " + e); 157 | } 158 | 159 | DEBUG([ config.java, '-jar', coverageReportJar, '--format', 'lcov', '-o', dirname, cover_out_file ].join(' ')); 160 | exec([ config.java, '-jar', coverageReportJar, '--format', 'lcov', '-o', dirname, cover_out_file ].join(' '), function(err, stdout, stderr) { 161 | if (err) { 162 | console.error('Error creating coverage report: ' + err); 163 | process.exit(1); 164 | } else { 165 | for (file in _yuitest_coverage) { 166 | cover = _yuitest_coverage[file]; 167 | total_lines = cover.coveredLines; 168 | total_functions = cover.coveredFunctions; 169 | 170 | if (total_lines) { 171 | line_coverage = Math.round((cover.calledLines / total_lines) * 100); 172 | } 173 | console.log('Line coverage for ' + file + ': ' + line_coverage + '%'); 174 | 175 | if (total_functions) { 176 | func_coverage = Math.round((cover.calledFunctions / total_functions) * 100); 177 | } 178 | console.log('Function coverage for ' + file + ': ' + func_coverage + '%'); 179 | } 180 | } 181 | }); 182 | } 183 | } 184 | 185 | function generateCoverage(file, cb, files, index) { 186 | var tempFile = PATH.join('/tmp', PATH.basename(file)) 187 | , realFile 188 | , real = true 189 | , keep = file 190 | ; 191 | 192 | try { 193 | realFile = require.resolve(file); 194 | } catch(e) { 195 | if (file.match(/^\./)) { 196 | file = PATH.join(config.testDir, file); 197 | } 198 | realFile = require.resolve(file); 199 | real = false; 200 | } 201 | 202 | if (realFile == file && real) { 203 | // A native module!! Who know where the heck these are - skip it 204 | DEBUG('Cannot get coverage for a native module: ' + file); 205 | cb(files, index); 206 | } else { 207 | exec(config.java + ' -jar ' + coverageJar + " -o " + tempFile + " " + realFile, function(err) { 208 | if (err) { 209 | console.error("Cannot coveage " + realFile + ': ' + err); 210 | process.exit(1); 211 | } else { 212 | FILES_FOR_COVERAGE[keep] = 1; 213 | cb(files, index); 214 | } 215 | }); 216 | } 217 | } 218 | 219 | function hookedLoader(request, parent, isMain) { 220 | if (FILES_FOR_COVERAGE[request]) { 221 | request = PATH.join('/tmp', PATH.basename(request)); 222 | var cached = m._resolveFilename(request, parent); 223 | delete m._cache[cached]; // make sure we get a fresh copy every time for mockery 224 | } 225 | 226 | return originalLoader(request, parent, isMain); 227 | } 228 | 229 | -------------------------------------------------------------------------------- /backend/nodejute/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jute", 3 | "description": "Javascript Unit Test Environment", 4 | "keywords": [ 5 | "selenium", 6 | "test", 7 | "testing", 8 | "unit", 9 | "tests", 10 | "jasmine", 11 | "phantomjs" 12 | ], 13 | "version": "0.0.69", 14 | "author": "Mark Ethan Trostler ", 15 | "preferGlobal": true, 16 | "bin": { 17 | "jute": "./jute_backend.js", 18 | "jute_submit_test": "./submit_test.js", 19 | "jute_v8": "./jute_v8.js", 20 | "jute_jasmine": "./jute_jasmine.js" 21 | }, 22 | "scripts": { 23 | "start": "node ./jute/daemon.js --start", 24 | "stop": "node ./jute/daemon.js --stop", 25 | "restart": "node ./jute/daemon.js --restart" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/zzo/JUTE.git" 30 | }, 31 | "homepage": "https://github.com/zzo/JUTE", 32 | "dependencies": { 33 | "soda": "~0.2", 34 | "node-uuid": "~1.2", 35 | "connect": "~1.7", 36 | "yui3": "~0.7", 37 | "jsdom": "~0.2", 38 | "htmlparser": "~1.7", 39 | "wordwrap": "", 40 | "xmlhttprequest": "~1.2", 41 | "cookie-sessions": "~0.0", 42 | "mime": "~1.2", 43 | "optimist": "~0.2", 44 | "webdriverio": "^2.1.0", 45 | "daemonize2": "~0.4", 46 | "jasmine-node": "~1.0", 47 | "glob": "~3.2.6" 48 | }, 49 | "config": { 50 | "port": "5883", 51 | "docRoot": "/var/www/", 52 | "testDir": "test/", 53 | "outputDir": "output/", 54 | "java": "/usr/bin/java", 55 | "logfile": "/tmp/jute.log", 56 | "pidfile": "/tmp/jute.pid", 57 | "inject": "1", 58 | "testRegex": "*.htm*" 59 | }, 60 | "devDependencies": { 61 | "jshint": "~2.1.4" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /backend/nodejute/submit_test.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | Copyright (c) 2011, Yahoo! Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use of this software in source and binary forms, 7 | with or without modification, are permitted provided that the following 8 | conditions are met: 9 | 10 | * Redistributions of source code must retain the above 11 | copyright notice, this list of conditions and the 12 | following disclaimer. 13 | 14 | * Redistributions in binary form must reproduce the above 15 | copyright notice, this list of conditions and the 16 | following disclaimer in the documentation and/or other 17 | materials provided with the distribution. 18 | 19 | * Neither the name of Yahoo! Inc. nor the names of its 20 | contributors may be used to endorse or promote products 21 | derived from this software without specific prior 22 | written permission of Yahoo! Inc. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 25 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 27 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 28 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 30 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | 38 | var config = (require('./getConfig'))(), 39 | opt = require( "optimist" ), 40 | os = require('os'), 41 | events = require("events"), 42 | eventHubF = function() { events.EventEmitter.call(this); }, 43 | args = opt 44 | .usage('Usage: $0 --test [testfile] [ --test [another testfile] ] [ --host [JUTE host] ] [ --port [JUTE host port] ] [ --sel_host [Selenium host] ] [ --sel_browser [Selenium browser spec] ] [ --seleniums # ] [ --sel2 ] [ --load ] ] [ --send_output ] [ --wait ] [ --clear_results ] [ -v8 ] [ --jasmine] [ --coverage ] [ --status ] [ --snapshot ] [ --retry ] [ --phantomjs ]') 45 | .alias('t', 'test') 46 | .alias('h', 'host') 47 | .alias('p', 'port') 48 | .alias('sh', 'sel_host') 49 | .alias('sb', 'sel_browser') 50 | .alias('se', 'seleniums') 51 | .alias('l', 'load') 52 | .alias('s', 'status') 53 | .alias('sn', 'snapshot') 54 | .alias('c', 'clear_results') 55 | .alias('w', 'wait') 56 | .alias('r', 'retry') 57 | .alias('ph', 'phantomjs') 58 | .alias('s2', 'sel2') 59 | .default('host', (config && config.host) || os.hostname()) 60 | .default('port', (config && config.port) || 8080) 61 | .default('send_output', false) 62 | .default('wait', false) 63 | .default('v8', false) 64 | .default('jasmine', false) 65 | .default('coverage', false) 66 | .default('snapshot', false) 67 | .default('load', false) 68 | .default('phantomjs', false) 69 | .default('clear_results', false) 70 | .default('seleniums', 1) 71 | .default('sel_browser', '*firefox') 72 | .default('sel2', 0) 73 | .default('retry', 0) 74 | .describe('test', 'Test file to run - relative to docRoot/testDir (npm set jute.testDir) - can specify multiple of these') 75 | .describe('host', 'Hostname of JUTE server') 76 | .describe('port', 'Port of JUTE server') 77 | .describe('sel_host', 'Hostname of Selenium RC or Grid Server (if not specified test(s) will run in all CURRENTLY captured browsers)') 78 | .describe('sel_browser', 'Selenium browser specification') 79 | .describe('seleniums', 'Number of Selenium browsers to run tests in parallel') 80 | .describe('sel2', 'Use Selenium2') 81 | .describe('load', 'Load up tests but do not run them immediately') 82 | .describe('snapshot', 'Dump a snapshot at end of test (Selenium only!)') 83 | .describe('send_output', 'For Selenium tests ONLY - send status messages back while running') 84 | .describe('wait', 'Wait for captured tests to finish') 85 | .describe('clear_results', 'Clear ALL previous test results before running specified test(s)') 86 | .describe('v8', 'Run these test(s) using the V8 backend') 87 | .describe('jasmine', 'Run these test(s) using Jasmine') 88 | .describe('coverage', 'Run ALL test(s) with code coverage for V8 or Jasmine') 89 | .describe('status', 'Just get status') 90 | .describe('retry', 'Number of time to retry a failed test') 91 | .describe('phantomjs', 'Path to phantomjs executable') 92 | .argv, 93 | util = require('util'), 94 | qs = require('querystring'), 95 | http = require('http'), 96 | juteArgs = {}; 97 | 98 | if (!config) { 99 | console.error('You must % npm start jute!'); 100 | process.exit(0); 101 | } 102 | 103 | if (args.help) { 104 | console.log(opt.help()); 105 | process.exit(0); 106 | } 107 | 108 | if (args.wait && args.sel_host) { 109 | console.log("You don't need '--wait' for Selenium tests!"); 110 | } 111 | 112 | if (args.wait && (args.v8 || args.jasmine)) { 113 | console.log("You don't need '--wait' for V8 or Jasmine tests!"); 114 | } 115 | 116 | if (args.v8 && args.sel_host) { 117 | console.error("Erg V8 or Selenium - pick one!"); 118 | process.exit(1); 119 | } 120 | 121 | if (args.jasmine && args.sel_host) { 122 | console.error("Erg Jasmine or Selenium - pick one!"); 123 | process.exit(1); 124 | } 125 | 126 | if (args.jasmine && args.v8) { 127 | console.error("Erg Jasmine or V8 - pick one!"); 128 | process.exit(1); 129 | } 130 | 131 | util.inherits(eventHubF, events.EventEmitter); 132 | var eventHub = new eventHubF(); 133 | 134 | var options = { 135 | host: args.host, 136 | port: args.port 137 | }; 138 | 139 | eventHub.on('tests', function(tests) { 140 | if (tests) { 141 | if (args.v8) { 142 | var exec = require('child_process').exec, 143 | path = require('path'); 144 | 145 | for (var i = 0; i < tests.length; i++) { 146 | var test = tests[i]; 147 | 148 | if (args.coverage) { 149 | test += "?do_coverage=1"; 150 | } 151 | 152 | exec(path.join(__dirname, 'jute_v8.js') + ' ' + test, function(error, stdout, stderr) { 153 | if (error) { 154 | console.error("Error running jute_v8: " + error); 155 | } else { 156 | console.error(stderr); 157 | console.log(stdout); 158 | } 159 | 160 | if (i == args.length) { 161 | process.exit(0); 162 | } 163 | }); 164 | } 165 | } else if (args.jasmine) { 166 | var exec = require('child_process').exec, 167 | path = require('path'); 168 | 169 | for (var i = 0; i < tests.length; i++) { 170 | var test = tests[i]; 171 | 172 | if (args.coverage) { 173 | test += "?do_coverage=1"; 174 | } 175 | 176 | exec(path.join(__dirname, 'jute_jasmine.js') + ' ' + test, function(error, stdout, stderr) { 177 | if (error) { 178 | console.error("Error running jute_jasmine: " + error); 179 | } else { 180 | console.error(stderr); 181 | console.log(stdout); 182 | } 183 | 184 | if (i == args.length) { 185 | process.exit(0); 186 | } 187 | }); 188 | } 189 | } else { 190 | // POST space separated list of tests 191 | juteArgs.tests = tests.join(' '); 192 | if (args.load) { 193 | juteArgs.load = 1; 194 | } 195 | if (args.wait) { 196 | juteArgs.wait = 1; 197 | } 198 | juteArgs.retry = args.retry; 199 | 200 | // Toss in Selenium stuff 201 | if (args.phantomjs) { 202 | juteArgs.phantomjs = 1; 203 | } 204 | 205 | // Toss in Selenium stuff 206 | if (args.sel_host) { 207 | juteArgs.sel_host = args.sel_host; 208 | juteArgs.sel_browser = args.sel_browser; 209 | juteArgs.seleniums = args.seleniums; 210 | if (args.sel2) { 211 | juteArgs.sel2 = 1; 212 | } 213 | if (args.snapshot) { 214 | juteArgs.snapshot = 1; 215 | } 216 | } 217 | 218 | // Whether to stream output back 219 | if (args.send_output) { 220 | juteArgs.send_output = 1; 221 | } 222 | 223 | options.path = '/jute/_run_test'; 224 | options.method = 'POST'; 225 | options.headers = { 'Content-Type': 'application/json' }; 226 | 227 | // See what we got 228 | console.log('Submitting ' + util.inspect(juteArgs) + ' to ' + args.host); 229 | 230 | // POST AWAY! 231 | var req = http.request(options, function(res) { 232 | 233 | // 6000 seconds = 100 minutes 234 | req.socket.setTimeout(6000000, function(e) { console.log('socket timeout!'); }); 235 | 236 | if (res.statusCode != 200) { 237 | console.log('JUTE Displeased'); 238 | } 239 | 240 | res.setEncoding('utf8'); 241 | res.on('data', function (chunk) { 242 | console.log(chunk); 243 | }); 244 | res.on('end', function() { 245 | }); 246 | }); 247 | 248 | // Not Good 249 | req.on('error', function(e) { 250 | console.error('Problem contacting JUTE server at: ' + args.host + ':' + args.port); 251 | console.error("Is JUTE running there? Did you specify '--host' and '--port' correctly?"); 252 | process.exit(1); 253 | }); 254 | 255 | req.end(JSON.stringify(juteArgs)); 256 | } 257 | } 258 | }); 259 | 260 | if (args.status) { 261 | options.path = '/jute/_status'; 262 | http.get(options, function(res) { 263 | var status = ''; 264 | res.on('data', function (chunk) { 265 | status += chunk; 266 | }); 267 | 268 | res.on('end', function() { 269 | console.log(status); 270 | process.exit(0); 271 | }); 272 | }); 273 | } else { 274 | 275 | // Read test from STDIN? 276 | if (args.test === true) { 277 | var stests = ''; 278 | 279 | process.stdin.resume(); 280 | process.stdin.setEncoding('utf8'); 281 | 282 | process.stdin.on('data', function(chunk) { 283 | stests += chunk; 284 | }); 285 | 286 | process.stdin.on('end', function() { 287 | eventHub.emit('tests', stests.trim().split('\n')); 288 | }); 289 | } else { 290 | // Make sure we have an array of test(s) 291 | if (args.test && typeof args.test != 'object') { 292 | args.test = [ args.test ]; 293 | } 294 | 295 | eventHub.emit('tests', args.test); 296 | } 297 | 298 | if (args.clear_results) { 299 | console.log('Clearing all previous results...'); 300 | options.path = '/jute/_clear_results'; 301 | http.get(options, function(res) { }); 302 | } 303 | 304 | } 305 | -------------------------------------------------------------------------------- /backend/nodejute/test/getConfig/testGetConfig.js: -------------------------------------------------------------------------------- 1 | YUI({ 2 | logInclude: { TestRunner: true }, 3 | }).use('jute', function(Y) { 4 | 5 | var suite = new Y.Test.Suite('getConfig'), 6 | getConfig = require('./getConfig', true), // 'true' here means do code coverae on it! 7 | fs = require('fs'); 8 | 9 | suite.add(new Y.Test.Case({ 10 | setUp: function() { 11 | this.expectedKeys = { 12 | uid: 12139982, 13 | gid: 295949, 14 | port: '8080', 15 | docRoot: '/home/trostler/JUTE/backend/nodejute', 16 | testDir: '/home/trostler/JUTE/backend/nodejute/test', 17 | outputDir: '/home/trostler/JUTE/backend/nodejute/tmp', 18 | java: '/usr/bin/java', 19 | logFile: '/tmp/FF.log', 20 | logFormat: '', 21 | testRegex: '*.htm*', 22 | outputDirWeb: '/tmp', 23 | testDirWeb: 'test' 24 | }; 25 | }, 26 | testIsFunction : function () { 27 | Y.Assert.isFunction(getConfig); 28 | }, 29 | testGetObject : function () { 30 | Y.Assert.isObject(getConfig()); 31 | }, 32 | testKeyNum : function () { 33 | Y.Assert.areEqual(Object.keys(this.expectedKeys).length, Object.keys(getConfig()).length); 34 | }, 35 | testKeys : function () { 36 | var config = getConfig(); 37 | for (var key in config) { 38 | Y.Assert.isNotNull(this.expectedKeys[key]); 39 | } 40 | } 41 | })); 42 | 43 | Y.Test.Runner.add(suite); 44 | Y.UnitTest.go(); 45 | }); 46 | 47 | -------------------------------------------------------------------------------- /backend/nodejute/test/jute/actions/clearResults/testClearResults.js: -------------------------------------------------------------------------------- 1 | YUI({ 2 | logInclude: { TestRunner: true } 3 | }).use('jute', function(Y) { 4 | 5 | var suite = new Y.Test.Suite('clearResults'), 6 | hub = require('./test/mock/hub').getNewHub(), 7 | util = require('util'), 8 | fs = require('fs'), 9 | path = require('path'), 10 | clearResults = require('./jute/actions/clearResults', true).Create(hub); // 'true' here means do code coverae on it! 11 | 12 | 13 | suite.add(new Y.Test.Case({ 14 | name: 'clear results' 15 | ,setUp: function() { 16 | this.testFile = path.join('/tmp', '.blah', 'foo'); 17 | 18 | try { fs.unlinkSync(this.testFile); } catch(e) {} 19 | try { fs.rmdirSync(path.dirname(this.testFile)); } catch(e) {} 20 | } 21 | ,testHookedUp : function(vals) { 22 | Y.Assert.areEqual(hub.listeners('action:clear_results').length, 1); 23 | } 24 | ,testVals : function(vals) { 25 | var res = Y.Mock(); 26 | 27 | hub.config.outputDir = path.dirname(this.testFile); 28 | 29 | fs.mkdirSync(hub.config.outputDir, 0777); 30 | fs.writeFileSync(this.testFile, 'HI', 'utf8'); 31 | 32 | Y.Mock.expect(res, { method: "end", args: ["OK"] }); 33 | 34 | hub.emit('action:clear_results', {}, res, {}); 35 | 36 | // Give it half a second & then verify 37 | this.wait(function() { 38 | Y.Mock.verify(res); 39 | // Make sure bogus file is gone 40 | try { 41 | fs.statSync(this.testFile); 42 | Y.Assert.fail('Test file still exists!!'); 43 | } catch (e) { 44 | Y.Assert.isNotUndefined(e, 'Test file is gone!!'); 45 | } 46 | }, 500); 47 | } 48 | })); 49 | 50 | Y.Test.Runner.add(suite); 51 | Y.UnitTest.go(); 52 | }); 53 | 54 | -------------------------------------------------------------------------------- /backend/nodejute/test/jute/actions/clearTests/testClearTests.js: -------------------------------------------------------------------------------- 1 | YUI({ 2 | logInclude: { TestRunner: true } 3 | }).use('jute', function(Y) { 4 | 5 | var suite = new Y.Test.Suite('clearTests'), 6 | hub = require('./test/mock/hub').getNewHub(), 7 | util = require('util'), 8 | clearResults = require('./jute/actions/clearTests', true).Create(hub); // 'true' here means do code coverae on it! 9 | 10 | suite.add(new Y.Test.Case({ 11 | name: 'clear tests' 12 | ,setUp: function() { 13 | this.cache = { tests_to_run: [ 'foobie', 'doobie', 'doo' ] }; 14 | } 15 | ,testHookedUp : function(vals) { 16 | Y.Assert.areEqual(hub.listeners('action:clear_tests').length, 1); 17 | } 18 | ,testVals : function(vals) { 19 | var res = Y.Mock(); 20 | 21 | Y.Mock.expect(res, { method: "end", args: ["OK"] }); 22 | 23 | hub.emit('action:clear_tests', {}, res, this.cache); 24 | 25 | // Give it half a second & then verify 26 | this.wait(function() { 27 | Y.Mock.verify(res); 28 | Y.Assert.areEqual(this.cache.tests_to_run.length, 0, 'Ensure cache is empty'); 29 | }, 500); 30 | } 31 | })); 32 | 33 | Y.Test.Runner.add(suite); 34 | Y.UnitTest.go(); 35 | }); 36 | 37 | -------------------------------------------------------------------------------- /backend/nodejute/test/jute/actions/common/testCommon.js: -------------------------------------------------------------------------------- 1 | YUI({ 2 | logInclude: { TestRunner: true } 3 | }).use('jute', function(Y) { 4 | 5 | var suite = new Y.Test.Suite('common'), 6 | hub = require('./test/mock/hub').getNewHub(), 7 | util = require('util'), 8 | path = require('path'), 9 | fs = require('fs'), 10 | common = require('./jute/actions/common', true).Create(hub); // 'true' here means do code coverae on it! 11 | 12 | suite.add(new Y.Test.Case({ 13 | name: 'common' 14 | ,setUp: function() { 15 | this.req = { headers: { 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:5.0) Gecko/20100101 Firefox/5.0 FirePHP/0.5' }, connection: { remoteAddress: '1.2.3.4' } }; 16 | } 17 | ,testBrowserName : function() { 18 | var bn = common.browserName(this.req), 19 | sb = [this.req.headers['user-agent'], this.req.connection.remoteAddress].join('---'); 20 | 21 | Y.Assert.areEqual(bn, sb, 'Correct browser name'); 22 | } 23 | ,testSaneNames : function() { 24 | 25 | var names = common.makeSaneNames(common.browserName(this.req)), 26 | fileName = names[0], packageName = names[1]; 27 | 28 | Y.Assert.areEqual(fileName.match(/[\/;]/, false, 'no funny chars!')); 29 | Y.Assert.areEqual(fileName.match(/[^_A-Za-z0-9-]/, false, 'no funny chars!')); 30 | 31 | try { 32 | fs.writeFileSync(path.join('/tmp', fileName), 'foobie', 'utf8'); 33 | } catch(e) { 34 | Y.Assert.fail('Cannot make a file with sane name: ' + e); 35 | } finally { 36 | try { 37 | fs.unlinkSync(path.join('/tmp', fileName)); 38 | } catch(e) { } 39 | } 40 | 41 | Y.Assert.areEqual(packageName.match(/_/, false, 'no funny chars!')); 42 | 43 | } 44 | ,testDumpFile : function() { 45 | var vars = { key: 'DATA' }; 46 | 47 | hub.config.outputDir = '/tmp'; 48 | hub.on(hub.LOG, function(sev, mesg) { 49 | if(sev == hub.ERROR) { 50 | Y.Assert.fail('dumpFile errored: ' + mesg); 51 | } 52 | }); 53 | 54 | var ret = common.dumpFile(vars, 'key', 'fileName', 'componentName'); 55 | 56 | Y.Assert.areEqual(ret[0], path.join('/tmp', 'componentName', 'fileName'), 'Matching file name'); 57 | Y.Assert.areEqual(ret[1], path.join('/tmp', 'componentName'), 'Matching dir name'); 58 | 59 | try { 60 | fs.unlinkSync(path.join('/tmp', 'componentName', 'fileName')); 61 | fs.rmdirSync(path.join('/tmp', 'componentName')); 62 | } catch(e) {} 63 | } 64 | ,testFailedTests: function() { 65 | var fname = path.join('/tmp', 'foo'); 66 | 67 | fs.writeFileSync(fname, 'failures="19"', 'utf8'); 68 | Y.assert(common.failedTests(fname)); 69 | 70 | fs.writeFileSync(fname, 'failures="0"', 'utf8'); 71 | Y.assert(!common.failedTests(fname)); 72 | 73 | fs.writeFileSync(fname, 'failures="2230"', 'utf8'); 74 | Y.assert(common.failedTests(fname)); 75 | } 76 | })); 77 | 78 | Y.Test.Runner.add(suite); 79 | Y.UnitTest.go(); 80 | }); 81 | 82 | -------------------------------------------------------------------------------- /backend/nodejute/test/jute/actions/getTest/testGetTest.js: -------------------------------------------------------------------------------- 1 | YUI({ 2 | logInclude: { TestRunner: true } 3 | }).use('jute', function(Y) { 4 | 5 | var suite = new Y.Test.Suite('getTest'), 6 | util = require('util'), 7 | glob = require('glob'), 8 | hub = require('./test/mock/hub').getNewHub(), 9 | common = require('./jute/actions/common').Create(hub) 10 | getTest = require('./jute/actions/getTest', true).Create(hub, common, glob); // 'true' here means do code coverae on it! 11 | 12 | suite.add(new Y.Test.Case({ 13 | name: 'get test' 14 | ,setUp: function() { 15 | this.cache = { tests_to_run: [], browsers: {} }; 16 | this.uuid = 'blag'; 17 | 18 | this.req = { session: { uuid: this.uuid } }; 19 | this.req.headers = { 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:5.0) Gecko/20100101 Firefox/5.0 FirePHP/0.5' } 20 | this.req.connection = { remoteAddress: '1.2.3.4' }; 21 | } 22 | ,testHookedUp : function(vals) { 23 | Y.Assert.areEqual(hub.listeners('action:get_test').length, 1); 24 | } 25 | ,testNoTests : function(vals) { 26 | var res = Y.Mock(); 27 | Y.Mock.expect(res, { 28 | method: "end", 29 | args: [Y.Mock.Value.String] 30 | }); 31 | 32 | hub.emit('action:get_test', this.req, res, this.cache); 33 | 34 | // Give it half a second & then verify 35 | this.wait(function() { 36 | Y.Mock.verify(res); 37 | Y.Assert.isNumber(this.cache.browsers[this.uuid].get_test); 38 | }, 500); 39 | } 40 | ,testSomeTests : function(vals) { 41 | var res = Y.Mock(); 42 | Y.Mock.expect(res, { 43 | method: "end", 44 | args: [Y.Mock.Value.String] 45 | }); 46 | 47 | this.cache.tests_to_run.push({ browser: 'notMe' }); 48 | 49 | hub.emit('action:get_test', this.req, res, this.cache); 50 | 51 | // Give it half a second & then verify 52 | this.wait(function() { 53 | Y.Mock.verify(res); 54 | Y.Assert.isNumber(this.cache.browsers[this.uuid].get_test); 55 | Y.Assert.areEqual(this.cache.tests_to_run.length, 1, 'test for other browser gone!'); 56 | }, 500); 57 | } 58 | ,testMyTestAlreadyRunning : function(vals) { 59 | var res = Y.Mock(); 60 | Y.Mock.expect(res, { 61 | method: "end", 62 | args: [Y.Mock.Value.String] 63 | }); 64 | 65 | Y.Mock.expect(res, { 66 | method: "write", 67 | args: [Y.Mock.Value.String] 68 | }); 69 | 70 | this.cache.tests_to_run.push({ browser: this.uuid, running: true, sendOutput: true }); 71 | 72 | hub.emit('action:get_test', this.req, res, this.cache); 73 | 74 | // Give it half a second & then verify 75 | this.wait(function() { 76 | Y.Mock.verify(res); 77 | Y.Assert.isNumber(this.cache.browsers[this.uuid].get_test); 78 | Y.Assert.areEqual(this.cache.tests_to_run.length, 0, 'test for me still here!'); 79 | }, 500); 80 | } 81 | ,testGetTest : function(vals) { 82 | var res = Y.Mock(), url = 'foo'; 83 | Y.Mock.expect(res, { 84 | method: "end", 85 | args: ['{"testLocation":"' + url + '"}'] 86 | }); 87 | 88 | this.cache.tests_to_run.push({ browser: this.uuid, url: url }); 89 | 90 | hub.emit('action:get_test', this.req, res, this.cache); 91 | 92 | // Give it half a second & then verify 93 | this.wait(function() { 94 | Y.Mock.verify(res); 95 | Y.Assert.isNumber(this.cache.browsers[this.uuid].get_test); 96 | Y.Assert.isNumber(this.cache.tests_to_run[0].running, 'test NOT set to run!'); 97 | Y.Assert.areEqual(this.cache.tests_to_run[0].browser, this.uuid, 'Test not running in browser'); 98 | }, 500); 99 | } 100 | ,testGetSelTest : function(vals) { 101 | var res = Y.Mock(), url = 'foo'; 102 | Y.Mock.expect(res, { 103 | method: "end", 104 | args: [ '{"testLocation":"' + url + '"}' ] 105 | }); 106 | 107 | this.cache.tests_to_run.push({ browser: 'sel', url: url }); 108 | this.req.session.seleniumUUID = 'sel'; 109 | 110 | hub.emit('action:get_test', this.req, res, this.cache); 111 | 112 | // Give it half a second & then verify - test should get taken 113 | this.wait(function() { 114 | Y.Mock.verify(res); 115 | Y.Assert.isNumber(this.cache.browsers[this.uuid].get_test); 116 | Y.Assert.isNumber(this.cache.tests_to_run[0].running, 'test NOT set to run!'); 117 | Y.Assert.areEqual(this.cache.tests_to_run[0].browser, this.uuid, 'Test not running in sel browser'); 118 | }, 500); 119 | } 120 | ,testSelTestsDone : function(vals) { 121 | var res = Y.Mock(), url = 'foo', event = false;; 122 | Y.Mock.expect(res, { 123 | method: "end", 124 | args: [Y.Mock.Value.String] 125 | }); 126 | 127 | this.req.session.seleniumUUID = 'sel'; 128 | 129 | hub.on('seleniumTestsFinished', function() { 130 | event = true; 131 | }); 132 | 133 | hub.emit('action:get_test', this.req, res, this.cache); 134 | 135 | // Give it half a second & then verify - test should get taken 136 | this.wait(function() { 137 | Y.Mock.verify(res); 138 | Y.Assert.isNumber(this.cache.browsers[this.uuid].get_test); 139 | Y.assert(event, 'Selenium test finished event did not get called!'); 140 | }, 500); 141 | } 142 | })); 143 | 144 | Y.Test.Runner.add(suite); 145 | Y.UnitTest.go(); 146 | }); 147 | 148 | -------------------------------------------------------------------------------- /backend/nodejute/test/jute/actions/heartBeat/testHeartBeat.js: -------------------------------------------------------------------------------- 1 | YUI({ 2 | logInclude: { TestRunner: true } 3 | }).use('jute', function(Y) { 4 | 5 | var suite = new Y.Test.Suite('heartBeat'), 6 | util = require('util'), 7 | hub = require('./test/mock/hub').getNewHub(), 8 | common = require('./jute/actions/common').Create(hub), 9 | getTest = require('./jute/actions/heartBeat', true).Create(hub, common); // 'true' here means do code coverae on it! 10 | 11 | suite.add(new Y.Test.Case({ 12 | name: 'heart beat' 13 | ,setUp: function() { 14 | this.cache = { tests_to_run: [], browsers: {} }; 15 | 16 | this.uuid = 'foo'; 17 | this.req = { session: { uuid: this.uuid } }; 18 | this.req.headers = { 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:5.0) Gecko/20100101 Firefox/5.0 FirePHP/0.5' }; 19 | this.req.connection = { remoteAddress: '1.2.3.4' }; 20 | } 21 | ,testHookedUp : function(vals) { 22 | Y.Assert.areEqual(hub.listeners('action:heart_beat').length, 1); 23 | } 24 | ,testNothing : function(vals) { 25 | var res = Y.Mock(); 26 | 27 | Y.Mock.expect(res, { 28 | method: "end", 29 | args: [Y.Mock.Value.String] 30 | }); 31 | 32 | hub.emit('action:heart_beat', this.req, res, this.cache); 33 | 34 | // Give it half a second & then verify 35 | this.wait(function() { 36 | hub.emit('action:checkedResults', {}); 37 | this.wait(function() { 38 | Y.Mock.verify(res); 39 | Y.Assert.isNumber(this.cache.browsers[this.uuid].heart_beat); 40 | }, 500); 41 | }, 500); 42 | } 43 | })); 44 | 45 | Y.Test.Runner.add(suite); 46 | Y.UnitTest.go(); 47 | }); 48 | 49 | -------------------------------------------------------------------------------- /backend/nodejute/test/jute/actions/pop/testPop.js: -------------------------------------------------------------------------------- 1 | YUI({ 2 | logInclude: { TestRunner: true } 3 | }).use('jute', function(Y) { 4 | 5 | var suite = new Y.Test.Suite('pop'), 6 | util = require('util'), 7 | hub = require('./test/mock/hub').getNewHub(), 8 | pop = require('./jute/actions/pop', true).Create(hub); // 'true' here means do code coverae on it! 9 | 10 | suite.add(new Y.Test.Case({ 11 | name: 'pop' 12 | ,setUp: function() { 13 | this.cache = { tests_to_run: [] }; 14 | } 15 | ,testHookedUp : function(vals) { 16 | Y.Assert.areEqual(hub.listeners('action:pop').length, 1); 17 | } 18 | ,testNothing : function(vals) { 19 | 20 | hub.emit('action:pop', {}, {}, this.cache); 21 | 22 | // Give it half a second & then verify 23 | this.wait(function() { 24 | Y.Assert.areEqual(this.cache.tests_to_run.length, 0); 25 | }, 500); 26 | } 27 | ,testSomething : function(vals) { 28 | 29 | this.cache.tests_to_run.push('foobie'); 30 | 31 | hub.emit('action:pop', {}, {}, this.cache); 32 | 33 | // Give it half a second & then verify 34 | this.wait(function() { 35 | Y.Assert.areEqual(this.cache.tests_to_run.length, 0); 36 | }, 500); 37 | } 38 | ,testMore : function(vals) { 39 | 40 | this.cache.tests_to_run.push('foobie'); 41 | this.cache.tests_to_run.push('goobie'); 42 | 43 | hub.emit('action:pop', {}, {}, this.cache); 44 | 45 | // Give it half a second & then verify 46 | this.wait(function() { 47 | Y.Assert.areEqual(this.cache.tests_to_run.length, 1); 48 | Y.Assert.areEqual(this.cache.tests_to_run[9], 'goobie'); 49 | }, 500); 50 | } 51 | })); 52 | 53 | Y.Test.Runner.add(suite); 54 | Y.UnitTest.go(); 55 | }); 56 | 57 | -------------------------------------------------------------------------------- /backend/nodejute/test/jute/actions/prune/testPrune.js: -------------------------------------------------------------------------------- 1 | YUI({ 2 | logInclude: { TestRunner: true } 3 | }).use('jute', function(Y) { 4 | 5 | var suite = new Y.Test.Suite('prune'), 6 | util = require('util'), 7 | hub = require('./test/mock/hub').getNewHub(), 8 | common = require('./jute/actions/common').Create(hub), 9 | prune = require('./jute/actions/prune', true).Create(hub, common); // 'true' here means do code coverae on it! 10 | 11 | suite.add(new Y.Test.Case({ 12 | name: 'prune' 13 | ,setUp: function() { 14 | 15 | this.uuid = 'foo'; 16 | this.req = { session: { uuid: this.uuid } }; 17 | this.req.headers = { 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:5.0) Gecko/20100101 Firefox/5.0 FirePHP/0.5' }; 18 | this.req.connection = { remoteAddress: '1.2.3.4' }; 19 | 20 | hub.removeAllListeners('pruneDone'); 21 | hub.removeAllListeners(hub.LOG); 22 | 23 | } 24 | ,testHookedUp : function(vals) { 25 | Y.Assert.areEqual(hub.listeners('action:prune').length, 1); 26 | } 27 | ,testStatus : function(vals) { 28 | var res = Y.Mock(); 29 | hub.emit('action:prune', 'status'); 30 | 31 | // Give it half a second & then verify 32 | this.wait(function() { 33 | Y.assert(true, 'We skipped all pruning for status call!'); 34 | }, 500); 35 | } 36 | ,testPruneBrowsers : function(vals) { 37 | var cache = { tests_to_run: [ { browser: 'aBrowser' } ], browsers: { aBrowser: { name: 'BLARG', heart_beat: 0 } } }, 38 | timedOut = false; 39 | 40 | hub.once(hub.LOG, function(sev, message) { 41 | if (sev == hub.ERROR && message.match(/We lost browser BLARG/)) { 42 | timedOut = true; 43 | } 44 | }); 45 | 46 | hub.emit('action:prune', 'zob', this.req, cache); 47 | 48 | // Give it half a second & then verify 49 | this.wait(function() { 50 | Y.Assert.isTrue(timedOut, 'We did not lose browser!'); 51 | Y.Assert.isUndefined(cache.browsers.aBrowser, 'Browser should be out of cache!!'); 52 | Y.Assert.areEqual(cache.tests_to_run.length, 0, 'Test not deleted for gone browser!'); 53 | }, 500); 54 | } 55 | ,testPruneTests : function(vals) { 56 | var now = new Date().getTime(), 57 | cache = { tests_to_run: [ { url: '/blah/mah/goo/foo', running: 1, browser: 'aBrowser' } ], browsers: { aBrowser: { name: 'BLARG', heart_beat: now }, foo: {} } }, 58 | killTest = false, redirected = false, dumped = false; 59 | 60 | hub.on(hub.LOG, function(sev, mesg) { 61 | if (sev == hub.ERROR && mesg.match(/running for too long/)) { 62 | killTest = true; 63 | } 64 | if (sev == hub.INFO && mesg.match(/Dumping/)) { 65 | dumped = true; 66 | } 67 | }); 68 | 69 | hub.once('pruneDone', function(redirect) { 70 | redirected = true; 71 | }); 72 | 73 | hub.emit('action:prune', 'zob', this.req, cache); 74 | 75 | this.wait(function() { 76 | Y.Assert.isTrue(killTest, 'We did not kill the test!'); 77 | Y.Assert.isTrue(redirected, 'We did not redirect the browser!'); 78 | Y.Assert.isTrue(dumped, 'Did not dump error XML'); 79 | Y.Assert.areEqual(cache.tests_to_run.length, 0, 'Test not deleted for gone browser!'); 80 | Y.Assert.isNumber(cache.browsers[this.uuid].get_test, 'Did not set get_test for cached browser'); 81 | Y.Assert.isNumber(cache.browsers[this.uuid].heart_beat, 'Did not set heart_beat for cached browser'); 82 | }, 1000); 83 | 84 | } 85 | ,testPruneTestsNotRunning : function(vals) { 86 | var now = new Date().getTime(), 87 | cache = { tests_to_run: [ { } ], browsers: { foo: { get_test: 1 } } }, 88 | tooLong = false, redirected = false; 89 | 90 | hub.on(hub.LOG, function(sev, mesg) { 91 | if (sev == hub.ERROR && mesg.match(/too long since/)) { 92 | tooLong = true; 93 | } 94 | }); 95 | 96 | hub.once('pruneDone', function(redirect) { 97 | redirected = redirect; 98 | }); 99 | 100 | hub.emit('action:prune', 'zob', this.req, cache); 101 | 102 | this.wait(function() { 103 | Y.assert(tooLong, 'We did not kill the test!'); 104 | Y.assert(redirected, 'We did not redirect the browser!'); 105 | }, 1000); 106 | 107 | } 108 | ,testPruneTestsNotRunningGetTest : function(vals) { 109 | var now = new Date().getTime(), 110 | cache = { tests_to_run: [ { } ], browsers: { foo: { get_test: 1 } } }, 111 | redirected = false; 112 | 113 | hub.once('pruneDone', function(redirect) { 114 | redirected = redirect; 115 | }); 116 | 117 | hub.emit('action:prune', 'get_test', this.req, cache); 118 | 119 | this.wait(function() { 120 | Y.assert(!redirected, 'We did redirect the browser!'); 121 | }, 1000); 122 | 123 | } 124 | 125 | })); 126 | 127 | Y.Test.Runner.add(suite); 128 | Y.UnitTest.go(); 129 | }); 130 | 131 | -------------------------------------------------------------------------------- /backend/nodejute/test/jute/configure/testConfigure.js: -------------------------------------------------------------------------------- 1 | /* 2 | var YUI = require('yui3').YUI, 3 | jute = require('./jv8').inject(YUI); 4 | */ 5 | 6 | YUI({ 7 | logInclude: { TestRunner: true } 8 | }).use('jute', function(Y) { 9 | 10 | var suite = new Y.Test.Suite('configure'), 11 | hub = require('./test/mock/hub').getNewHub(), 12 | util = require('util'), 13 | fs = require('fs'), 14 | path = require('path'), 15 | configure = require('./jute/configure', true).Create(hub); // 'true' here means do code coverae on it! 16 | 17 | suite.add(new Y.Test.Case({ 18 | name: 'configure basics', 19 | setUp: function() { 20 | this.expectedKeys = { 21 | port: '8080', 22 | docRoot: '/tmp', 23 | testDir: 'test', 24 | outputDir: 'output', 25 | java: '/usr/bin/java', 26 | logFile: '/tmp/FF.log', 27 | logFormat: '', 28 | testRegex: '*.htm*' 29 | }; 30 | 31 | this.badVal = '/KRAZY'; 32 | 33 | hub.removeAllListeners('configureDone'); 34 | hub.removeAllListeners('configureError'); 35 | } 36 | ,testVals : function(vals) { 37 | var clone = JSON.parse(JSON.stringify(this.expectedKeys)); 38 | for (var k in vals) { 39 | clone[k] = vals[k]; 40 | } 41 | 42 | for (var k in this.expectedKeys) { 43 | if (!clone[k]) { 44 | clone[k] = this.expectedKeys[k]; 45 | } 46 | } 47 | 48 | for (var k in clone) { 49 | process.env['npm_package_config_' + k] = clone[k]; 50 | } 51 | 52 | hub.emit('configure'); 53 | } 54 | ,testIsListening : function () { 55 | // Should be 1 listener for the 'configure' event 56 | Y.Assert.areEqual(hub.listeners('configure').length, 1); 57 | } 58 | ,testDocRootFail : function () { 59 | var test = this; 60 | 61 | hub.on('configureDone', function() { 62 | Y.Assert.fail("docRoot supposed to be invalid!!"); 63 | }); 64 | 65 | hub.on('configureError', function(obj) { 66 | Y.Assert.areEqual(obj.name, 'docRoot', "docRoot value should be wrong"); 67 | Y.Assert.areEqual(obj.value, test.badVal, "docRoot value should be " + test.badVal); 68 | }); 69 | 70 | this.testVals({ docRoot: this.badVal }); 71 | } 72 | ,testOutputDirFail : function () { 73 | var test = this; 74 | 75 | hub.on('configureDone', function() { 76 | Y.Assert.fail("outputDir supposed to be invalid!!"); 77 | }); 78 | 79 | hub.on('configureError', function(obj) { 80 | Y.Assert.areEqual('outputDir', obj.name, "testDir value should be wrong"); 81 | Y.Assert.areEqual(path.join('/tmp', test.badVal), obj.value, "testDir value should be " + test.badVal); 82 | }); 83 | 84 | this.testVals( { docRoot: '/tmp', outputDir: this.badVal } ); 85 | } 86 | ,testTestDirFail : function () { 87 | var test = this; 88 | 89 | hub.on('configureDone', function(obj) { 90 | Y.Assert.areEqual(path.join('/tmp', test.badVal), obj.testDir, "testDir value should be " + test.badVal); 91 | fs.rmdirSync('/tmp/output'); 92 | }); 93 | 94 | hub.on('configureError', function(obj) { 95 | console.log(obj); 96 | Y.Assert.areEqual('testDir', obj.name, "testDir value should be wrong"); 97 | Y.Assert.areEqual(path.join('/tmp', test.badVal), obj.value, "testDir value should be " + test.badVal); 98 | fs.rmdirSync('/tmp/output'); 99 | }); 100 | 101 | try { 102 | fs.mkdirSync('/tmp/output', 0777); 103 | } catch(e) {} 104 | 105 | this.testVals( { docRoot: '/tmp', outputDir: 'output', testDir: this.badVal } ); 106 | } 107 | ,testJavaFail : function () { 108 | 109 | hub.on('configureDone', function(obj) { 110 | Y.Assert.fail("java supposed to be invalid!!"); 111 | }); 112 | 113 | hub.on('configureError', function(obj) { 114 | Y.Assert.areEqual('java', obj.name, "java value should be wrong"); 115 | Y.Assert.areEqual('/zany', obj.value, "java value should be /zany"); 116 | }); 117 | 118 | this.testVals( { java: '/zany' } ); 119 | } 120 | ,testUIDFail : function () { 121 | 122 | hub.on('configureDone', function(obj) { 123 | console.log(util.inspect(obj)); 124 | Y.Assert.fail("uid supposed to be invalid!!"); 125 | fs.rmdirSync('/tmp/output'); 126 | }); 127 | 128 | hub.on('configureError', function(obj) { 129 | Y.Assert.areEqual('uid/gid', obj.name, "uid/gid value should be wrong"); 130 | Y.Assert.areEqual('zany', obj.value[1], "java value should be zany"); 131 | fs.rmdirSync('/tmp/output'); 132 | process.env.npm_package_config_uid = ''; 133 | }); 134 | 135 | try { 136 | fs.mkdirSync('/tmp/output', 0777); 137 | } catch(e) {} 138 | 139 | this.testVals( { uid: 'zany' } ); 140 | } 141 | ,testGIDFail : function () { 142 | 143 | hub.on('configureDone', function(obj) { 144 | Y.Assert.fail("gid supposed to be invalid!!"); 145 | fs.rmdirSync('/tmp/output'); 146 | }); 147 | 148 | hub.on('configureError', function(obj) { 149 | Y.Assert.areEqual('uid/gid', obj.name, "uid/gid value should be wrong"); 150 | Y.Assert.areEqual('zany', obj.value[0], "java value should be zany"); 151 | fs.rmdirSync('/tmp/output'); 152 | process.env.npm_package_config_gid = ''; 153 | }); 154 | 155 | 156 | try { 157 | fs.mkdirSync('/tmp/output', 0777); 158 | } catch(e) {} 159 | 160 | this.testVals( { gid: 'zany' } ); 161 | } 162 | ,testHubConfig : function () { 163 | var test = this; 164 | 165 | hub.on('configureDone', function(obj) { 166 | for (var k in obj) { 167 | if (!test.expectedKeys[k]) continue; 168 | if (k == 'uid' || k == 'gid') continue; 169 | if (k == 'outputDir' || k == 'testDir') { 170 | test.expectedKeys[k] = path.join(test.expectedKeys.docRoot, test.expectedKeys[k]); 171 | } 172 | Y.Assert.areEqual(test.expectedKeys[k], obj[k], test.expectedKeys[k] + ' should be ' + obj[k]); 173 | } 174 | fs.rmdirSync('/tmp/output'); 175 | }); 176 | 177 | hub.on('configureError', function(obj) { 178 | Y.Assert.fail("config should have worked!!"); 179 | fs.rmdirSync('/tmp/output'); 180 | }); 181 | 182 | try { 183 | fs.mkdirSync('/tmp/output', 0777); 184 | } catch(e) {} 185 | 186 | this.testVals( { } ); 187 | } 188 | ,testJavaEnv : function () { 189 | 190 | hub.on('configureDone', function(obj) { 191 | Y.Assert.fail("java supposed to be invalid!!"); 192 | }); 193 | 194 | hub.on('configureError', function(obj) { 195 | Y.Assert.areEqual('java', obj.name, "uid/gid value should be wrong"); 196 | Y.Assert.areEqual('/not/here/bin/java', obj.value, "java value should be zany"); 197 | }); 198 | 199 | process.env.JAVA_HOME = '/not/here'; 200 | this.testVals( { } ); 201 | process.env.JAVA_HOME = ''; 202 | } 203 | })); 204 | 205 | Y.Test.Runner.add(suite); 206 | Y.UnitTest.go(); 207 | }); 208 | 209 | -------------------------------------------------------------------------------- /backend/nodejute/test/jute_backend/testBackend.js: -------------------------------------------------------------------------------- 1 | var getConfig = require('./getConfig'), 2 | hub = require('./test/mock/hub'); 3 | 4 | YUI({ 5 | logInclude: { TestRunner: true }, 6 | }).use('jute', function(Y) { 7 | 8 | var suite = new Y.Test.Suite('backend'); 9 | suite.add(new Y.Test.Case({ 10 | name:'simple test', 11 | setUp: function() { 12 | this.hub = hub.getNewHub(); 13 | }, 14 | testIsObject : function () { 15 | console.log('EROIJRWIJOEIJOEFWIJEFWIJOEFWIJOEFWIJ'); 16 | getConfig.Create(this.hub); 17 | 18 | Y.log(gc); 19 | Y.Assert.isObject(gc); 20 | }, 21 | testMessage : function () { 22 | Y.log('testIsObject'); 23 | // Y.Assert.areEqual(this.tb.message, "I am a toolbar!"); 24 | } 25 | 26 | })); 27 | 28 | Y.Test.Runner.add(suite); 29 | Y.UnitTest.go(); 30 | }); 31 | -------------------------------------------------------------------------------- /backend/nodejute/test/mock/hub.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | 3 | var events = require("events"), 4 | util = require("util"), 5 | eventHubF = function() { events.EventEmitter.call(this); this.LOG = 'log'; this.ERROR = 'error'; this.INFO = 'info'; this.DEBUG = 'debug'; }, 6 | hub; 7 | 8 | util.inherits(eventHubF, events.EventEmitter); 9 | hub = new eventHubF(); 10 | 11 | return { 12 | getNewHub: function() { hub.config = {}; return hub; } 13 | }; 14 | })(); 15 | -------------------------------------------------------------------------------- /backend/nodejute/test/mock/req.js: -------------------------------------------------------------------------------- 1 | YUI({ 2 | }).use('test', function(Y) { 3 | var mockXhr = Y.Mock(); 4 | }); 5 | -------------------------------------------------------------------------------- /backend/nodejute/testV8Script.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var YUI = require("yui3").YUI 4 | ,fs = require('fs') 5 | ,vm = require('vm') 6 | ,path = require('path') 7 | ,fname = process.argv[2] 8 | ,base = path.dirname(fname) 9 | ; 10 | 11 | module.paths.push(__dirname); 12 | module.paths.push(path.join(__dirname, base)); 13 | console.log(module); 14 | console.log(require); 15 | 16 | YUI().add('jute', function(Y) { 17 | Y.namespace('UnitTest').go = function() { Y.Test.Runner.run(); }; 18 | Y.Test.Runner.subscribe(Y.Test.Runner.COMPLETE_EVENT, 19 | function(data) { console.log('Tests Done!'); } 20 | ); 21 | 22 | }, '1.0', { requires: [ 'test' ] }); 23 | 24 | YUI().use('jute', function() { 25 | // start barebones 26 | var sandbox = { 27 | YUI: YUI 28 | ,require: require 29 | }, 30 | data = fs.readFileSync(fname, 'utf8'); 31 | 32 | try { 33 | vm.runInNewContext(data, sandbox, fname); 34 | } catch(e) { 35 | console.error(e.message); 36 | console.error(e.stack); 37 | } 38 | }); 39 | -------------------------------------------------------------------------------- /js/jute.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011, Yahoo! Inc. All rights reserved. 3 | * Code licensed under the BSD License: 4 | * http://developer.yahoo.com/yui/license.html 5 | * version: 1.0 6 | * 7 | * JUTE javascript/browser/client/v8 portion 8 | */ 9 | if (window.__done) { 10 | // V8!! 11 | Y.namespace('UnitTest').go = function() { Y.Test.Runner.run(); }; 12 | Y.Test.Runner.subscribe(Y.Test.Runner.COMPLETE_EVENT, 13 | function(data) { 14 | var cover_out = Y.Test.Runner.getCoverage(Y.Coverage.Format.JSON), 15 | report_data = Y.Test.Format.JUnitXML(data.results); 16 | 17 | window.__done(data, report_data, typeof(_yuitest_coverage) == 'object' ? _yuitest_coverage : null, cover_out); 18 | } 19 | ); 20 | } else { 21 | // BROWSER!! 22 | var UT = Y.namespace('UnitTest'), button, load_button, kick_button, clear_button, clear_results; 23 | 24 | UT.heartBeat = function() { 25 | var cfg = { 26 | method: 'GET', 27 | on: { 28 | success: function (transactionid, response) { 29 | var obj, current_status, current_results, status_output, result_output, i, 30 | test, color, errorCount = 0; 31 | try { 32 | obj = Y.JSON.parse(response.responseText); 33 | if (typeof(obj.redirect_run_tests) === 'string') { 34 | // kick the run_tests iframe 35 | Y.one('#run_tests').setAttribute('src', obj.redirect_run_tests); 36 | } else { 37 | current_status = obj.current_status; 38 | current_results = obj.current_results; 39 | 40 | // Make test/browser output pretty 41 | status_output = '

Status

  • Browsers
    • '; 42 | Y.each(current_status.browsers, 43 | function(value, key, object) { 44 | status_output += '
    • ' + value.name + '
    • '; 45 | status_output += '
      • heart beat ' + value.heart_beat + '
      • '; 46 | status_output += '
      • get test ' + value.get_test + '
      '; 47 | } 48 | ); 49 | status_output += '
    '; 50 | 51 | status_output += '
  • Tests
    • '; 52 | for (i = 0; i < current_status.tests_to_run.length; i = i + 1) { 53 | test = current_status.tests_to_run[i]; 54 | color = ''; 55 | if (test.running > 0) { 56 | color = 'style="background-color:yellow"'; 57 | } 58 | status_output += '
    • Test
      • '; 59 | status_output += '
      • Test: ' + test.url + '
      • '; 60 | status_output += '
      • Browser: ' + test.browser + '
      • '; 61 | status_output += '
      • Running: ' + test.running + '
      '; 62 | } 63 | status_output += '
    '; 64 | status_output += '
'; 65 | Y.one('#list').setContent(status_output); 66 | 67 | // Make result output pretty 68 | result_output = '

Results

    '; 69 | if (current_results) { 70 | if (current_results['lcov-report']) { 71 | result_output += '
  • Total Coverage
  • '; 72 | } 73 | Y.each(current_results, 74 | function(value, key, object) { 75 | var file, color, found = 0, show; 76 | 77 | if (key === 'lcov.info' || key === 'lcov-report') { 78 | return; 79 | } 80 | 81 | result_output += '
  • ' + key + '
    • '; 82 | if (value.test_results) { 83 | for (i = 0; i < value.test_results.length; i = i + 1) { 84 | file = value.test_results[i].name; 85 | color = value.test_results[i].failed ? 'red' : 'green'; 86 | show = file; 87 | show = show.replace(/-test.xml$/, ''); 88 | result_output += '
    • ' + show + '
    • '; 89 | if (value.test_results[i].failed && !found) { 90 | found = 1; 91 | errorCount +=1; 92 | } 93 | } 94 | } 95 | if (value.coverage == 1) { 96 | result_output += '
    • Coverage Report
    • '; 97 | } 98 | result_output += '
    '; 99 | } 100 | ); 101 | } 102 | result_output += '
'; 103 | 104 | Y.one('#results').setContent(result_output); 105 | UT.current_status = Y.JSON.stringify(current_status); 106 | Y.one('#count').setContent('

' + errorCount + ' test failure(s) found.

'); 107 | } 108 | } catch (e) { 109 | console.log(e); 110 | Y.one('#list').setContent('
' + response.responseText + '
'); 111 | } 112 | }, 113 | end: function(transactionid) { 114 | Y.later(5000, {}, UT.heartBeat); 115 | } 116 | } 117 | }; 118 | 119 | Y.io('/jute/_heart_beat', cfg); 120 | 121 | }; 122 | 123 | UT.content_set = false; 124 | UT.waitLoop = function() { 125 | var cfg = { 126 | method: 'GET', 127 | data: "d=" + new Date().getTime(), 128 | on: { 129 | success: function (transactionid, response) { 130 | var data, qs, connect, i, content_node, content, test; 131 | try { 132 | data = Y.JSON.parse(response.responseText); 133 | if (data.testLocation) { 134 | // get the current URL 135 | qs = window.location.search.substring(1); 136 | //get the parameters & mash them with testLocation 137 | connect = '?'; 138 | if (data.testLocation.match(/\?/)) { 139 | connect = '&'; 140 | } 141 | if (qs) { 142 | data.testLocation = data.testLocation + connect + qs; 143 | } 144 | window.location.href = data.testLocation; 145 | } else if (!UT.content_set && data.availableTests) { 146 | content_node = Y.one('#content'); 147 | content = ''; 148 | content += '

Available Tests

'; 149 | content += ''; 150 | for (i = 0; i < data.availableTests.length; i = i + 1) { 151 | test = data.availableTests[i]; 152 | content += ''; 153 | content += ''; 154 | content += ''; 155 | content += ''; 156 | content += ''; 157 | } 158 | content += '
Test File (located at /jutebase/test/) Run Without Coverage Run With Coverage
' + test.test_url.replace(/\/jutebase\/test/gi,'') + ' RunRun
'; 159 | content += ''; 160 | content_node.setContent(content); 161 | Y.one('#run_all_no_cov').on('click', function() { 162 | Y.all('.no_cov_cbox').set('checked', Y.one('#run_all_no_cov').get('checked')); 163 | }); 164 | Y.one('#run_all_cov').on('click', function() { 165 | Y.all('.cov_cbox').set('checked', Y.one('#run_all_cov').get('checked')); 166 | }); 167 | UT.content_set = true; 168 | } 169 | } 170 | catch(e) { 171 | // Nothing to run prolly or server went away 172 | console.log(e); 173 | } 174 | }, 175 | end: function(transactionid) { 176 | Y.later(5000, {}, UT.waitLoop); 177 | }, 178 | failure: function() { 179 | // this of course will never happen... 180 | } 181 | } 182 | }; 183 | 184 | Y.io('/jute/_get_test', cfg); 185 | }; 186 | 187 | button = Y.one('#get_coverage_button'); 188 | if (button) { 189 | button.on('click', function() { 190 | // open iframe in same domain to grab coverage output 191 | console.log("current status: " + UT.current_status); 192 | var current_status = Y.JSON.parse(UT.current_status), i, obj; 193 | for (i = 0; i < current_status.tests_to_run.length; i = i + 1) { 194 | obj = current_status.tests_to_run[i]; 195 | if (navigator.userAgent === obj.browser && obj.running > 0) { 196 | Y.one('#grabber').setAttribute('src', obj.remote_grabber); 197 | Y.later(5000, {}, function() { Y.one('#run_tests').setAttribute('src', '/jute_docs/run_tests.html'); }); 198 | } 199 | } 200 | }); 201 | } 202 | 203 | load_button = Y.one('#load_button'); 204 | if (load_button) { 205 | load_button.on('click', function() { 206 | var load_me = Y.one('#load_file').get('value'); 207 | Y.io('/jute/_run_test', 208 | { 209 | method: 'POST', 210 | data: 'test=' + escape(load_me) 211 | } 212 | ); 213 | }); 214 | } 215 | 216 | kick_button = Y.one('#kick_frame'); 217 | if (kick_button) { 218 | kick_button.on('click', function() { 219 | Y.one('#run_tests').setAttribute('src', '/jute_docs/run_tests.html'); 220 | }); 221 | } 222 | 223 | clear_button = Y.one('#clear_tests'); 224 | if (clear_button) { 225 | clear_button.on('click', function() { 226 | Y.io('/jute/_clear_tests', 227 | { 228 | method: 'GET', 229 | on: { 230 | success: function() { 231 | Y.one('#run_tests').setAttribute('src', '/jute_docs/run_tests.html'); 232 | }, 233 | failure: function() { 234 | Y.one('#run_tests').setAttribute('src', '/jute_docs/run_tests.html'); 235 | } 236 | } 237 | } 238 | ); 239 | }); 240 | } 241 | 242 | clear_results = Y.one('#clear_results'); 243 | if (clear_results) { 244 | clear_results.on('click', function() { 245 | Y.io('/jute/_clear_results'); 246 | }); 247 | } 248 | 249 | Y.Test.Runner.subscribe(Y.Test.Runner.COMPLETE_EVENT, 250 | function(data) { 251 | var cover_out = Y.Test.Runner.getCoverage(Y.Coverage.Format.JSON), 252 | report_data = Y.Test.Format.JUnitXML(data.results); 253 | Y.io('/jute/_test_report', 254 | { 255 | method: 'POST', 256 | data: 'results=' + escape(report_data) + '&name=' + escape(data.results.name) + "&coverage=" + escape(cover_out), 257 | on: { 258 | success: function(tid, args) { 259 | if (!window.location.toString().match("_one_shot")) { 260 | window.location.href = '/jute_docs/run_tests.html'; 261 | } 262 | }, 263 | failure: function(tid, args) { 264 | if (!window.location.toString().match("_one_shot")) { 265 | window.location.href = '/jute_docs/run_tests.html'; 266 | } 267 | } 268 | 269 | } 270 | } 271 | ); 272 | } 273 | ); 274 | 275 | // some boilerplate stuff 276 | UT.go = function() { 277 | //initialize the console 278 | var yconsole = new Y.Console({ 279 | newestOnTop: false 280 | }); 281 | yconsole.render('#log'); 282 | 283 | //run the tests 284 | try { 285 | Y.Test.Runner.run(); 286 | } 287 | catch (e) { 288 | // skip this test - pop & move on 289 | Y.io('/jute/_pop', { on: { end: function() { window.location.href = '/jute_docs/run_tests.html'; } } }); 290 | } 291 | }; 292 | } 293 | --------------------------------------------------------------------------------